#let qctr = counter("question") #let pctr = counter("points") #let french_month = ("janvier", "février", "mars", "avril", "mai", "juin", "juillet", "août", "septembre", "octobre", "novembre", "décembre") #let compile_mode = sys.inputs.at("mode", default: ""); #let enable_solutions = { compile_mode in ("solution", "notes") }; #let enable_notes = { compile_mode == "notes" }; #let points_counter(h) = { // Find the first heading at a level equal or lower (more important) than this one let headings_after = query(selector(heading) .after(h.location(), inclusive: false) ).filter(nh => nh.level <= h.level); let start_value = pctr.at(h.location()) let stop_value = if headings_after.len() == 0 { pctr.final() } else { pctr.at(headings_after.first().location()) } stop_value.at(0) - start_value.at(0) } #let show_points(pts) = if pts == 0 { none } else if pts == 1 { [(1 pt)] } else { [(#pts pts)] } // Rend les réponses visibles seulement si compilé avec `--input mode=solution` #let solution(content) = if enable_solutions { set text(fill: olive); content } else { hide(content) } #let qcm(qcm_list) = { //let checked_box = "☑"; //let unchecked_box = "☐"; let checked_box = box(stroke: black, outset: 0.2em, "X") + " "; let unchecked_box = box(stroke: black, outset: 0.2em, hide("X")) + " "; set list(marker: unchecked_box, indent: 2em) if enable_solutions { show : i => { show box: b => checked_box; set text(fill: olive); i }; qcm_list } else { qcm_list } } #let exam(title: "Exam", course: none, date: none, class: none, 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 }; let total = context [#pctr.final().at(0)] 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%) align(center, text(size:14pt, author)) v(3%) align(center, text(size:14pt, date.display("[day] ") + french_month.at(date.month() - 1) + date.display(" [year]"))) v(7%) align(left, text(size: 20pt, "Nom :")) align(left, text(size: 20pt, "Prénom :")) v(3%) line(length: 100%, stroke: luma(80)) v(3%) text(size: 16pt, "Indications :") { set text(size: 12pt) set list(marker: "●", indent: 2em, body-indent: 1em) indications } v(5%) align(right, text(size: 12pt, [Total: #total points])) } pagebreak() 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)))) let head = text(fill: gray)[#course #year - #title]; set page(header: head, footer: align(right, text(fill: gray, "Page ") + context counter(page).display("1 | 1", both: true))) doc }; // Bloc réponse caché en mode production #let reponse(a) = { let answ_block = block(width: 100%, inset: 1em, solution(a)); block(stroke: black, answ_block) v(2em) } #let notes(n) = if enable_notes { text(fill: red, n) } else { none }; // Question d'examen. Numérotée automatiquement, nombre de points optionnel // (défaut 1), bloc de réponse automatique si 2e argument présent #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)) } }