fully dynamic counters
This commit is contained in:
parent
c995bfa0c4
commit
4d81aa3e55
1 changed files with 103 additions and 47 deletions
150
lib.typ
150
lib.typ
|
|
@ -51,7 +51,8 @@
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// Displays a list of sub-questions and the associated points
|
// Displays a list of sub-questions and the associated points.
|
||||||
|
// Updates the exercise's points automatically.
|
||||||
// items: array of (int, expression), where
|
// items: array of (int, expression), where
|
||||||
// - int is the number of points this item is worth,
|
// - int is the number of points this item is worth,
|
||||||
// - expression is the item to typeset.
|
// - expression is the item to typeset.
|
||||||
|
|
@ -63,11 +64,13 @@
|
||||||
//))
|
//))
|
||||||
#let exercise_items(items) = block[
|
#let exercise_items(items) = block[
|
||||||
#let c = counter("exercise_items")
|
#let c = counter("exercise_items")
|
||||||
|
#let exercise_points = 0
|
||||||
#c.update(0)
|
#c.update(0)
|
||||||
#table(
|
#table(
|
||||||
columns: (1fr, auto),
|
columns: (1fr, auto),
|
||||||
stroke: none,
|
stroke: none,
|
||||||
..for (points, statement) in items {
|
..for (points, statement) in items {
|
||||||
|
exercise_points += points
|
||||||
let pt
|
let pt
|
||||||
if points <= 1 {
|
if points <= 1 {
|
||||||
pt = get_L("point_short")
|
pt = get_L("point_short")
|
||||||
|
|
@ -79,35 +82,54 @@
|
||||||
rect(radius:1cm, inset: (x:5pt, y:3pt), stroke: gray+0.5pt)[#points #pt],
|
rect(radius:1cm, inset: (x:5pt, y:3pt), stroke: gray+0.5pt)[#points #pt],
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)]
|
)
|
||||||
|
#context {
|
||||||
|
state("exercise-registry").update( arr => {
|
||||||
|
if (arr.len() == 0) {
|
||||||
|
arr
|
||||||
|
} else {
|
||||||
|
let new_arr = arr.slice(0, arr.len()-1)
|
||||||
|
new_arr.push((arr.last().at(0), arr.last().at(1), exercise_points))
|
||||||
|
new_arr
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
// Displays a header for the exercise, ex. number is incremented automatically
|
// Displays a header for the exercise, ex. number is incremented automatically
|
||||||
// Used to compute the overview table (see below)
|
// Used to compute the overview table (see below)
|
||||||
// title: string, title of the exercise
|
// title: string, title of the exercise
|
||||||
// points: int, the number of points the exercise is worth
|
// points: int, the number of points the exercise is worth
|
||||||
#let exercise_header(title, points) = {
|
#let exercise_header(title, points) = {
|
||||||
|
// state("exercise_points").update(points)
|
||||||
let exercise_counter = counter("exercise")
|
let exercise_counter = counter("exercise")
|
||||||
|
exercise_counter.step()
|
||||||
context {
|
context {
|
||||||
let exercise_registry = state("exercise-registry").get()
|
let current_exercise = exercise_counter.get().at(0)
|
||||||
exercise_counter.step()
|
let exercise_registry = state("exercise-registry")
|
||||||
|
let final_points = 0
|
||||||
|
if (exercise_registry.final().len() >= current_exercise) {
|
||||||
|
final_points = exercise_registry.final().at(exercise_counter.get().at(0)-1).at(2)
|
||||||
|
}
|
||||||
let exercise_label = exercise_counter.display()
|
let exercise_label = exercise_counter.display()
|
||||||
// exercise_registry.update(entries => entries + ((exercise_label, title, points),))
|
exercise_registry.update(entries => entries + ((exercise_label, title, points),))
|
||||||
}
|
|
||||||
block(radius: 1cm, stroke: none, fill: lightgray, inset: 1pt,
|
block(radius: 1cm, stroke: none, fill: lightgray, inset: 1pt,
|
||||||
table(
|
table(
|
||||||
columns: (auto, 1fr, auto),
|
columns: (auto, 1fr, auto),
|
||||||
align: (left, center, right),
|
align: (left, center, right),
|
||||||
stroke: none,
|
stroke: none,
|
||||||
[#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#get_L("exercise") #context{exercise_counter.display()}*]], [#title], [#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#format_points(points)*]],
|
[#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#get_L("exercise") #context{exercise_counter.display()}*]], [#title], [#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#format_points(final_points)*]],
|
||||||
)
|
)
|
||||||
)}
|
)}}
|
||||||
|
|
||||||
#let exam(
|
#let exam(
|
||||||
title: "%KLAUSUR or EXAM%",
|
title: "%KLAUSUR or EXAM%",
|
||||||
|
|
||||||
subtitle: [%COURSE_TITLE_FIRST_LINE% \ %COURSE_TITLE_SECOND_LINE%],
|
course_title: [%COURSE_TITLE_FIRST_LINE% \ %COURSE_TITLE_SECOND_LINE%],
|
||||||
header_left: "%COURSE_SHORT_TITLE%",
|
course_short_title: "%COURSE_SHORT_TITLE%",
|
||||||
header_right: "%DATE%",
|
date: "%DATE%",
|
||||||
|
|
||||||
|
institution: smallcaps("NAWI Graz"),
|
||||||
|
|
||||||
course_number: "%COURSE_NUMBER%",
|
course_number: "%COURSE_NUMBER%",
|
||||||
duration_minutes: "%DURATION%",
|
duration_minutes: "%DURATION%",
|
||||||
|
|
@ -142,10 +164,12 @@ if language == "en" {
|
||||||
student_number: "Student number",
|
student_number: "Student number",
|
||||||
trainer_name: "Trainer",
|
trainer_name: "Trainer",
|
||||||
exercise: "Exercise",
|
exercise: "Exercise",
|
||||||
|
exercise_short: "Ex.",
|
||||||
point: "point",
|
point: "point",
|
||||||
points: "points",
|
points: "points",
|
||||||
point_short: "pt",
|
point_short: "pt",
|
||||||
points_short: "pts",
|
points_short: "pts",
|
||||||
|
points_obtained: "Points",
|
||||||
total_points: "Total",
|
total_points: "Total",
|
||||||
minutes: "minutes",
|
minutes: "minutes",
|
||||||
)
|
)
|
||||||
|
|
@ -157,10 +181,12 @@ if language == "en" {
|
||||||
student_number: "Matrikelnr.",
|
student_number: "Matrikelnr.",
|
||||||
trainer_name: "Übungsleiter",
|
trainer_name: "Übungsleiter",
|
||||||
exercise: "Beispiel",
|
exercise: "Beispiel",
|
||||||
|
exercise_short: "Bsp.",
|
||||||
point: "Punkt",
|
point: "Punkt",
|
||||||
points: "Punkte",
|
points: "Punkte",
|
||||||
point_short: "P.",
|
point_short: "P.",
|
||||||
points_short: "P.",
|
points_short: "P.",
|
||||||
|
points_obtained: "Erreichte Punkte",
|
||||||
total_points: "Gesamt",
|
total_points: "Gesamt",
|
||||||
minutes: "Minuten",
|
minutes: "Minuten",
|
||||||
)
|
)
|
||||||
|
|
@ -169,42 +195,67 @@ if language == "en" {
|
||||||
state("translations").update(translations)
|
state("translations").update(translations)
|
||||||
|
|
||||||
// State and counter for exercises
|
// State and counter for exercises
|
||||||
context { let exercise_registry = state("exercise-registry", ()) }
|
state("exercise-registry").update(())
|
||||||
|
|
||||||
// Displays an overview of the exam as a table, with
|
// Displays an overview of the exam as a table, with
|
||||||
// a row per exercise, the corresponding points and space to write the mark.
|
// a row per exercise, the corresponding points and space to write the mark.
|
||||||
// The
|
|
||||||
let exercise_overview_table() = context {
|
let exercise_overview_table() = context {
|
||||||
let entries = state("exercise-registry").final()
|
let exercise_registry = state("exercise-registry")
|
||||||
|
let entries = exercise_registry.final()
|
||||||
let total_points = entries.fold(0, (sum, entry) => sum + entry.at(2))
|
let total_points = entries.fold(0, (sum, entry) => sum + entry.at(2))
|
||||||
align(center)[
|
|
||||||
#set text(size: 14pt)
|
let columns = entries.map((i) => 1fr)
|
||||||
#table(
|
columns.push(1fr)
|
||||||
columns: (auto, auto),
|
[== #get_L("points_obtained")]
|
||||||
stroke: frame(0.5pt),
|
set text(size: 14pt)
|
||||||
fill: none,
|
block(radius: 0.3cm, stroke: none, fill: lightgray, inset: (x:0pt, y:4pt),
|
||||||
inset: 10pt,
|
table(
|
||||||
align: horizon,
|
columns: columns,
|
||||||
table.header[#get_L("exercise")][#get_L("points", cap: true)],
|
stroke: none,
|
||||||
table.hline(stroke: 1pt),
|
fill: none,
|
||||||
..for entry in entries {
|
gutter: 0pt,
|
||||||
(
|
inset: (x, y) => {
|
||||||
[#entry.at(0)],
|
let top = 8pt
|
||||||
[#h(2cm) / #entry.at(2)],
|
if y == 0 { top = 2pt }
|
||||||
)
|
if x == 0 {
|
||||||
},
|
(left:4pt, right:0pt, top: top, bottom: 0pt)
|
||||||
table.hline(stroke: 1pt),
|
} else if x == entries.len() {
|
||||||
[*#get_L("total_points")*], [#h(2.2cm) / *#total_points*]
|
(left:4pt, right:4pt, top: top, bottom: 0pt)
|
||||||
)
|
} else {
|
||||||
]
|
(left:2pt, right:0pt, top: top, bottom: 0pt)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
align: (x, y) => { if (y == 1) { right+horizon } else { center+horizon }},
|
||||||
|
..for entry in entries {
|
||||||
|
(
|
||||||
|
[#get_L("exercise_short", cap: true) #entry.at(0)],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
[*#get_L("total_points")*],
|
||||||
|
..for entry in entries {
|
||||||
|
(
|
||||||
|
rect(radius:0.2cm, fill: white, width: 100%, inset: 8pt, outset: 0pt, stroke:none)[/#entry.at(2)],
|
||||||
|
)
|
||||||
|
},
|
||||||
|
rect(radius:0.2cm, fill: white, width: 100%, inset: 8pt, outset: 0pt, stroke:black)[#strong([/#total_points])],
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Header and footer
|
// Header and footer
|
||||||
set page(
|
set page(
|
||||||
|
header-ascent: 20%,
|
||||||
header: [
|
header: [
|
||||||
#header_left
|
#set text(size: 8pt)
|
||||||
#h(1fr)
|
#table(
|
||||||
_#(header_right)_
|
stroke: none,
|
||||||
|
gutter: 4pt,
|
||||||
|
inset: 0pt,
|
||||||
|
align: (x, y) => { if x < 2 { left+bottom } else { right }},
|
||||||
|
columns: (auto, 1fr, auto),
|
||||||
|
[#institution], [], [#course_short_title \ _#(date)_],
|
||||||
|
)
|
||||||
],
|
],
|
||||||
footer: [#align(right)[#context(counter(page).display("1 / 1", both:true))]],
|
footer: [#align(right)[#context(counter(page).display("1 / 1", both:true))]],
|
||||||
)
|
)
|
||||||
|
|
@ -214,23 +265,24 @@ set page(
|
||||||
v(1fr)
|
v(1fr)
|
||||||
align(center)[= #title]
|
align(center)[= #title]
|
||||||
v(0.5cm)
|
v(0.5cm)
|
||||||
align(center)[== #subtitle]
|
align(center)[== #course_title]
|
||||||
v(1fr)
|
v(1fr)
|
||||||
|
|
||||||
[*#get_L("course_number")*: #course_number* \ #get_L("exam_duration"): #duration_minutes #get_L("minutes")*]
|
[*#get_L("course_number")*: #course_number* \ #get_L("exam_duration"): #duration_minutes #get_L("minutes")*]
|
||||||
|
|
||||||
v(0.5cm)
|
v(0.5cm)
|
||||||
|
|
||||||
table(
|
block(radius: 0.2cm, fill: lightgray, inset: 3pt, clip: true, table(
|
||||||
fill: (lightgray, none),
|
fill: lightgray,
|
||||||
stroke: frame(0.5pt),
|
stroke: none,
|
||||||
columns: (auto, 1fr),
|
columns: (auto, 1fr),
|
||||||
inset: 10pt,
|
gutter: 3pt,
|
||||||
|
inset: (left:7pt, right:0pt, y:0pt),
|
||||||
align: horizon,
|
align: horizon,
|
||||||
[#get_L("student_name")], [],
|
[#get_L("student_name")], rect(fill: white, width: 100%, radius: 0.2cm),
|
||||||
[#get_L("student_number")], [],
|
[#get_L("student_number")], rect(fill: white, width: 100%, radius: 0.2cm),
|
||||||
[#get_L("trainer_name")], [], // comment this line if unneeded
|
[#get_L("trainer_name")], rect(fill: white, width: 100%, radius: 0.2cm),
|
||||||
)
|
))
|
||||||
|
|
||||||
v(0.5cm)
|
v(0.5cm)
|
||||||
|
|
||||||
|
|
@ -238,21 +290,25 @@ if (instructions != none) {
|
||||||
[ #instructions ]
|
[ #instructions ]
|
||||||
} else {
|
} else {
|
||||||
if language == "de" [
|
if language == "de" [
|
||||||
|
- _Lesen Sie die ganze Prüfungsaufgabe durch_.
|
||||||
- Es sind keine Unterlagen und auch keine Taschenrechner erlaubt.
|
- Es sind keine Unterlagen und auch keine Taschenrechner erlaubt.
|
||||||
- Alle Rechenschritte (inklusive Zwischenresultate und Lösungswege) sind anzugeben und alle Antworten genau zu begründen bzw. zu beweisen!
|
- Alle Rechenschritte (inklusive Zwischenresultate und Lösungswege) sind anzugeben und alle Antworten genau zu begründen bzw. zu beweisen!
|
||||||
- Schreiben Sie auf jedes _lose_ Blatt Ihren Namen und Ihre Matrikelnummer!
|
- Schreiben Sie auf jedes _lose_ Blatt Ihren Namen und Ihre Matrikelnummer!
|
||||||
- Nicht mit roter Farbe schreiben.
|
- Nicht mit roter Farbe schreiben.
|
||||||
|
- Viel Erfolg!
|
||||||
]
|
]
|
||||||
if language == "en" [
|
if language == "en" [
|
||||||
|
- _Before starting, read the whole exam._
|
||||||
- Course material and calculators are not allowed.
|
- Course material and calculators are not allowed.
|
||||||
- Write down all the steps, including intermediate results. All answers must be substantiated!
|
- Write down all the steps, including intermediate results. All answers must be substantiated!
|
||||||
- Do not write in red.
|
- Do not write in red.
|
||||||
|
- Good luck!
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
v(1fr)
|
v(1fr)
|
||||||
|
|
||||||
// exercise_overview_table()
|
exercise_overview_table()
|
||||||
|
|
||||||
v(1fr)
|
v(1fr)
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue