diff --git a/abstract.tex b/abstract.tex index 04e7281..ae8bac8 100644 --- a/abstract.tex +++ b/abstract.tex @@ -1,18 +1,24 @@ This is a general-purpose, simple exam generator for the \LaTeX\ documentclass exam~\cite{exam}. -It reads the question database from the source code of old exams or a file with -questions. -The questions are expected to start with the \verb'\question' macro and end by -the start of the next question, the \verb'\end{document}' macro or the end of -the file. -In each question the generator will look for a label with the format -\verb'\label{q:tag1:tag2:...:tagN}' or comments on the form -\verb'% tags: tag1:...:tagN'. -The set of tags is the union of all tags found. +It reads the question database from the source code of old exams, a file with +questions or teaching material with exercises. +The questions are expected to be either questions from the exam document class, +i.e.\ start with the +\verb'\question' +macro and end by the start of the next question, or be an exercise environment, +\verb'\begin{exercise}' +and +\verb'\end{exercise}'. + +In each question the generator will look for tags: +tags can be used to tag that a question covers a specific topic or intended +learning outcome (or both). +The set of tags is the union of all tags found in the body of the question. The tags are used to filter which questions qualify for inclusion in the exam to be generated. -The tags can be used to represent topic, difficulty level, intended learning -outcomes, etc. +Tags are given in questions as comments on the form +\verb'% tags: tag1:...:tagN', +and several such lines are allowed. The generation algorithm works as follows: The set of desired tags, \(D\), is specified on the command line. diff --git a/examgen.py.nw b/examgen.py.nw index b190e69..731d679 100644 --- a/examgen.py.nw +++ b/examgen.py.nw @@ -153,13 +153,13 @@ two database files. Since the design allows for using old exams as database files, then we must adapt our database format to this. -We know that the exams are using the exam~\cite{exam} document class. +We assume that the exams are using the exam~\cite{exam} document class. As such each question will start with the command \verb'\question' and end at the beginning of the next question. Or in the special case of the last question, it ends with the end of the [[questions]] environment. -(See lines 87--290 in the example exam in \cref{sec:ExampleExam} for an example -of a [[questions]] environment.) +However, we also want to be able to use exercises from teaching material. + We can thus make use of Python's regular expressions facilities~\cite{regex}: <>= import re @@ -167,12 +167,17 @@ import re match a question: <>= question_code_pattern = re.compile( - "\\\\question(.|\n)*?(?=(\\\\question|\\\\end{questions}))", + "(\\\\question(.|\n)*?" + "(?=(\\\\question|\\\\end{questions}|\\\\begin{exercise}))|" + "\\\\begin{exercise}(.|\n)*?\\\\end{exercise})", re.MULTILINE) @ (See~\cite{regex-lookaround} for a treatment of zero-width assertions in regular expressions.) +The regular expression consists of two parts. +The first part matches questions (exam format) and the second part matches +exercise environments. This expression will conveniently also include any parts or solution -environments used in the question. +environments used in the exam question format. To read the questions database we need to do the following: <>= @@ -212,15 +217,27 @@ A suitable one is to construct the question from its LaTeX code: <>= def __init__(self, code): <> -@ This LaTeX code is also a natural attribute of the class: +@ This LaTeX code is also a natural attribute of the class. +However, it should be on the exam question format, so for the exercise +environments we must do some transformation. <>= self.__code = code +if self.__code.find(r"\begin{exercise}") >= 0: + <> @ We also need a get-method for the code attribute: <>= def get_code(self): return self.__code @ +To transform an exercise to a question we can replace the beginning of the +environment with the question command and simply drop the end of the +environment. +<>= +self.__code = self.__code.replace(r"\begin{exercise}", r"\question ") +self.__code = self.__code.replace(r"\end{exercise}", "") +@ + To be able to add a question to a set, the data structure must be : <>= def __hash__(self):