% \iffalse meta-comment % An Infrastructure for marking up Assignments % Copyright (c) 2019 Michael Kohlhase, all rights reserved % this file is released under the % LaTeX Project Public License (LPPL) % The original of this file is in the public repository at % http://github.com/sLaTeX/sTeX/ % \fi % % \iffalse % %<*driver> \def\libfolder#1{../../lib/#1} \input{../../doc/stex-docheader} \begin{document} \DocInput{\jobname.dtx} \end{document} % % \fi % % \GetFileInfo{hwexam.sty} % % \MakeShortVerb{\|} % \def\scsys#1{{{\sc #1}}\index{#1@{\sc #1}}} % \def\latexml{\scsys{LaTeXML}} % % \title{\texttt{hwexam.sty/cls}: An Infrastructure for formatting Assignments % and Exams\thanks{Version {\fileversion} (last revised {\filedate})}} % \author{Michael Kohlhase\\ % FAU Erlangen-N\"urnberg\\ % \url{http://kwarc.info/kohlhase}} % \maketitle % % \begin{documentation} %\ifinfulldoc\else % %\begin{abstract}{} % The |hwexam| package and class allows individual course assignment sheets and compound % assignment documents using problem files marked up with the |problem| package. % % This is the documentation for the \pkg{hwexam} package. For a more high-level % introduction, see \href{\basedocurl/manual.pdf}{the \sTeX Manual} or the % \href{\basedocurl/stex.pdf}{full \sTeX documentation}. % \end{abstract} % % \tableofcontents % \begin{sfragment}{The User Intterface} % \input{../../doc/packages/stex-hwexam} % \end{sfragment} % \fi % % \end{documentation} % %\begin{implementation} % % \section{Implementation: The hwexam Package} % % \subsection{Package Options} % % The first step is to declare (a few) package options that handle whether certain % information is printed or not. Some come with their own conditionals that are set by the % options, the rest is just passed on to the |problems| package. % % \begin{macrocode} %<*package> \ProvidesExplPackage{hwexam}{2022/09/14}{3.2.0}{homework assignments and exams} \RequirePackage{l3keys2e} \newif\iftest\testfalse \DeclareOption{test}{\testtrue\PassOptionsToPackage{\CurrentOption}{problem}} \newif\ifmultiple\multiplefalse \DeclareOption{multiple}{\multipletrue} \DeclareOption{lang}{\PassOptionsToPackage{\CurrentOption}{problem}} \DeclareOption*{\PassOptionsToPackage{\CurrentOption}{problem}} \ProcessOptions % \end{macrocode} % Then we make sure that the necessary packages are loaded (in the right versions). % \begin{macrocode} \RequirePackage{keyval}[1997/11/10] \RequirePackage{problem} % \end{macrocode} % % \begin{macro}{\hwexam@*@kw} % For multilinguality, we define internal macros for keywords that can be specialized in % |*.ldf| files. % \begin{macrocode} \newcommand\hwexam@assignment@kw{Assignment} \newcommand\hwexam@given@kw{Given} \newcommand\hwexam@due@kw{Due} \newcommand\hwexam@testemptypage@kw{This~page~was~intentionally~left~blank~for~extra~space} \newcommand\hwexam@minutes@kw{minutes} \newcommand\correction@probs@kw{prob.} \newcommand\correction@pts@kw{total} \newcommand\correction@reached@kw{reached} \newcommand\correction@sum@kw{Sum} \newcommand\correction@grade@kw{grade} \newcommand\correction@forgrading@kw{To~be~used~for~grading,~do~not~write~here} % \end{macrocode} % \end{macro} % % For the other languages, we set up triggers % \begin{macrocode} \AddToHook{begindocument}{ \ltx@ifpackageloaded{babel}{ \makeatletter \clist_set:Nx \l_tmpa_clist {\bbl@loaded} \exp_args:NNx \clist_if_in:NnT \l_tmpa_clist {\detokenize{ngerman}}{ \input{hwexam-ngerman.ldf} } \exp_args:NNx \clist_if_in:NnT \l_tmpa_clist {\detokenize{finnish}}{ \input{hwexam-finnish.ldf} } \exp_args:NNx \clist_if_in:NnT \l_tmpa_clist {\detokenize{french}}{ \input{hwexam-french.ldf} } \exp_args:NNx \clist_if_in:NnT \l_tmpa_clist {\detokenize{russian}}{ \input{hwexam-russian.ldf} } \makeatother }{} } % \end{macrocode} % \subsection{Assignments} % % Then we set up a counter for problems and make the problem counter inherited from % |problem.sty| depend on it. Furthermore, we specialize the |\prob@label| macro to take % the assignment counter into account. % \begin{macrocode} \newcounter{assignment} %\numberproblemsin{assignment} % \end{macrocode} % % We will prepare the keyval support for the |assignment| environment. % % \begin{macrocode} \keys_define:nn { hwexam / assignment } { id .str_set_x:N = \l_@@_assign_id_str, number .int_set:N = \l_@@_assign_number_int, title .tl_set:N = \l_@@_assign_title_tl, type .tl_set:N = \l_@@_assign_type_tl, given .tl_set:N = \l_@@_assign_given_tl, due .tl_set:N = \l_@@_assign_due_tl, loadmodules .code:n = { \bool_set_true:N \l_@@_assign_loadmodules_bool } } \cs_new_protected:Nn \_@@_assignment_args:n { \str_clear:N \l_@@_assign_id_str \int_set:Nn \l_@@_assign_number_int {-1} \tl_clear:N \l_@@_assign_title_tl \tl_clear:N \l_@@_assign_type_tl \tl_clear:N \l_@@_assign_given_tl \tl_clear:N \l_@@_assign_due_tl \bool_set_false:N \l_@@_assign_loadmodules_bool \keys_set:nn { hwexam / assignment }{ #1 } } % \end{macrocode} % % The next three macros are intermediate functions that handle the case gracefully, where % the respective token registers are undefined. % % The |\given@due| macro prints information about the given and due status of the % assignment. Its arguments specify the brackets. % % \begin{macrocode} \newcommand\given@due[2]{ \bool_lazy_all:nF { {\tl_if_empty_p:V \l_@@_inclassign_given_tl} {\tl_if_empty_p:V \l_@@_assign_given_tl} {\tl_if_empty_p:V \l_@@_inclassign_due_tl} {\tl_if_empty_p:V \l_@@_assign_due_tl} }{ #1 } \tl_if_empty:NTF \l_@@_inclassign_given_tl { \tl_if_empty:NF \l_@@_assign_given_tl { \hwexam@given@kw\xspace\l_@@_assign_given_tl } }{ \hwexam@given@kw\xspace\l_@@_inclassign_given_tl } \bool_lazy_or:nnF { \bool_lazy_and_p:nn { \tl_if_empty_p:V \l_@@_inclassign_due_tl }{ \tl_if_empty_p:V \l_@@_assign_due_tl } }{ \bool_lazy_and_p:nn { \tl_if_empty_p:V \l_@@_inclassign_due_tl }{ \tl_if_empty_p:V \l_@@_assign_due_tl } }{ ,~ } \tl_if_empty:NTF \l_@@_inclassign_due_tl { \tl_if_empty:NF \l_@@_assign_due_tl { \hwexam@due@kw\xspace \l_@@_assign_due_tl } }{ \hwexam@due@kw\xspace \l_@@_inclassign_due_tl } \bool_lazy_all:nF { { \tl_if_empty_p:V \l_@@_inclassign_given_tl } { \tl_if_empty_p:V \l_@@_assign_given_tl } { \tl_if_empty_p:V \l_@@_inclassign_due_tl } { \tl_if_empty_p:V \l_@@_assign_due_tl } }{ #2 } } % \end{macrocode} % % \begin{macro}{\assignment@title} % This macro prints the title of an assignment, the local title is overwritten, if there % is one from the |\inputassignment|. |\assignment@title| takes three arguments the % first is the fallback when no title is given at all, the second and third go around % the title, if one is given. % \begin{macrocode} \newcommand\assignment@title[3]{ \tl_if_empty:NTF \l_@@_inclassign_title_tl { \tl_if_empty:NTF \l_@@_assign_title_tl { #1 }{ #2\l_@@_assign_title_tl#3 } }{ #2\l_@@_inclassign_title_tl#3 } } % \end{macrocode} % \end{macro} % % \begin{macro}{\assignment@number} % Like |\assignment@title| only for the number, and no around part. % \begin{macrocode} \newcommand\assignment@number{ \int_compare:nNnTF \l_@@_inclassign_number_int = {-1} { \int_compare:nNnTF \l_@@_assign_number_int = {-1} { \arabic{assignment} } { \int_use:N \l_@@_assign_number_int } }{ \int_use:N \l_@@_inclassign_number_int } } % \end{macrocode} % \end{macro} % % With them, we can define the central |assignment| environment. This has two forms % (separated by |\ifmultiple|) in one we make a title block for an assignment sheet, and % in the other we make a section heading and add it to the table of contents. We first % define an assignment counter % % \begin{environment}{assignment} % For the |assignment| environment we delegate the work to the |@assignment| environment % that depends on whether |multiple| option is given. % \begin{macrocode} \newenvironment{assignment}[1][]{ \_@@_assignment_args:n { #1 } %\sref@target \int_compare:nNnTF \l_@@_assign_number_int = {-1} { \global\stepcounter{assignment} }{ \global\setcounter{assignment}{\int_use:N\l_@@_assign_number_int} } \setcounter{sproblem}{0} \renewcommand\prob@label[1]{\assignment@number.##1} \def\current@section@level{\document@hwexamtype} %\sref@label@id{\document@hwexamtype \thesection} \begin{@assignment} }{ \end{@assignment} } % \end{macrocode} % In the multi-assignment case we just use the |omdoc| environment for suitable % sectioning. % \begin{macrocode} \def\ass@title{ {\protect\document@hwexamtype}~\arabic{assignment} \assignment@title{}{\;(}{)\;} -- \given@due{}{} } \ifmultiple \newenvironment{@assignment}{ \bool_if:NTF \l_@@_assign_loadmodules_bool { \begin{sfragment}[loadmodules]{\ass@title} }{ \begin{sfragment}{\ass@title} } }{ \end{sfragment} } % \end{macrocode} % for the single-page case we make a title block from the same components. % \begin{macrocode} \else \newenvironment{@assignment}{ \begin{center}\bf \Large\@title\strut\\ \document@hwexamtype~\arabic{assignment}\assignment@title{\;}{:\;}{\\} \large\given@due{--\;}{\;--} \end{center} }{} \fi% multiple % \end{macrocode} % \end{environment} % % \subsection{Including Assignments} % % \begin{macro}{\in*assignment} % This macro is essentially a glorified |\include| statement, it just sets some internal % macros first that overwrite the local points Importantly, it resets the |inclassig| % keys after the input. % \begin{macrocode} \keys_define:nn { hwexam / inclassignment } { %id .str_set_x:N = \l_@@_assign_id_str, number .int_set:N = \l_@@_inclassign_number_int, title .tl_set:N = \l_@@_inclassign_title_tl, type .tl_set:N = \l_@@_inclassign_type_tl, given .tl_set:N = \l_@@_inclassign_given_tl, due .tl_set:N = \l_@@_inclassign_due_tl, mhrepos .str_set_x:N = \l_@@_inclassign_mhrepos_str } \cs_new_protected:Nn \_@@_inclassignment_args:n { \int_set:Nn \l_@@_inclassign_number_int {-1} \tl_clear:N \l_@@_inclassign_title_tl \tl_clear:N \l_@@_inclassign_type_tl \tl_clear:N \l_@@_inclassign_given_tl \tl_clear:N \l_@@_inclassign_due_tl \str_clear:N \l_@@_inclassign_mhrepos_str \keys_set:nn { hwexam / inclassignment }{ #1 } } \_@@_inclassignment_args:n {} \newcommand\inputassignment[2][]{ \_@@_inclassignment_args:n { #1 } \str_if_empty:NTF \l_@@_inclassign_mhrepos_str { \input{#2} }{ \stex_in_repository:nn{\l_@@_inclassign_mhrepos_str}{ \input{\mhpath{\l_@@_inclassign_mhrepos_str}{#2}} } } \_@@_inclassignment_args:n {} } \newcommand\includeassignment[2][]{ \newpage \inputassignment[#1]{#2} } % \end{macrocode} % \end{macro} % % \subsection{Typesetting Exams} % % \begin{macro}{\quizheading} % \begin{macrocode} \ExplSyntaxOff \newcommand\quizheading[1]{% \def\@tas{#1}% \large\noindent NAME: \hspace{8cm} MAILBOX:\\[2ex]% \ifx\@tas\@empty\else% \noindent TA:~\@for\@I:=\@tas\do{{\Large$\Box$}\@I\hspace*{1em}}\\[2ex]% \fi% } \ExplSyntaxOn % \end{macrocode} % \end{macro} % % \begin{macro}{\testheading} % \begin{macrocode} \def\hwexamheader{\input{hwexam-default.header}} \def\hwexamminutes{ \tl_if_empty:NTF \testheading@duration { {\testheading@min}~\hwexam@minutes@kw }{ \testheading@duration } } \keys_define:nn { hwexam / testheading } { min .tl_set:N = \testheading@min, duration .tl_set:N = \testheading@duration, reqpts .tl_set:N = \testheading@reqpts, tools .tl_set:N = \testheading@tools } \cs_new_protected:Nn \_@@_testheading_args:n { \tl_clear:N \testheading@min \tl_clear:N \testheading@duration \tl_clear:N \testheading@reqpts \tl_clear:N \testheading@tools \keys_set:nn { hwexam / testheading }{ #1 } } \newenvironment{testheading}[1][]{ \_@@_testheading_args:n{ #1 } \newcount\check@time\check@time=\testheading@min \advance\check@time by -\theassignment@totalmin \newif\if@bonuspoints \tl_if_empty:NTF \testheading@reqpts { \@bonuspointsfalse }{ \newcount\bonus@pts \bonus@pts=\theassignment@totalpts \advance\bonus@pts by -\testheading@reqpts \edef\bonus@pts{\the\bonus@pts} \@bonuspointstrue } \edef\check@time{\the\check@time} \makeatletter\hwexamheader\makeatother }{ \newpage } % \end{macrocode} % \end{macro} % % \begin{macro}{\@problem} % This macro acts on a problem's record in the |*.aux| file. Here we redefine it (it was % defined to do nothing in |problem.sty|) to generate the correction table. % \begin{macrocode} %<@@=problems> \renewcommand\@problem[3]{ \stepcounter{assignment@probs} \def\@@pts{#2} \ifx\@@pts\@empty\else \addtocounter{assignment@totalpts}{#2} \fi \def\@@min{#3}\ifx\@@min\@empty\else\addtocounter{assignment@totalmin}{#3}\fi \xdef\correction@probs{\correction@probs & #1}% \xdef\correction@pts{\correction@pts & #2} \xdef\correction@reached{\correction@reached &} } %<@@=hwexam> % \end{macrocode} % \end{macro} % % \begin{macro}{\correction@table} % This macro generates the correction table % \begin{macrocode} \newcounter{assignment@probs} \newcounter{assignment@totalpts} \newcounter{assignment@totalmin} \def\correction@probs{\correction@probs@kw} \def\correction@pts{\correction@pts@kw} \def\correction@reached{\correction@reached@kw} \stepcounter{assignment@probs} \newcommand\correction@table{ \resizebox{\textwidth}{!}{% \begin{tabular}{|l|*{\theassignment@probs}{c|}|l|}\hline% &\multicolumn{\theassignment@probs}{c||}%| {\footnotesize\correction@forgrading@kw} &\\\hline \correction@probs & \correction@sum@kw & \correction@grade@kw\\\hline \correction@pts &\theassignment@totalpts & \\\hline \correction@reached & & \\[.7cm]\hline \end{tabular}}} % % \end{macrocode} % \end{macro} % % \subsection{Leftovers} % % at some point, we may want to reactivate the logos font, then we use % \begin{verbatim} % here we define the logos that characterize the assignment % \font\bierfont=../assignments/bierglas % \font\denkerfont=../assignments/denker % \font\uhrfont=../assignments/uhr % \font\warnschildfont=../assignments/achtung % % \newcommand\bierglas{{\bierfont\char65}} % \newcommand\denker{{\denkerfont\char65}} % \newcommand\uhr{{\uhrfont\char65}} % \newcommand\warnschild{{\warnschildfont\char 65}} % \newcommand\hardA{\warnschild} % \newcommand\longA{\uhr} % \newcommand\thinkA{\denker} % \newcommand\discussA{\bierglas} % \end{verbatim} % \end{implementation} % \ifinfulldoc\else\printbibliography\fi \endinput % \iffalse %%% Local Variables: %%% mode: doctex %%% TeX-master: t %%% End: % \fi % LocalWords: texttt scsys sc latexml fileversion filedate maketitle setcounter newpage % LocalWords: tocdepth tableofcontents pts showmeta showmeta showignores omdoc extrefs % LocalWords: testspace testnewpage testemptypage testheading testheading reqpts reqpts % LocalWords: exfig makeatletter makeatother vspace hrule vspace vspace noindent textsf % LocalWords: includeassignment includeassignment HorIacJuc cscpnrr11 importmodule baz % LocalWords: includemhassignment includemhassignment importmhmodule foobar ldots sref % LocalWords: mhcurrentrepos mh-variants mh-variant compactenum printbibliography Cwd % LocalWords: langle rangle langle rangle ltxml.cls ltxml.sty respetively metakeys qw % LocalWords: cwd stex graphicx amssymb amstext amsmath newif iftest testfalse testtrue % LocalWords: ifsolutions solutionsfalse ifmultiple multiplefalse multipletrue keyval % LocalWords: ltxml assig srefaddidkey addmetakey ifx assignment@titleblock stepcounter % LocalWords: document@hwexamtype importmodules metasetkeys inclassig@title inclassig % LocalWords: inclassig@title inclassig@type inclassig@type inclassig@number xspace kv % LocalWords: inclassig@number inclassig@due inclassig@due inclassig@given ignorespaces % LocalWords: inclassig@given newenvironment currentsectionlevel OptionalKeyVals kvi % LocalWords: omgroup vals hwexamtype ednote textbackslash newcommand inputassignment % LocalWords: unlist quizheading tas hspace hfill textbf newcount vfill addtocounter % LocalWords: theassignment@totalmin theassignment@totalpts assignment@probs xdef hline % LocalWords: assignment@totalpts assignment@totalmin correction@probs correction@probs % LocalWords: newcounter theassignment@probs footnotesize mh@currentrepos endinput % LocalWords: inclassig@mhrepos inclassig@mhrepos doctex inputmhassignment % LocalWords: GPL structuresharing STR iffalse cls NeedsTeXFormat hwexam hwexam.dtx sc