From da1e5434247ebee45c84e80effb8a6b078afdde3 Mon Sep 17 00:00:00 2001
From: electron-rare <108685187+electron-rare@users.noreply.github.com>
Date: Wed, 10 Jun 2026 21:05:09 +0200
Subject: [PATCH] feat: moodle ws provisioning + real fixture
---
fixtures/course-contents-3.json | 1 +
ops/README.md | 141 ++++++++++++++++++++++++++++++++
ops/provision-ws.php | 55 +++++++++++++
3 files changed, 197 insertions(+)
create mode 100644 fixtures/course-contents-3.json
create mode 100644 ops/README.md
create mode 100644 ops/provision-ws.php
diff --git a/fixtures/course-contents-3.json b/fixtures/course-contents-3.json
new file mode 100644
index 0000000..7816d83
--- /dev/null
+++ b/fixtures/course-contents-3.json
@@ -0,0 +1 @@
+[{"id":7,"name":"G\u00e9n\u00e9ralit\u00e9s","visible":1,"summary":"
KiCad pour Makers<\/h3>\n
Concevez un PCB complet de A \u00e0 Z avec KiCad 9. Du sch\u00e9ma \u00e0 la commande chez JLCPCB.<\/p>\n
Dur\u00e9e :<\/strong> ~2h | Niveau :<\/strong> D\u00e9butant \u2192 Interm\u00e9diaire<\/p>\nProjet fil rouge :<\/strong> Breakout ESP32, 50\u00d730mm, 2 couches<\/p>\n\ud83d\udd27 Fichiers KiCad sur GitHub<\/a><\/p>","summaryformat":1,"section":0,"hiddenbynumsections":0,"uservisible":true,"component":null,"itemid":null,"modules":[{"id":66,"url":"https:\/\/moodle.saillant.cc\/mod\/url\/view.php?id=66","name":"Ouvrir l'IDE de d\u00e9veloppement","instance":6,"contextid":58,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/url\/1775166325\/monologo?filtericon=1","modname":"url","purpose":"content","branded":false,"modplural":"URLs","indent":0,"onclick":"","afterlink":null,"customdata":"{\"display\":4,\"filtericon\":true}","noviewlink":false,"completion":0,"downloadcontent":1,"dates":[],"groupmode":0,"contents":[{"type":"url","filename":"Ouvrir lIDE de d\u00e9veloppement","filepath":null,"filesize":0,"fileurl":"https:\/\/coder.saillant.cc\/templates\/kicad-makers\/workspace","timecreated":null,"timemodified":1775136728,"sortorder":null,"userid":null,"author":null,"license":null}],"contentsinfo":{"filescount":1,"filessize":0,"lastmodified":1775136728,"mimetypes":[],"repositorytype":""}},{"id":2,"url":"https:\/\/moodle.saillant.cc\/mod\/forum\/view.php?id=2","name":"Annonces","instance":2,"contextid":68,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/forum\/1775166325\/monologo?filtericon=1","modname":"forum","purpose":"collaboration","branded":false,"modplural":"Forums","indent":0,"onclick":"","afterlink":null,"activitybadge":[],"customdata":"{\"trackingtype\":\"1\"}","noviewlink":false,"completion":0,"downloadcontent":1,"dates":[],"groupmode":0}]},{"id":8,"name":"Module 1 : D\u00e9couverte de KiCad","visible":1,"summary":"","summaryformat":1,"section":1,"hiddenbynumsections":0,"uservisible":true,"component":null,"itemid":null,"modules":[{"id":27,"url":"https:\/\/moodle.saillant.cc\/mod\/book\/view.php?id=27","name":"Module 1 : D\u00e9couverte de KiCad","instance":21,"contextid":69,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/book\/1775166325\/monologo?filtericon=1","modname":"book","purpose":"content","branded":false,"modplural":"Livres","indent":0,"onclick":"","afterlink":null,"customdata":"\"\"","noviewlink":false,"completion":1,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":false,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0,"contents":[{"type":"content","filename":"structure","filepath":"\/","filesize":0,"fileurl":null,"content":"[{\"title\":\"Introduction\",\"href\":\"242\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"1.1 Introduction et installation\",\"href\":\"243\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Pourquoi KiCad plut\\u00f4t qu'Eagle, EasyEDA ou Altium ?\",\"href\":\"244\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Installation par syst\\u00e8me d'exploitation\",\"href\":\"245\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"1.2 Le workflow KiCad : de l'id\\u00e9e au PCB physique\",\"href\":\"246\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Vocabulaire essentiel\",\"href\":\"247\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Le gestionnaire de projets (KiCad Project Manager)\",\"href\":\"248\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"L'\\u00e9diteur de sch\\u00e9ma (Eeschema)\",\"href\":\"249\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"L'\\u00e9diteur PCB (Pcbnew)\",\"href\":\"250\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"La visionneuse 3D\",\"href\":\"251\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"1.4 Exercice : explorer le projet d\\u00e9mo KiCad\",\"href\":\"252\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"1.5 D\\u00e9but du projet fil rouge : cr\\u00e9er \\\"ESP32-Breakout\\\"\",\"href\":\"253\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"R\\u00e9capitulatif\",\"href\":\"254\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]}]","timecreated":1775113012,"timemodified":1775113012,"sortorder":0,"userid":null,"author":null,"license":null},{"type":"file","filename":"index.html","filepath":"\/242\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/242\/index.html","content":"Introduction","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/243\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/243\/index.html","content":"1.1 Introduction et installation","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/244\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/244\/index.html","content":"Pourquoi KiCad plut\u00f4t qu'Eagle, EasyEDA ou Altium ?","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/245\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/245\/index.html","content":"Installation par syst\u00e8me d'exploitation","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/246\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/246\/index.html","content":"1.2 Le workflow KiCad : de l'id\u00e9e au PCB physique","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/247\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/247\/index.html","content":"Vocabulaire essentiel","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/248\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/248\/index.html","content":"Le gestionnaire de projets (KiCad Project Manager)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/249\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/249\/index.html","content":"L'\u00e9diteur de sch\u00e9ma (Eeschema)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/250\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/250\/index.html","content":"L'\u00e9diteur PCB (Pcbnew)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/251\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/251\/index.html","content":"La visionneuse 3D","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/252\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/252\/index.html","content":"1.4 Exercice : explorer le projet d\u00e9mo KiCad","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/253\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/253\/index.html","content":"1.5 D\u00e9but du projet fil rouge : cr\u00e9er \"ESP32-Breakout\"","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/254\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/69\/mod_book\/chapter\/254\/index.html","content":"R\u00e9capitulatif","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]}],"contentsinfo":{"filescount":14,"filessize":0,"lastmodified":1775151150,"mimetypes":[],"repositorytype":""}},{"id":51,"url":"https:\/\/moodle.saillant.cc\/mod\/quiz\/view.php?id=51","name":"Quiz 1 : D\u00e9couverte","instance":21,"contextid":36,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/quiz\/1775166325\/monologo?filtericon=1","modname":"quiz","purpose":"assessment","branded":false,"modplural":"Tests","indent":0,"onclick":"","afterlink":null,"customdata":"{\"customcompletionrules\":{\"completionpassorattemptsexhausted\":[],\"completionminattempts\":\"0\"}}","noviewlink":false,"completion":2,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":true,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0}]},{"id":9,"name":"Module 2 : Sch\u00e9matique","visible":1,"summary":"","summaryformat":1,"section":2,"hiddenbynumsections":0,"uservisible":true,"component":null,"itemid":null,"modules":[{"id":28,"url":"https:\/\/moodle.saillant.cc\/mod\/book\/view.php?id=28","name":"Module 2 : Sch\u00e9matique","instance":22,"contextid":70,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/book\/1775166325\/monologo?filtericon=1","modname":"book","purpose":"content","branded":false,"modplural":"Livres","indent":0,"onclick":"","afterlink":null,"customdata":"\"\"","noviewlink":false,"completion":1,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":false,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0,"contents":[{"type":"content","filename":"structure","filepath":"\/","filesize":0,"fileurl":null,"content":"[{\"title\":\"Introduction\",\"href\":\"255\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"2.1 Objectif du module\",\"href\":\"256\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Placer le connecteur USB-C\",\"href\":\"257\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"R\\u00e9sistances CC1\\\/CC2 (n\\u00e9gociation USB-C)\",\"href\":\"258\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Le r\\u00e9gulateur AMS1117-3.3 (SOT-223)\",\"href\":\"259\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Condensateurs de d\\u00e9couplage (r\\u00e8gle fondamentale)\",\"href\":\"260\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Power Flags (obligatoires pour passer l'ERC)\",\"href\":\"261\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Placer l'ESP32\",\"href\":\"262\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Alimentation de l'ESP32\",\"href\":\"263\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Boutons BOOT et RESET\",\"href\":\"264\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"LEDs de statut\",\"href\":\"265\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Headers GPIO (J2)\",\"href\":\"266\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"2.5 Power Flags et Net Labels \\u2014 pourquoi l'ERC en a besoin\",\"href\":\"267\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"2.6 Exercice guid\\u00e9 : connecteur DHT22\",\"href\":\"268\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"2.7 V\\u00e9rification : lancer l'ERC\",\"href\":\"269\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"R\\u00e9capitulatif\",\"href\":\"270\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]}]","timecreated":1775113012,"timemodified":1775113012,"sortorder":0,"userid":null,"author":null,"license":null},{"type":"file","filename":"index.html","filepath":"\/255\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/255\/index.html","content":"Introduction","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/256\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/256\/index.html","content":"2.1 Objectif du module","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/257\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/257\/index.html","content":"Placer le connecteur USB-C","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/258\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/258\/index.html","content":"R\u00e9sistances CC1\/CC2 (n\u00e9gociation USB-C)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/259\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/259\/index.html","content":"Le r\u00e9gulateur AMS1117-3.3 (SOT-223)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/260\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/260\/index.html","content":"Condensateurs de d\u00e9couplage (r\u00e8gle fondamentale)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/261\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/261\/index.html","content":"Power Flags (obligatoires pour passer l'ERC)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/262\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/262\/index.html","content":"Placer l'ESP32","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/263\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/263\/index.html","content":"Alimentation de l'ESP32","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/264\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/264\/index.html","content":"Boutons BOOT et RESET","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/265\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/265\/index.html","content":"LEDs de statut","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/266\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/266\/index.html","content":"Headers GPIO (J2)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/267\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/267\/index.html","content":"2.5 Power Flags et Net Labels \u2014 pourquoi l'ERC en a besoin","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/268\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/268\/index.html","content":"2.6 Exercice guid\u00e9 : connecteur DHT22","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/269\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/269\/index.html","content":"2.7 V\u00e9rification : lancer l'ERC","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/270\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/70\/mod_book\/chapter\/270\/index.html","content":"R\u00e9capitulatif","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]}],"contentsinfo":{"filescount":17,"filessize":0,"lastmodified":1775151150,"mimetypes":[],"repositorytype":""}},{"id":52,"url":"https:\/\/moodle.saillant.cc\/mod\/quiz\/view.php?id=52","name":"Quiz 2 : Sch\u00e9matique","instance":22,"contextid":37,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/quiz\/1775166325\/monologo?filtericon=1","modname":"quiz","purpose":"assessment","branded":false,"modplural":"Tests","indent":0,"onclick":"","afterlink":null,"customdata":"{\"customcompletionrules\":{\"completionpassorattemptsexhausted\":[],\"completionminattempts\":\"0\"}}","noviewlink":false,"completion":2,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":true,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0}]},{"id":10,"name":"Module 3 : Du sch\u00e9ma au PCB","visible":1,"summary":"","summaryformat":1,"section":3,"hiddenbynumsections":0,"uservisible":true,"component":null,"itemid":null,"modules":[{"id":29,"url":"https:\/\/moodle.saillant.cc\/mod\/book\/view.php?id=29","name":"Module 3 : Du sch\u00e9ma au PCB","instance":23,"contextid":71,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/book\/1775166325\/monologo?filtericon=1","modname":"book","purpose":"content","branded":false,"modplural":"Livres","indent":0,"onclick":"","afterlink":null,"customdata":"\"\"","noviewlink":false,"completion":1,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":false,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0,"contents":[{"type":"content","filename":"structure","filepath":"\/","filesize":0,"fileurl":null,"content":"[{\"title\":\"Introduction\",\"href\":\"271\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"3.1 Objectif du module\",\"href\":\"272\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"3.2 Attribution des empreintes (Footprint Assignment)\",\"href\":\"273\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"SMD vs Traversant \\u2014 quand choisir\",\"href\":\"274\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Les tailles de bo\\u00eetiers courants\",\"href\":\"275\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Attribution dans KiCad\",\"href\":\"276\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"3.3 Passer au PCB Editor\",\"href\":\"277\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"3.4 Contour de la carte (Board Outline)\",\"href\":\"278\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"3.5 Placement des composants\",\"href\":\"279\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Coordonn\\u00e9es de placement d\\u00e9taill\\u00e9es (en mm)\",\"href\":\"280\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"3.6 R\\u00e8gles de design\",\"href\":\"281\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"3.7 Routage des pistes\",\"href\":\"282\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Ordre de routage recommand\\u00e9\",\"href\":\"283\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Astuces de routage\",\"href\":\"284\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"3.8 Plan de masse (Ground Plane)\",\"href\":\"285\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Cr\\u00e9er le plan de masse\",\"href\":\"286\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"3.9 Exercice : S\\u00e9rigraphie\",\"href\":\"287\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"R\\u00e9capitulatif\",\"href\":\"288\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]}]","timecreated":1775113012,"timemodified":1775113012,"sortorder":0,"userid":null,"author":null,"license":null},{"type":"file","filename":"index.html","filepath":"\/271\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/271\/index.html","content":"Introduction","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/272\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/272\/index.html","content":"3.1 Objectif du module","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/273\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/273\/index.html","content":"3.2 Attribution des empreintes (Footprint Assignment)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/274\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/274\/index.html","content":"SMD vs Traversant \u2014 quand choisir","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/275\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/275\/index.html","content":"Les tailles de bo\u00eetiers courants","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/276\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/276\/index.html","content":"Attribution dans KiCad","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/277\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/277\/index.html","content":"3.3 Passer au PCB Editor","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/278\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/278\/index.html","content":"3.4 Contour de la carte (Board Outline)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/279\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/279\/index.html","content":"3.5 Placement des composants","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/280\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/280\/index.html","content":"Coordonn\u00e9es de placement d\u00e9taill\u00e9es (en mm)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/281\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/281\/index.html","content":"3.6 R\u00e8gles de design","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/282\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/282\/index.html","content":"3.7 Routage des pistes","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/283\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/283\/index.html","content":"Ordre de routage recommand\u00e9","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/284\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/284\/index.html","content":"Astuces de routage","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/285\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/285\/index.html","content":"3.8 Plan de masse (Ground Plane)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/286\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/286\/index.html","content":"Cr\u00e9er le plan de masse","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/287\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/287\/index.html","content":"3.9 Exercice : S\u00e9rigraphie","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/288\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/71\/mod_book\/chapter\/288\/index.html","content":"R\u00e9capitulatif","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]}],"contentsinfo":{"filescount":19,"filessize":0,"lastmodified":1775151150,"mimetypes":[],"repositorytype":""}},{"id":53,"url":"https:\/\/moodle.saillant.cc\/mod\/quiz\/view.php?id=53","name":"Quiz 3 : PCB","instance":23,"contextid":38,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/quiz\/1775166325\/monologo?filtericon=1","modname":"quiz","purpose":"assessment","branded":false,"modplural":"Tests","indent":0,"onclick":"","afterlink":null,"customdata":"{\"customcompletionrules\":{\"completionpassorattemptsexhausted\":[],\"completionminattempts\":\"0\"}}","noviewlink":false,"completion":2,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":true,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0}]},{"id":11,"name":"Module 4 : Fabrication","visible":1,"summary":"","summaryformat":1,"section":4,"hiddenbynumsections":0,"uservisible":true,"component":null,"itemid":null,"modules":[{"id":30,"url":"https:\/\/moodle.saillant.cc\/mod\/book\/view.php?id=30","name":"Module 4 : Fabrication","instance":24,"contextid":72,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/book\/1775166325\/monologo?filtericon=1","modname":"book","purpose":"content","branded":false,"modplural":"Livres","indent":0,"onclick":"","afterlink":null,"customdata":"\"\"","noviewlink":false,"completion":1,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":false,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0,"contents":[{"type":"content","filename":"structure","filepath":"\/","filesize":0,"fileurl":null,"content":"[{\"title\":\"Introduction\",\"href\":\"289\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"4.1 Objectif du module\",\"href\":\"290\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"4.2 V\\u00e9rification finale : DRC\",\"href\":\"291\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Erreurs courantes et corrections\",\"href\":\"292\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"4.3 Inspection 3D\",\"href\":\"293\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"4.4 G\\u00e9n\\u00e9rer les fichiers Gerber\",\"href\":\"294\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Proc\\u00e9dure de g\\u00e9n\\u00e9ration\",\"href\":\"295\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Fichier de per\\u00e7age (drill)\",\"href\":\"296\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"Cr\\u00e9er l'archive ZIP\",\"href\":\"297\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"V\\u00e9rifier dans le Gerber Viewer\",\"href\":\"298\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"4.5 G\\u00e9n\\u00e9rer la BOM (Bill of Materials)\",\"href\":\"299\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Estimation de co\\u00fbt\",\"href\":\"300\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"4.6 Commander chez JLCPCB\",\"href\":\"301\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[{\"title\":\"Options recommand\\u00e9es\",\"href\":\"302\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"D\\u00e9composition des co\\u00fbts\",\"href\":\"303\\\/index.html\",\"level\":1,\"hidden\":\"0\",\"subitems\":[]}]},{\"title\":\"4.7 Exercice : Fichier Pick-and-Place\",\"href\":\"304\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"4.8 Bonus : yiacad-kicad-plugin\",\"href\":\"305\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"R\\u00e9capitulatif\",\"href\":\"306\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]},{\"title\":\"F\\u00e9licitations !\",\"href\":\"307\\\/index.html\",\"level\":0,\"hidden\":\"0\",\"subitems\":[]}]","timecreated":1775113012,"timemodified":1775113012,"sortorder":0,"userid":null,"author":null,"license":null},{"type":"file","filename":"index.html","filepath":"\/289\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/289\/index.html","content":"Introduction","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/290\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/290\/index.html","content":"4.1 Objectif du module","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/291\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/291\/index.html","content":"4.2 V\u00e9rification finale : DRC","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/292\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/292\/index.html","content":"Erreurs courantes et corrections","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/293\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/293\/index.html","content":"4.3 Inspection 3D","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/294\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/294\/index.html","content":"4.4 G\u00e9n\u00e9rer les fichiers Gerber","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/295\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/295\/index.html","content":"Proc\u00e9dure de g\u00e9n\u00e9ration","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/296\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/296\/index.html","content":"Fichier de per\u00e7age (drill)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/297\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/297\/index.html","content":"Cr\u00e9er l'archive ZIP","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/298\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/298\/index.html","content":"V\u00e9rifier dans le Gerber Viewer","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/299\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/299\/index.html","content":"4.5 G\u00e9n\u00e9rer la BOM (Bill of Materials)","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/300\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/300\/index.html","content":"Estimation de co\u00fbt","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/301\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/301\/index.html","content":"4.6 Commander chez JLCPCB","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/302\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/302\/index.html","content":"Options recommand\u00e9es","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/303\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/303\/index.html","content":"D\u00e9composition des co\u00fbts","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/304\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/304\/index.html","content":"4.7 Exercice : Fichier Pick-and-Place","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/305\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/305\/index.html","content":"4.8 Bonus : yiacad-kicad-plugin","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/306\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/306\/index.html","content":"R\u00e9capitulatif","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]},{"type":"file","filename":"index.html","filepath":"\/307\/","filesize":0,"fileurl":"https:\/\/moodle.saillant.cc\/webservice\/pluginfile.php\/72\/mod_book\/chapter\/307\/index.html","content":"F\u00e9licitations !","timecreated":1775151150,"timemodified":1775151150,"sortorder":0,"userid":null,"author":null,"license":null,"tags":[]}],"contentsinfo":{"filescount":20,"filessize":0,"lastmodified":1775151150,"mimetypes":[],"repositorytype":""}},{"id":54,"url":"https:\/\/moodle.saillant.cc\/mod\/quiz\/view.php?id=54","name":"Quiz 4 : Fabrication","instance":24,"contextid":39,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/quiz\/1775166325\/monologo?filtericon=1","modname":"quiz","purpose":"assessment","branded":false,"modplural":"Tests","indent":0,"onclick":"","afterlink":null,"customdata":"{\"customcompletionrules\":{\"completionpassorattemptsexhausted\":[],\"completionminattempts\":\"0\"}}","noviewlink":false,"completion":2,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":true,"istrackeduser":true,"uservisible":true,"details":[],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0},{"id":56,"url":"https:\/\/moodle.saillant.cc\/mod\/customcert\/view.php?id=56","name":"Certificat de r\u00e9ussite","instance":2,"contextid":51,"visible":1,"uservisible":true,"visibleoncoursepage":1,"modicon":"https:\/\/moodle.saillant.cc\/theme\/image.php\/fer\/customcert\/1775166325\/monologo?filtericon=1","modname":"customcert","purpose":"other","branded":false,"modplural":"Certificats personnalis\u00e9s","indent":0,"onclick":"","afterlink":null,"customdata":"\"\"","noviewlink":false,"completion":2,"completiondata":{"state":0,"timecompleted":0,"overrideby":null,"valueused":false,"hascompletion":true,"isautomatic":true,"istrackeduser":true,"uservisible":true,"details":[{"rulename":"completionview","rulevalue":{"status":0,"description":"Consulter"}}],"isoverallcomplete":false},"downloadcontent":1,"dates":[],"groupmode":0}]}]
\ No newline at end of file
diff --git a/ops/README.md b/ops/README.md
new file mode 100644
index 0000000..18791d1
--- /dev/null
+++ b/ops/README.md
@@ -0,0 +1,141 @@
+# ops/README.md — Moodle web-service provisioning
+
+## provision-ws.php
+
+Idempotent script that bootstraps the read-only WS integration on the live Moodle.
+
+**What it does:**
+1. Enables Moodle web services + REST protocol + built-in mobile service
+ (`enablewebservices`, `enablemobilewebservice`, `webserviceprotocols`).
+2. Creates the service user `wsreader` (WS Reader, wsreader@saillant.cc)
+ if it does not already exist.
+3. Enrols `wsreader` as a student in every real course (id > 1) so that
+ `core_course_get_contents` returns content for each course.
+
+Script is safe to re-run: user creation is skipped if the username exists;
+enrolment is idempotent via the manual enrolment plugin.
+
+**How to run (from Tower, where Moodle containers live):**
+
+```bash
+WSPASS=$(grep WSREADER_PASSWORD ~/formations-app.env | cut -d= -f2-)
+docker cp ops/provision-ws.php moodle:/tmp/provision-ws.php
+docker exec -e WSREADER_PASSWORD="$WSPASS" moodle php /tmp/provision-ws.php
+```
+
+Expected output:
+```
+user created: (or "user exists: " on re-run)
+enrolled: freertos
+enrolled: kicad-makers
+enrolled: esp32-ia
+enrolled: docker-selfhosting
+enrolled: iot-az
+enrolled: llm-locaux
+OK
+```
+
+## Env file — Tower: `/home/clems/formations-app.env`
+
+```
+WSREADER_PASSWORD=
+MOODLE_WS_TOKEN=
+```
+
+**Never commit this file.** It lives on Tower only.
+
+## Token renewal
+
+The mobile-service token does not expire by default in Moodle 4.5.
+To revoke and renew:
+
+```bash
+# Revoke current token via Moodle admin UI:
+# Site admin → Server → Web services → Manage tokens → delete wsreader token
+# Then re-issue:
+WSPASS=$(grep WSREADER_PASSWORD ~/formations-app.env | cut -d= -f2-)
+TOKEN=$(curl -s "https://moodle.saillant.cc/login/token.php" \
+ --data-urlencode "username=wsreader" \
+ --data-urlencode "password=$WSPASS" \
+ --data-urlencode "service=moodle_mobile_app" \
+ | python3 -c 'import json,sys; print(json.load(sys.stdin)["token"])')
+sed -i "s/^MOODLE_WS_TOKEN=.*/MOODLE_WS_TOKEN=$TOKEN/" ~/formations-app.env
+echo "New token: $TOKEN"
+```
+
+## Fixture provenance — fixtures/course-contents-3.json
+
+Captured: 2026-06-10 from live Moodle (https://moodle.saillant.cc)
+Course: id=3 (`kicad-makers` — "KiCad pour Makers")
+Command:
+```bash
+TOKEN=$(grep MOODLE_WS_TOKEN ~/formations-app.env | cut -d= -f2-)
+curl -s "https://moodle.saillant.cc/webservice/rest/server.php" \
+ --data-urlencode "wstoken=$TOKEN" \
+ --data-urlencode "wsfunction=core_course_get_contents" \
+ --data-urlencode "moodlewsrestformat=json" \
+ --data-urlencode "courseid=3" > fixtures/course-contents-3.json
+```
+
+**No tokens are embedded in the fixture.** `pluginfile.php` URLs in the
+fixture do NOT include the token — authentication is added at fetch-time
+by appending `?token=` to the URL.
+
+## Where chapter titles live (CRITICAL for Task 3 parser)
+
+The fixture is a JSON array of **sections**. Each section has a `modules[]`
+array. Book modules have `modname == "book"`.
+
+Each book module's `contents[]` array contains two entry types:
+
+### 1. The `structure` entry (type=`"content"`, filename=`"structure"`, filepath=`"/"`)
+
+`contents[0]` is always a special entry with:
+- `type: "content"`
+- `filename: "structure"`
+- `filepath: "/"`
+- `content`: a **JSON string** encoding an array of chapter objects:
+ ```json
+ [
+ {
+ "title": "Introduction",
+ "href": "242/index.html",
+ "level": 0,
+ "hidden": "0",
+ "subitems": [...]
+ },
+ ...
+ ]
+ ```
+ Each object has `title` (human-readable chapter title), `href` (chapter
+ id from the path, e.g. `242`), `level` (0=top-level, 1=subchapter),
+ `hidden`, and `subitems` (nested subchapters with the same shape).
+
+ **This is the authoritative TOC with hierarchy** — use this to build
+ the navigation tree.
+
+### 2. Per-chapter `file` entries (type=`"file"`, filename=`"index.html"`)
+
+Each subsequent entry has:
+- `type: "file"`
+- `filename: "index.html"`
+- `filepath: "//"` — e.g. `"/242/"` (chapter id is the path segment)
+- `content`: the **plain-text chapter title** (e.g. `"Introduction"`)
+- `fileurl`: pluginfile URL to fetch the chapter HTML, e.g.:
+ `https://moodle.saillant.cc/webservice/pluginfile.php/69/mod_book/chapter/242/index.html`
+ Fetch with `?token=` appended.
+
+**Parser recipe for Task 3:**
+1. Filter `contents` where `type == "file"` and `filename == "index.html"`.
+2. Extract `chapterId` from `filepath` (strip slashes → integer).
+3. Use `content` as the chapter title.
+4. Use `fileurl + "?token=" + TOKEN` to fetch HTML.
+5. Optionally parse the `structure` entry for hierarchy (level/subitems).
+
+Example (from module "Module 1 : Découverte de KiCad"):
+| filepath | content (title) | fileurl (fetch with ?token=…) |
+|----------|------------------------------------------|--------------------------------------------------------------|
+| /242/ | Introduction | …/pluginfile.php/69/mod_book/chapter/242/index.html |
+| /243/ | 1.1 Introduction et installation | …/pluginfile.php/69/mod_book/chapter/243/index.html |
+| /244/ | Pourquoi KiCad plutôt qu'Eagle, … | …/pluginfile.php/69/mod_book/chapter/244/index.html |
+| /254/ | Récapitulatif | …/pluginfile.php/69/mod_book/chapter/254/index.html |
diff --git a/ops/provision-ws.php b/ops/provision-ws.php
new file mode 100644
index 0000000..077f25f
--- /dev/null
+++ b/ops/provision-ws.php
@@ -0,0 +1,55 @@
+dirroot.'/user/lib.php');
+require_once($CFG->dirroot.'/lib/enrollib.php');
+
+// 1) Enable web services + REST + built-in mobile service
+// (mobile service already includes core_course_get_contents & file serving)
+set_config('enablewebservices', 1);
+set_config('enablemobilewebservice', 1);
+$protocols = (string)get_config('core', 'webserviceprotocols');
+if (strpos($protocols, 'rest') === false) {
+ set_config('webserviceprotocols', trim($protocols === '' ? 'rest' : $protocols . ',rest', ','));
+}
+
+// 2) Service user
+$username = 'wsreader';
+$password = getenv('WSREADER_PASSWORD');
+if (!$password) { fwrite(STDERR, "WSREADER_PASSWORD required\n"); exit(1); }
+$user = core_user::get_user_by_username($username);
+if (!$user) {
+ $u = new stdClass();
+ $u->username = $username;
+ $u->password = $password;
+ $u->firstname = 'WS';
+ $u->lastname = 'Reader';
+ $u->email = 'wsreader@saillant.cc';
+ $u->confirmed = 1;
+ $u->mnethostid = $CFG->mnet_localhost_id;
+ $uid = user_create_user($u, true, false);
+ $user = core_user::get_user($uid);
+ echo "user created: {$user->id}\n";
+} else {
+ echo "user exists: {$user->id}\n";
+}
+
+// 3) Enrol as student in every real course (id > 1) so get_contents works
+$student = $DB->get_record('role', ['shortname' => 'student'], '*', MUST_EXIST);
+$manual = enrol_get_plugin('manual');
+foreach ($DB->get_records_select('course', 'id > 1') as $course) {
+ $minstance = null;
+ foreach (enrol_get_instances($course->id, false) as $i) {
+ if ($i->enrol === 'manual') { $minstance = $i; break; }
+ }
+ if (!$minstance) {
+ $id = $manual->add_instance($course);
+ $minstance = $DB->get_record('enrol', ['id' => $id], '*', MUST_EXIST);
+ }
+ $manual->enrol_user($minstance, $user->id, $student->id);
+ echo "enrolled: {$course->shortname}\n";
+}
+echo "OK\n";