Proper package and demo
This commit is contained in:
parent
8f3a5f4ce7
commit
a00747fe47
81
demo.typ
81
demo.typ
@ -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 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
|
#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 $<
|
||||||
|
```
|
||||||
|
@ -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
7
typst.toml
Normal 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"
|
Loading…
Reference in New Issue
Block a user