diff --git a/README.md b/README.md deleted file mode 100644 index a546bee..0000000 --- a/README.md +++ /dev/null @@ -1,143 +0,0 @@ -# The `exam` package - -This is a simple package to typeset exams. With it, you can define exercises, -and items, each with a number of points. -A summary is then printed on the front page. - -## Installation (Linux only) - -#### Using `typship` - -```bash -typship download -n local https://imsc.uni-graz.at/git/gjankowiak/typst-exam/ -``` - -#### Manually - -```bash -mkdir -p ~/.local/share/typst/packages/local/exam -cd ~/.local/share/typst/packages/local/exam -curl https://imsc.uni-graz.at/git/gjankowiak/typst-exam/archive/v0.1.0.tar.gz | tar zx --xform 's/typst-exam/0.1.0/' -``` - -## Usage - -Writing an exam is now easy (see [API](#api) for all options): - -```typ -#import "@local/exam:0.1.0": exam, exercise, exercise-items, mtext - -#show: exam.with( - title: "Exam", - - course-title: [Abstract Binary Computation & Elegant Finite Graphs], - - institution: [Super University], - - date: "1. January 1970", - course-short-title: "ABC & EFG", - - course-code: "π", - duration-minutes: "90", - - ask-trainer-name: false, - ask-group: true, - - language: "en", -) -``` - -### Defining exercises and items - -A new exercise can be started using `#exercise("title", nb_points)`, for example: - -```typ -#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? transitive? symmetric? antisymmetric? -``` - -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`. - -```typ -#exercise("Properties of functions", 3) - -Let $f : (0, +infinity) → (0, +infinity)$ with $f (x) = e^(-x)$. - -#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) - -You can also [play with it](https://typst.app/project/rrQbGYoQ3pePdfSMl0tmv4) on the Typst Playground (you might need an account). - -### 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. - -### API - -```typ -#exam( - title: "%KLAUSUR or EXAM%", - - 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"), - - duration-minutes: "%DURATION%", - - ask-student-number: false, - ask-trainer-name: false, - ask-group: true, - - instructions: none, - - language: "de", // or "en" - font-size: 12pt, - paper-size: "a4", - - exercise-numbering: "1", -) -``` diff --git a/lib.typ b/lib.typ index a2004c6..c29a740 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(override-points: true, numbering: "a)", items) = block[ - #let c = counter("exercise-items") +#let exercise_items(items) = block[ + #let c = counter("exercise_items") #let exercise_points = 0 #c.update(0) #table( @@ -78,24 +78,21 @@ pt = get_L("points_short") } ( - [#c.step()#v(4pt) *#context c.display(numbering)* #statement], + [#c.step()#v(4pt) *#context c.display("a)")* #statement], rect(radius:1cm, inset: (x:5pt, y:3pt), stroke: gray+0.5pt)[#points #pt], ) } ) - - #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 - } - }) - } + #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 + } + }) } ] @@ -103,45 +100,43 @@ // 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(title, points) = { +#let exercise_header(title, points) = { // state("exercise_points").update(points) let exercise_counter = counter("exercise") exercise_counter.step() context { let current_exercise = exercise_counter.get().at(0) - let exercise-numbering = state("exercise-numbering").get() 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(exercise-numbering) + let exercise_label = exercise_counter.display() 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(exercise-numbering)}*]], [#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()}*]], [#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-code: "%course-code%", - + course_title: [%COURSE_TITLE_FIRST_LINE% \ %COURSE_TITLE_SECOND_LINE%], + course_short_title: "%COURSE_SHORT_TITLE%", date: "%DATE%", institution: smallcaps("NAWI Graz"), - duration-minutes: "%DURATION%", + course_number: "%COURSE_NUMBER%", + 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, @@ -149,8 +144,6 @@ font-size: 12pt, paper-size: "a4", - exercise-numbering: "1", - body ) = { @@ -162,8 +155,6 @@ set page(paper: paper-size) set text(size: font-size, lang: language, hyphenate: true) -state("exercise-numbering").update(exercise-numbering) - /* * Localization */ @@ -171,7 +162,7 @@ state("exercise-numbering").update(exercise-numbering) let translations = none if language == "en" { translations = ( - course-code: "Course code", + course_number: "Course number", exam_duration: "Exam duration", student_name: "Name", student_number: "Student number", @@ -189,7 +180,7 @@ if language == "en" { ) } else if language == "de" { translations = ( - course-code: "LV-Nummer", + course_number: "LV-Nummer", exam_duration: "Arbeitszeit", student_name: "Name", student_number: "Matrikelnr.", @@ -269,7 +260,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))]], @@ -280,10 +271,10 @@ set page( v(1fr) align(center)[= #title] v(0.5cm) -align(center)[== #course-title] +align(center)[== #course_title] v(1fr) -[*#get_L("course-code")*: #course-code \ *#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) @@ -295,13 +286,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 deleted file mode 100644 index d9784d9..0000000 Binary files a/screenshot-example.png and /dev/null differ