Proper package and demo

This commit is contained in:
Maxime Augier 2024-05-22 21:43:01 +02:00
parent 8f3a5f4ce7
commit a00747fe47
3 changed files with 108 additions and 40 deletions

View File

@ -1,45 +1,70 @@
#import "exam.typ" : exam, question #import "@ysec/exam:0.1.0": exam, question, reponse
#show: doc => exam( #show: doc => exam(
title: "Test Intermédiaire 2", title: "Examen en Typst",
subtitle: "HEIG-VD - Introduction à la Sécurité Informatique (ISI)", subtitle: "Y-Security - Typst Essentials",
course: "ISI", course: "TES",
date: datetime(year: 2024, month: 5, day: 27), date: datetime(year: 2024, month: 5, day: 27),
author: "Maxime Augier", author: "Maxime Augier",
indications: [ time: "45 minutes",
- Ce test doit être réalisé en maximum *45 minutes*. allowed: "une page A4 (recto-verso) de résumé personnel",
- 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
],
doc 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 linfrastructure 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 lURL 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 #question[Comment fonctionne la numérotation des questions ?][
ci dessus ?][Injection SQL (dans une clause ORDER BY)] 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 $<
```

View File

@ -3,11 +3,13 @@
#let french_month = ("janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre") #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) = { #let points_counter(h) = {
// Find the first heading at a level equal or higher than this one // Find the first heading at a level equal or higher than this one
let headings_after = query(selector(heading) let headings_after = query(selector(heading)
.after(h.location()) .after(h.location())
).filter(nh => nh.level >= h.level); ).filter(nh => nh.level <= h.level);
let start_value = pctr.at(h.location()) let start_value = pctr.at(h.location())
let stop_value = if headings_after.len() == 0 { pctr.final() } 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" } #let show_points(pts) = [#pts] + if pts < 2 { " pt" } else { " pts" }
// Rend les réponses visibles seulement si compilé avec `--input grading=true` // Rend les réponses visibles seulement si compilé avec `--input mode=solution`
#let grading(content) = if sys.inputs.at("grading", default: "solution") == "solution" { set text(fill: olive); content } else { hide(content) } #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, #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") set text(font: "Montserrat")
let date = if date == none { datetime.today() } else { date }; 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:26pt, title))
align(center, text(size:15pt, fill: luma(80), subtitle)) align(center, text(size:15pt, fill: luma(80), subtitle))
v(5%) v(5%)
@ -57,15 +81,27 @@
set text(font: "Carlito", size: 12pt) 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)))])) 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 doc
}; };
#let question(points: 1, q, a) = { #let reponse(a) = {
context pctr.step() let answ_block = block(width: 100%, inset: 1em, solution(a));
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));
block(stroke: black, answ_block) 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))
}
}

7
typst.toml Normal file
View File

@ -0,0 +1,7 @@
[package]
name = "exam"
version = "0.1.0"
entrypoint = "lib.typ"
authors = ["Maxime Augier <max@xolus.net>"]
license = "MIT"
description = "A midterm exam"