Preskoči na vsebino
Integracije za razvijalce

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?

Iz Puzzel
V Puzzel

Webhook 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.

Nastavitev
  1. 1 Odpri dejavnost v urejevalniku in pojdi v meni Razvijalec.
  2. 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. 3 Dejavnost enkrat odigraj sam. Prva zahteva POST prispe takoj, ko nekaj odgovoriš.
Sprejemnik, od začetka do konca
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);
});
Sproži se med igranjem, ne šele na koncu

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.

Kako ločiti zaključek od navadnega shranjevanja

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.

Pošlje ga igralčev brskalnik

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.

Vsebino obravnavaj kot trditev, ne kot dokaz

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.

index.html
<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>
Preveri, od kod prihaja sporočilo

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.

Enaka vsebina, enak čas pošiljanja

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.

index.html
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);
});
Kaj prispe
{
  "completed": true,
  "activityKey": "-Nq8sample_activity_key"
}
Rezultat je ob prihodu že shranjen

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.

Le znotraj iframe

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.

PoljeTipKaj počne
activityKey
vedno
string
stringDejavnost, ki ji rezultat pripada. Enak ključ, kot ga vidiš v URL-ju same dejavnosti, za ?p=.
playerUid
vedno
string
stringKdo 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
objectRegistracijska 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
numberKako daleč je prišel, kot odstotek. 100 pomeni končano.
timePassed
vedno
number
numberČas, preživet na dejavnosti, v milisekundah.
lastPlayedAt
vedno
number
numberKdaj je bil ta rezultat shranjen, kot časovni žig Unix v milisekundah.
createdAt
včasih
number
numberKdaj se je poskus začel, kot časovni žig Unix v milisekundah.
playerInput
včasih
object
objectKaj 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
objectKateri od teh elementov so pravilni, ključeno na enak način. Odsotno, dokler ni odgovorjeno še nič.
score
včasih
number
numberDosež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
numberLastna mera vrste za to, kako dobro je šlo, kjer jo vodi — na primer besede na minuto pri vaji tipkanja.
attempts
včasih
number
numberKateri 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
booleanPoskus se je končal na napačnem odgovoru in je zaključen, ne da bi bil dokončan.
hasAlternateCompletion
včasih
boolean
booleanDejavnost 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
arrayZnaki, ki jih je igralec večkrat zgrešil, najprej najpogosteje zgrešeni. Le pri vaji tipkanja.
contentVersion
včasih
number
numberKatera 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.
Končan kviz, kot prispe
{
  "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
}
Odsotno, ne prazno

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.

reset
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');
Ponastavitev je edini sprožilec

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.

Slišana je le stran, ki dejavnost vdeluje

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.

Pošlji nam igralca

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.

Pošlji nam žeton

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.

index.html
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();
});
Prosi nas, naj to vklopimo

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 identiteti

Ustvarjanje 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 API

Kadar 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.

Nič od tega?

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