Poveži Puzzel s svojo platformo
Puzzel lahko rezultat igralca preda naravnost drugemu sistemu — njegove točke, kako daleč je prišel, kaj je odgovoril — ne da bi bil ta sistem cel LMS. Ta stran zajema vse kanale, ki obstajajo: kaj gre ven, kdaj gre ven in najmanjšo stvar, ki jo moraš zgraditi, da to ujameš.
- Rezultati gredo ven prek
- Webhooka ali sporočila strani okoli dejavnosti
- Tvoja stran lahko
- Prijavi igralca in ponastavi dejavnost
- Vklopljeno
- Za posamezno dejavnost, v urejevalniku pod Razvijalec
- Vključeno v
- Plačljiv paket — na brezplačnem računu so te nastavitve izklopljene
Kateri kanal potrebuješ?
Iz dejavnosti lahko odidejo tri stvari, ena pa lahko pride noter. Kateri kanal ustreza, je odvisno od enega samega vprašanja: ali igralec sedi znotraj tvoje strani ali povsem drugje?
Vsak shranjeni rezultat je z metodo POST poslan na URL, ki ga imaš v lasti, kot JSON.
- Uporabi, kadar
- Igralec je lahko kjer koli — deljena povezava, koda QR, tuja spletna stran — ti pa želiš rezultat v svoji lastni bazi podatkov.
- Potrebuješ
- Končno točko HTTPS, ki sprejme zahtevo POST iz drugega izvora.
save_puzzle_results_via_webhookEnak JSON, poslan strani, ki vdela dejavnost, namesto strežniku.
- Uporabi, kadar
- Dejavnost vdelaš na svojo stran tečaja, sama stran pa lahko naredi kaj z rezultatom.
- Potrebuješ
- Iframe na svoji strani in poslušalca sporočil. Brez strežnika, brez CORS.
save_results_iframe_postmessageEno sporočilo, ko igralec konča, ki ne nosi ničesar drugega kot to dejstvo.
- Uporabi, kadar
- Zanima te le, ali je končal — da odkljukaš učno uro, odkleneš naslednjo ali prikažeš svoj zaslon.
- Potrebuješ
- Iframe na svoji strani in poslušalca sporočil.
send_completion_signal_when_embeddedWebhook za rezultate
V urejevalniku vklopi "Pošlji rezultate na webhook" in vnesi URL. Od takrat naprej brskalnik igralca ob vsakem shranjevanju napredka pošlje cel rezultat na ta URL z metodo POST, kot JSON.
- 1 Odpri dejavnost v urejevalniku in pojdi v meni Razvijalec.
- 2 Vklopi "Pošlji rezultate na webhook" in v polje pod njo prilepi svojo končno točko. Biti mora poln URL — sama domena je zavrnjena — in mora biti https, ker brskalnik blokira navaden klic http s strani, ki je postrežena prek https.
- 3 Dejavnost enkrat odigraj sam. Prva zahteva POST prispe takoj, ko nekaj odgovoriš.
import express from 'express';
const app = express();
// The POST is made by the player's browser, so the browser asks permission
// first. Answer the preflight and the real request can land.
app.use((req, res, next) => {
res.set('Access-Control-Allow-Origin', 'https://puzzel.org');
res.set('Access-Control-Allow-Headers', 'Content-Type');
if (req.method === 'OPTIONS') return res.sendStatus(204);
next();
});
app.post('/puzzel/results', express.json(), async (req, res) => {
const result = req.body;
// One run posts several times as it goes. Key on the pair, not on an
// insert: each call carries the whole state, so the newest simply wins.
await db.results.upsert(
{ playerUid: result.playerUid, activityKey: result.activityKey },
result
);
// Only act on the finish once.
if (result.progress >= 100 || result.hasAlternateCompletion) {
await markCourseStepComplete(result);
}
res.sendStatus(200);
});Rezultat gre ven ob vsakem shranjevanju vnosa: po premoru pri tipkanju, ko je kartica postavljena, ko se časovnik ustavi, in še enkrat, ko je dejavnost končana. Dolga križanka pomeni nekaj deset klicev, ne enega — zato svoj obdelovalnik napiši kot upsert po ključih playerUid in activityKey, ne kot vstavljanje. Vsak klic nosi celotno stanje, zato najnovejši vedno nadomesti prejšnjega, izgubljeni klic pa nadomesti naslednji.
progress je odstotek: 100 pomeni, da je dejavnost končana. Nekaj vrst se lahko konča, ne da bi dosegle to vrednost — do konca odgovorjen kviz, rešeno polje z rešitvijo — te namesto tega nosijo hasAlternateCompletion. Oboje obravnavaj kot končano.
Zahteva POST prihaja iz zavihka, v katerem se dejavnost igra, ne s strežnika Puzzel. Večina končnih točk, zgrajenih za sprejemanje webhookov, to že sprejme. Če tvoja nikoli ne vidi zahteve, je razlog ta: brskalnik najprej vpraša za dovoljenje, zato na predhodno zahtevo OPTIONS odgovori z glavo Access-Control-Allow-Origin, nato pa sledi prava zahteva POST.
Zahteva ni podpisana in prihaja iz brskalnika, ki ga ne nadzoruješ, zato lahko enako zahtevo pošlje tudi kdor koli, ki si ogleda stran. To je v redu za polnjenje vrstice napredka ali nadzorne plošče. Za vse, česar ne bi pustil, da si nastavi učenec sam — oceno, ki šteje, potrdilo, plačilo — to preveri z rezultati na svoji nadzorni plošči Puzzel ali pa naj točke namesto tega prenesejo povezovalniki ocen za LMS.
Rezultati na nadrejeno stran
Če dejavnost vdelaš, lahko isti JSON pošlješ svoji strani namesto strežniku. Vklopi "Pošlji rezultate na nadrejeno stran" in poslušaj sporočilo. Iz brskalnika ne gre nič, zato ni treba zgraditi končne točke ali razmišljati o CORS.
<iframe
src="https://puzzel.org/en/quiz/embed?p=-Nq8sample_activity_key"
width="100%"
height="700"
frameborder="0"
allowfullscreen
></iframe>
<script>
window.addEventListener('message', (event) => {
if (event.origin !== 'https://puzzel.org') return;
const result = event.data;
if (!result || !result.activityKey) return;
// Same shape the webhook posts, same advice: it arrives repeatedly.
saveProgress(result);
});
</script>Tvoj poslušalec zazna vsako sporočilo, poslano strani, tudi iz drugih okvirjev in razširitev brskalnika. Preden zaupaš vsebini, primerjaj event.origin z https://puzzel.org.
To je dvojček webhooka: enaka polja, poslana ob enakih trenutkih. Vse pod "Kaj vsebuje rezultat" velja tudi tukaj.
Signal o zaključku
Najmanjši kanal, za primere, ko te sam rezultat ne zanima: vklopi "Pošlji signal o zaključku" in tvoja stran ob trenutku, ko igralec konča, prejme eno sporočilo.
window.addEventListener('message', (event) => {
if (event.origin !== 'https://puzzel.org') return;
if (event.data?.completed !== true) return;
// { completed: true, activityKey: '-Nq8sample_activity_key' }
unlockNextLesson(event.data.activityKey);
});{
"completed": true,
"activityKey": "-Nq8sample_activity_key"
}Signal je namerno poslan šele po tem, ko je zaključno shranjevanje že opravljeno, zato ga stran, ki se odzove tako, da rezultat prebere nazaj, tam tudi najde.
Oba sporočilna kanala pošiljata strani, ki dejavnost okvirja. Če je dejavnost odprta v svojem zavihku, ni nikogar, ki bi ga obvestila, zato se ne pošlje nič.
Kaj vsebuje rezultat
Ena oblika, ne glede na to, kateri kanal jo prenaša. Igralčevi odgovori so ključeni po id-jih elementov same dejavnosti, zato se enaki ključi pojavijo tudi v correctUids.
| Polje | Tip | Kaj počne |
|---|---|---|
activityKey vedno string | string | Dejavnost, ki ji rezultat pripada. Enak ključ, kot ga vidiš v URL-ju same dejavnosti, za ?p=. |
playerUid vedno string | string | Kdo je igral, kot anonimen id. Za tega igralca na tej napravi je stalen, zato je to tisto, po čemer ključiš rezultate — ni e-poštni naslov in ni račun Puzzel. |
player včasih object | object | Registracijska polja, ki jih dejavnost zahteva, tako kot si jih nastavil: name, email, class, student_id in podobno. Odsotno, dokler se igralec ne registrira, in povsem odsotno pri dejavnosti, ki ne zahteva ničesar. |
progress vedno number | number | Kako daleč je prišel, kot odstotek. 100 pomeni končano. |
timePassed vedno number | number | Čas, preživet na dejavnosti, v milisekundah. |
lastPlayedAt vedno number | number | Kdaj je bil ta rezultat shranjen, kot časovni žig Unix v milisekundah. |
createdAt včasih number | number | Kdaj se je poskus začel, kot časovni žig Unix v milisekundah. |
playerInput včasih object | object | Kaj je igralec dejansko vnesel, ključeno po id-ju elementa, ki mu pripada. Oblika znotraj je odvisna od vrste dejavnosti — beseda, seznam postavljenih kartic, izbrana možnost. |
correctUids včasih object | object | Kateri od teh elementov so pravilni, ključeno na enak način. Odsotno, dokler ni odgovorjeno še nič. |
score včasih number | number | Dosežene točke, pri vrstah, ki merijo točke poskusa. Drugje odsotno — tudi pri poskusu, ki je res dosegel nič točk, zato pred branjem preveri, da ključ obstaja. |
performance včasih number | number | Lastna mera vrste za to, kako dobro je šlo, kjer jo vodi — na primer besede na minuto pri vaji tipkanja. |
attempts včasih number | number | Kateri poskus je to: 1 prvič, ob vsakem ponovnem začetku še eno več. Le pri vrstah, ki poskus lahko končajo predčasno in štejejo ponovitve. |
knockedOut včasih boolean | boolean | Poskus se je končal na napačnem odgovoru in je zaključen, ne da bi bil dokončan. |
hasAlternateCompletion včasih boolean | boolean | Dejavnost je bila končana na način, ki ne doseže 100 % — do konca odgovorjen kviz, rešeno polje z rešitvijo. Obravnavaj to kot zaključek. |
missedKeys včasih array | array | Znaki, ki jih je igralec večkrat zgrešil, najprej najpogosteje zgrešeni. Le pri vaji tipkanja. |
contentVersion včasih number | number | Katera različica vsebine dejavnosti je bila uporabljena za ta poskus. Spremeni se, ko lastnik uredi vprašanja, zato je star rezultat mogoče ločiti od trenutnega. |
{
"activityKey": "-Nq8sample_activity_key",
"playerUid": "kK3r9TzSampleAnonymousUid",
"player": {
"name": "Ava Ortega",
"email": "ava@school.example",
"class": "5B"
},
"progress": 100,
"timePassed": 243120,
"lastPlayedAt": 1758297843120,
"createdAt": 1758297600000,
"playerInput": {
"-Nq8item_one": "Lisbon",
"-Nq8item_two": "1969"
},
"correctUids": {
"-Nq8item_one": true,
"-Nq8item_two": false
},
"score": 800,
"contentVersion": 1757923200000
}Polje, ki ne velja, je izpuščeno iz JSON namesto poslano kot null ali nič. Tako se vrsta, ki ne meri točk, loči od poskusa, ki je dosegel nič točk — zato beri z privzeto vrednostjo in nikoli ne predpostavi, da je ključ prisoten.
Ponastavitev dejavnosti s tvoje strani
En ukaz potuje v drugo smer. Ko je vklopljeno "Sprejmi sprožilce z nadrejene strani", lahko stran, ki dejavnost vdeluje, počisti igralčeve odgovore in dejavnost vrne na začetek — za tvoj lasten gumb "poskusi znova" zunaj okvirja.
const frame = document.querySelector('iframe').contentWindow;
// Sent to the frame, from the page that embeds it — no other sender is
// accepted, and the activity has to have the trigger setting switched on.
frame.postMessage({ trigger: { type: 'reset' } }, 'https://puzzel.org');Ni sporočila za oddajo rezultata, odpiranje zaključnega sporočila ali skok na vprašanje. Sporočilo, ki zahteva karkoli drugega kot ponastavitev, je prezrto.
Sprožilec je sprejet le s strani, ki dejavnost okvirja, in od nikoder drugod — ne od sosednjega okvirja, ne od skripte na strani. Nastavitev je privzeto izklopljena, zato jo vklopi za dejavnosti, ki jih upravljaš.
Prinesi svojo identiteto igralca
Če tvoja platforma že ve, kdo igra, dejavnosti ni treba spet vprašati. Obstajata dve rokovanji, obe za dejavnost znotraj tvoje strani, in obe vklopimo mi, ne ti v urejevalniku — spremenita namreč, komu rezultat pripada, zato ju nastavimo skupaj s tabo, ne prek potrditvenega polja.
Dejavnost se javi s sporočilom 'app-loaded' in počaka. Tvoja stran nazaj pošlje igralčeve podatke, rezultat pa je shranjen pod njimi, ne da bi igralec kaj vtipkal ali videl zaslon za registracijo.
Enako rokovanje, le da tvoja stran namesto samih polj pošlje JWT, ki ga je izdal tvoj ponudnik identitete. Preden igralca spustimo noter, ga preverimo glede na izdajatelje, nastavljene za tvoj račun, zato je identiteta dokazana in ne le zatrjena — to je tisto, za kar prosiš, kadar mora biti rezultat zanesljiv.
window.addEventListener('message', (event) => {
if (event.origin !== 'https://puzzel.org') return;
// The activity says it is ready for a player.
if (event.data === 'app-loaded') {
frame.postMessage(
{
player: { name: 'Ava Ortega', email: 'ava@school.example', class: '5B' },
// true starts a fresh attempt instead of resuming this player's last one
should_reset: false
},
'https://puzzel.org'
);
}
// Signed in; the board is coming.
if (event.data?.success === true) hideYourOwnSpinner();
});Povej nam, katero od obeh želiš in kje bodo dejavnosti vdelane, mi pa bomo nastavili tvoj račun in te vodili skozi postopek.
Piši nam o identitetiUstvarjanje dejavnosti iz tvojega sistema
Vse zgoraj govori o tem, kako rezultat pride ven. Obratna smer — ustvarjanje samih dejavnosti iz vsebine, ki jo že imaš — je Puzzle API: en POST na vrsto dejavnosti, nazaj pa dobiš ključ in URL za vdelavo.
Preberi referenco APIKadar je pripravljen povezovalnik boljša izbira
Če je platforma na drugi strani pravi LMS, verjetno ne potrebuješ ničesar od tega. Ocene se lahko same vrnejo v njegovo redovalnico, ne da bi ti moral karkoli gostiti.
Zagnano znotraj LMS, točke pa se zapišejo nazaj v njegovo redovalnico.
Objavi dejavnost kot nalogo, ocene pa se vrnejo samodejno.
Platforme za tečaje, strani s članstvom, intraneti in vse, kar si zgradil sam, so ravno tisto, za kar so kanali na tej strani namenjeni. Vdelava skupaj s signalom o zaključku pokrije večino tega.
Česa ni
Da tega ne iščeš zaman:
- Ni končne točke za branje rezultatov nazaj. API ustvarja dejavnosti; rezultati odhajajo skozi kanale na tej strani ali skozi izvoze v tvoji nadzorni plošči.
- Webhook ni podpisan. Ni ničesar, s čimer bi zahtevo preveril, zato rezultat ne sme biti edina stvar, na kateri temelji nekaj pomembnega.
- Ni webhooka za cel račun. URL je nastavitev posamezne dejavnosti, zato ga kopirana dejavnost prevzame, nova pa se začne brez njega.
- Nič pri ekipni igri ali sobi v živo. Oba sporočilna kanala in webhook so namenjeni samostojnemu igranju, nastavitve pa se same izklopijo, ko je vklopljena ekipna igra.
- Ni čakalne vrste za dostavo. Nič se ne shrani in ponovno pošlje — naslednje shranjevanje je ponovni poskus, pomemben pa je zadnji klic poskusa.
Nekaj ne dela, kot bi moralo?
Pošlji zahtevo, ki si jo poskusil, in napako, ki si jo dobil nazaj, in dobil boš pravi odgovor osebe, ki je napisala končno točko.
Piši podpori po e-pošti