From a00747fe47fce2cf779089ec30034cfec5ce2112 Mon Sep 17 00:00:00 2001 From: Maxime Augier Date: Wed, 22 May 2024 21:43:01 +0200 Subject: [PATCH] Proper package and demo --- demo.typ | 81 +++++++++++++++++++++++++++++---------------- exam.typ => lib.typ | 60 ++++++++++++++++++++++++++------- typst.toml | 7 ++++ 3 files changed, 108 insertions(+), 40 deletions(-) rename exam.typ => lib.typ (55%) create mode 100644 typst.toml diff --git a/demo.typ b/demo.typ index 437a6a3..dacaaf9 100644 --- a/demo.typ +++ b/demo.typ @@ -1,45 +1,70 @@ -#import "exam.typ" : exam, question +#import "@ysec/exam:0.1.0": exam, question, reponse #show: doc => exam( - title: "Test Intermédiaire 2", - subtitle: "HEIG-VD - Introduction à la Sécurité Informatique (ISI)", - course: "ISI", + title: "Examen en Typst", + subtitle: "Y-Security - Typst Essentials", + course: "TES", date: datetime(year: 2024, month: 5, day: 27), author: "Maxime Augier", - indications: [ - - Ce test doit être réalisé en maximum *45 minutes*. - - - Aucun document n'est admis, excepté une page A4 (recto-verso) de - résumé personnel - - - L'utilisation de tout type de matériel électronique est interdite. - - - Ne pas oublier d'inscrire votre nom et prénom à l'endroit prévu à cet effet. - - - Merci de respecter un silence absolu jusqu'à ce que le dernier étudiant - aie rendu sa copie -], + time: "45 minutes", + allowed: "une page A4 (recto-verso) de résumé personnel", doc ) -= Web += Questions et Réponses +La commande `#question` permet de définir une question avec un bloc de réponse. +Le contenu de la réponse sera uniquement affiché dans le corrigé. -Vous opérez l’infrastructure d'une association de hackers amateurs, et remarquez un comportement étrange sur votre serveur web. +#question[Quelle est la manière la plus simple de fournir une réponse ?][ + Avec un 2e argument à la fonction `question`. +] -Vous lisez les logs de votre serveur web, et observez plusieurs requêtes suspectes, par exemple l’URL suivante : +#question[Est-il possible d'avoir un bloc réponse séparé ?] -`https://hackerz-club.ch/forum/browse.php?category=XSS&sort=post_date+union+select+%2A+from+users` +#reponse[Bien sûr ! Avec la commande `réponse`. ] -#question[Quel est le *nom usuel* de la vulnérabilité exploitée -ci dessus ?][Injection SQL (dans une clause ORDER BY)] +#question[Comment fonctionne la numérotation des questions ?][ + Le compteur global `"question"` est utilisé +] -#question(points: 2)[Quel est l'effet de l'attaque ?][Le vol de la table `users` de la base de données] += Formats alternatifs -= Crypto +#question[Préféréz-vous les questions à choix multiples ?] -Bla += Comptage des points -= Sécu +#question(points: 5)[Comment changer la valeur en points de la question ?] -Bli +#reponse[Avec l'argument optionnel `points` de la commande `question`] +#question(points: 2)[Comment fonctionne le comptage des points ?] +#reponse[ + Le compteur global `"points"` enregistre le total de points disponibles +en partant du début du document. Chaque en-tête de section calcule les points +disponibles à partir de la différence de ce compteur entre l'en-tête de section +en cours et le prochain en-tête. +] + +== Sous-sections + +#question(points: 1)[Et s'il y a des sous-sections ?][ + La différence est en fait calculée avec le prochain en-tête de niveau égal ou inférieur, ce qui correspond à la division logique du document. +] + += Compilation + +Le package doit être installé dans l'emplacement adéquat (par défaut: `$HOME/.local/share/typst/packages/y-sec/exam/0.1.0`) + +#question[Comment contrôler l'affichage des réponses ?][ + Passer l'option `--input mode=solution` à `typst` pour activer le mode solution. +] + +Le makefile suivant permet de générer les deux documents à la suite: + +```make +%.pdf: %.typ + typst compile $< + +%-solution.pdf: %.typ + typst compile --input mode=solution $< +``` diff --git a/exam.typ b/lib.typ similarity index 55% rename from exam.typ rename to lib.typ index 362f168..f0587b9 100644 --- a/exam.typ +++ b/lib.typ @@ -3,11 +3,13 @@ #let french_month = ("janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre") +#let solution_mode = { sys.inputs.at("mode", default: "") == "solution" }; + #let points_counter(h) = { // Find the first heading at a level equal or higher than this one let headings_after = query(selector(heading) .after(h.location()) - ).filter(nh => nh.level >= h.level); + ).filter(nh => nh.level <= h.level); let start_value = pctr.at(h.location()) let stop_value = if headings_after.len() == 0 { pctr.final() } @@ -17,18 +19,40 @@ #let show_points(pts) = [#pts] + if pts < 2 { " pt" } else { " pts" } -// Rend les réponses visibles seulement si compilé avec `--input grading=true` -#let grading(content) = if sys.inputs.at("grading", default: "solution") == "solution" { set text(fill: olive); content } else { hide(content) } +// Rend les réponses visibles seulement si compilé avec `--input mode=solution` +#let solution(content) = if solution_mode { set text(fill: olive); content } else { hide(content) } + +#let choix(rep, content) = { + if rep and solution_mode { "☒ " } else { "☐ " } + content +} #let exam(title: "Exam", course: none, date: none, class: none, - author: none, subtitle: none, indications: none, doc) = { + year: none, author: none, subtitle: none, indications: none, + time: none, allowed: none, doc) = { { set text(font: "Montserrat") let date = if date == none { datetime.today() } else { date }; + let year = if year == none { date.year() } else { year }; - align(center, text(size:26pt, course + " - " + date.display("[year]"))) + + let indications = if indications == none { + [ + - Ce test doit être réalisé en maximum *#time*. + + - Aucun document n'est admis, excepté #allowed . + + - L'utilisation de tout type de matériel électronique est interdite. + + - Ne pas oublier d'inscrire votre nom et prénom à l'endroit prévu à cet effet. + + - Merci de respecter un silence absolu jusqu'à ce que le dernier étudiant + aie rendu sa copie +]} else { indications }; + + align(center, text(size:26pt, [#course - #year])) align(center, text(size:26pt, title)) align(center, text(size:15pt, fill: luma(80), subtitle)) v(5%) @@ -57,15 +81,27 @@ set text(font: "Carlito", size: 12pt) show heading: h => pad(bottom: 0.5em, text(fill: eastern, size: 16pt, weight: "light", h.body + [ (#show_points(points_counter(h)))])) + + set page(header: [#course #year - #title], footer: align(right, text(fill: gray, "Page ") + context counter(page).display("1 | 1", both: true))) doc }; -#let question(points: 1, q, a) = { - context pctr.step() - let pts = strong([(#show_points(points))]); - - let num = context { qctr.step(); qctr.display() } - [#num) #q #pts] - let answ_block = block(width: 100%, inset: 1em, grading(a)); +#let reponse(a) = { + let answ_block = block(width: 100%, inset: 1em, solution(a)); block(stroke: black, answ_block) } + +#let question(points: 1, q, ..a) = { + context pctr.update(t => t + points) + let pts = strong([(#show_points(points))]); + + context { qctr.step() } + let num = context { qctr.display() }; + [#num) #q #pts] + if a.pos().len() > 0 { + reponse(a.pos().at(0)) + } + +} + + diff --git a/typst.toml b/typst.toml new file mode 100644 index 0000000..5751c6d --- /dev/null +++ b/typst.toml @@ -0,0 +1,7 @@ +[package] +name = "exam" +version = "0.1.0" +entrypoint = "lib.typ" +authors = ["Maxime Augier "] +license = "MIT" +description = "A midterm exam"