diff --git a/README.md b/README.md index 4abc958..a739df5 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ A summary is then printed on the front page. Writing an exam is now easy: - #import "@local/exam:0.1.0": exam, exercise_header, exercise_items, mtext + #import "@local/exam:0.1.0": exam, exercise, exercise-items, mtext #show: exam.with( title: "Exam", @@ -32,7 +32,7 @@ Writing an exam is now easy: date: "1. January 1970", course_short_title: "ABC & EFG", - course_number: "π", + course_code: "π", duration_minutes: "90", ask_trainer_name: false, @@ -43,37 +43,59 @@ Writing an exam is now easy: ### Defining exercises and items -A new exercise can be started using `#exercise_header("title", nb_points)`, for example: +A new exercise can be started using `#exercise("title", nb_points)`, for example: - #exercise_header("Relations and their properties", 2) + #exercise("Relations and their properties", 2) Consider the relation $R subset NN^2$, defined as follows: $ (x, y) in R #h(0.5cm) <==> #h(0.5cm) x + y #mtext[is odd]. $ - Is $R$ reflexive? transitiv? symmetrisch? antisymmetrisch? + Is $R$ reflexive? transitive? symmetric? antisymmetric? -On can also defined (sub-)items for the exercise using `#exercise_items(items, override_points:true)`, +On can also defined (sub-)items for the exercise using `#exercise-items(override_points:true, numbering:"a)", items)`, where `items` is an array of `(nb_points, statement)`. By default, the number of points the exercise is worth is recomputed as the sum of points for all items. This behaviour can be turned off by setting `override_points: false`. - #exercise_header("Properties of functions", 3) + #exercise("Properties of functions", 3) Let $f : (0, +infinity) → (0, +infinity)$ with $f (x) = e^(-x)$. - #exercise_items(( + #exercise-items(( (1, [Is $f$ injective?]), (1, [Is $f$ surjective?]), (1, [Is $f$ bijective?]), )) + #exercise("Logical operators", 3.14) + + Consider the following truthtable: + + #align(center, table( + stroke: frame(0.5pt), + columns: (auto, auto, auto), + align: center, + [$A$], [$B$], [$A or B$], + [1], [1], [1], + [1], [0], [1], + [0], [1], [1], + [0], [0], [1] + )) + + #exercise-items(override-points: false, numbering: "i)", ( + (1, [Is the truthtable correct?]), + (1, [If not, fix it.]), + )) + All together, this should output something like: ![screenshot](screenshot-example.png) ### Utilities +These are used in the example above. + - `mtext(str)` to typeset text within math mode using the default text font. - `frame(stroke_width)` provides `stroke` for use in a `table`, horizontal lines only, top and bottom lines are bold. diff --git a/lib.typ b/lib.typ index c29a740..476b489 100644 --- a/lib.typ +++ b/lib.typ @@ -57,13 +57,13 @@ // - int is the number of points this item is worth, // - expression is the item to typeset. // example: -// #exercise_items(( +// #exercise-items(( // (1, [Untersuchen Sie, ob $f$ injektiv ist.]), // (1, [Untersuchen Sie, ob $f$ surjektiv ist.]), // (1, [Geben Sie im Falle ihrer Existenz die Umkehrfunktion $f^(−1)$ an.]) //)) -#let exercise_items(items) = block[ - #let c = counter("exercise_items") +#let exercise-items(override-points: true, numbering: "a)", items) = block[ + #let c = counter("exercise-items") #let exercise_points = 0 #c.update(0) #table( @@ -78,21 +78,24 @@ pt = get_L("points_short") } ( - [#c.step()#v(4pt) *#context c.display("a)")* #statement], + [#c.step()#v(4pt) *#context c.display(numbering)* #statement], 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 - } - }) + + #if override-points { + 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 + } + }) + } } ] @@ -100,7 +103,7 @@ // Used to compute the overview table (see below) // title: string, title of the exercise // points: int, the number of points the exercise is worth -#let exercise_header(title, points) = { +#let exercise(title, points, numbering: "1") = { // state("exercise_points").update(points) let exercise_counter = counter("exercise") exercise_counter.step() @@ -111,32 +114,33 @@ 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(numbering) exercise_registry.update(entries => entries + ((exercise_label, title, points),)) block(radius: 1cm, stroke: none, fill: lightgray, inset: 1pt, table( columns: (auto, 1fr, auto), align: (left, center, right), 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(final_points)*]], + [#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#get_L("exercise") #context{exercise_counter.display(numbering)}*]], [#title], [#rect(radius:1cm, outset: 4pt, inset: (x:2pt, y:0pt), fill: white)[*#format_points(final_points)*]], ) )}} #let exam( title: "%KLAUSUR or EXAM%", - course_title: [%COURSE_TITLE_FIRST_LINE% \ %COURSE_TITLE_SECOND_LINE%], - course_short_title: "%COURSE_SHORT_TITLE%", + course-title: [%COURSE_TITLE_FIRST_LINE% \ %COURSE_TITLE_SECOND_LINE%], + course-short-title: "%course-short-title%", + course-code: "%course-code%", + date: "%DATE%", institution: smallcaps("NAWI Graz"), - course_number: "%COURSE_NUMBER%", - duration_minutes: "%DURATION%", + duration-minutes: "%DURATION%", - ask_student_number: false, - ask_trainer_name: false, - ask_group: true, + ask-student-number: false, + ask-trainer-name: false, + ask-group: true, instructions: none, @@ -162,7 +166,7 @@ set text(size: font-size, lang: language, hyphenate: true) let translations = none if language == "en" { translations = ( - course_number: "Course number", + course-code: "Course code", exam_duration: "Exam duration", student_name: "Name", student_number: "Student number", @@ -180,7 +184,7 @@ if language == "en" { ) } else if language == "de" { translations = ( - course_number: "LV-Nummer", + course-code: "LV-Nummer", exam_duration: "Arbeitszeit", student_name: "Name", student_number: "Matrikelnr.", @@ -260,7 +264,7 @@ set page( inset: 0pt, align: (x, y) => { if x < 2 { left+bottom } else { right }}, columns: (auto, 1fr, auto), - [#institution], [], [#course_short_title \ _#(date)_], + [#institution], [], [#course-short-title \ _#(date)_], ) ], footer: [#align(right)[#context(counter(page).display("1 / 1", both:true))]], @@ -271,10 +275,10 @@ set page( v(1fr) align(center)[= #title] v(0.5cm) -align(center)[== #course_title] +align(center)[== #course-title] v(1fr) -[*#get_L("course_number")*: #course_number* \ #get_L("exam_duration"): #duration_minutes #get_L("minutes")*] +[*#get_L("course-code")*: #course-code \ *#get_L("exam_duration"): #duration-minutes #get_L("minutes")*] v(0.5cm) @@ -286,13 +290,13 @@ block(radius: 0.2cm, fill: lightgray, inset: 3pt, clip: true, table( inset: (left:7pt, right:0pt, y:0pt), align: horizon, [#get_L("student_name")], rect(fill: white, width: 100%, radius: 0.2cm), - .. if ask_student_number { + .. if ask-student-number { ([#get_L("student_number")], rect(fill: white, width: 100%, radius: 0.2cm)) }, - ..if ask_trainer_name { + ..if ask-trainer-name { ([#get_L("trainer_name")], rect(fill: white, width: 100%, radius: 0.2cm)) }, - ..if ask_group { + ..if ask-group { ([#get_L("group")], rect(fill: white, width: 100%, radius: 0.2cm)) } )) diff --git a/screenshot-example.png b/screenshot-example.png index 820f59a..6f1c1ca 100644 Binary files a/screenshot-example.png and b/screenshot-example.png differ