I'm trying to create a custom points table for exams. Currently, the instructors in our department write these exams in MS Word, so to ease the growing pains, I'd like to emulate the old design as closely as possible.
I am aware that the exam package has a built in grade table, but it is a different format. Furthermore, for questions with subparts (e.g. 1a-1h), there's no way to directly access the point values of each (the exam package has \pointsofquestion{#}
, but it only refers to the top level).
Ideally, I'd like to be able to automatically compute the Multiple Choice and Free Response point totals and populate the "Points Possible" columns of the table. Automatically generating the table seems a little too far fetched (number of rows may vary), but I'm not opposed to a solution for that too.
Edit 1:
As mentioned in the comments, I've been hacking up the code posted in the referenced answer. At the moment, I'm able to grab all the labels for the question parts along with the point values to create the code for the table. Below is a MWE of getting the labels and point values. I've cut out the specific code that builds the table as I can figure that out that syntax fairly easily now.
\documentclass[addpoints]{exam}
\usepackage{xparse,xpatch}
% redefine \question command to be \myquest
\appto\questions{\let\examquest\question\let\question\myquest}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\ExplSyntaxOn
\tl_new:N \g_grade_list_tl % this is a grading list
\int_new:N \g_mcscore_int% this will be the multiple choice score
\int_new:N \g_frscore_int% this will be the free response score
\int_new:N \g_exscore_int% this will be the total exam score
%% Add question parts to grading list
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
% don't do anything special inside solutions
\if@insolution\exampart[#1]
\else\exampart[#1]
\int_gadd:Nn \g_frscore_int {#1}
\tl_gput_right:Nx \g_grade_list_tl {\arabic{question}\alph{partno},}
\tl_gput_right:No \g_grade_list_tl {#1,}
\fi
}
}
\NewDocumentCommand\prtGradeList{}{
\tl_use:N \g_grade_list_tl}
\ExplSyntaxOff
\begin{document}
\begin{questions}
\question
\begin{parts}
\part[1]
\part[2]
\end{parts}
\question
\begin{parts}
\part[4]
\part[2]
\end{parts}
\question
\begin{parts}
\part[2]
\part[4]
\part[4]
\part[1]
\end{parts}
\question[5]
\end{questions}
\prtGradeList{}
\end{document}
What I have been able to make is a document with several questions followed by a grading table and then display the values and points under the table. The values under the table are just for testing purposes and won't be in the final product. Currently the vertical line on the RHS of the table is missing, but that's only because I haven't finished each row.
Thus, what I think my next step is now is to find the length of the list of labels and values, divide it in "half" (if the number of questions/parts is odd, I'd like to make the RHS longer).
Finally, the icing on the cake would be if I could place this table on the first page of the document. I haven't figured out how to do that yet as I'm not sure if I should save the table to an external file or something else.
Edit 2:
After reading the expl3 documentation and experimenting, I'm trying to build the table using sequences:
\documentclass[addpoints]{exam}
\usepackage{xparse,xpatch,multirow}
\usepackage[table,xcdraw]{xcolor}
\definecolor{rowGray}{HTML}{EFEFEF}
\def\scantronPt{1} %% Scantron point
\def\numGradeCols{2}
% redefine \question command to be \myquest
\appto\questions{\let\examquest\question\let\question\myquest}
% redefine \part command to be \mypart
\appto\parts{\let\exampart\part\let\part\mypart}
\makeatletter
\ExplSyntaxOn
\tl_new:N \g_grade_table_tl% this will; become the new grade table
\seq_new:N \g_grade_seq % this is a grading sequence
\int_new:N \g_mcscore_int% this will be the multiple choice score
\int_new:N \g_frscore_int% this will be the free response score
\int_new:N \g_exscore_int% this will be the total exam score
%% Add question parts to grading sequence
\NewDocumentCommand\mypart{o}{
\IfNoValueTF{#1}{\exampart}{
\if@insolution\exampart[#1]
\else\exampart[#1]
\int_gadd:Nn \g_frscore_int {#1}
\seq_gput_right:Nx \g_grade_seq {\arabic{question}\alph{partno}}
\seq_gput_right:No \g_grade_seq {#1}
\fi
}
}
%% Add question to grading sequence
\NewDocumentCommand\myquest{o}{
\IfNoValueTF{#1}{\examquest}{
\if@insolution\examquest[#1]
\else\examquest[#1]
\int_gadd:Nn \g_frscore_int {#1}
\seq_gput_right:Nx \g_grade_seq {\arabic{question}}
\seq_gput_right:No \g_grade_seq {#1}
\fi
}
}
\NewDocumentCommand\GradeTable{}{% the new grade table
%\BuildGradeTable{}
\seq_gput_right:Nn \g_grade_seq {Scantron}
\seq_gput_right:Nx \g_grade_seq {\scantronPt}
\seq_new:N \g_gradeLeft_seq
%% Macro
\def\seqLen{\seq_count:N \g_grade_seq}
\def\seqLeftLen{\seq_count:N \g_gradeLeft_seq}
%% Grab sequence original length
\int_const:Nn \seqOrigLen \seqLen
%% Create two integer variables
\int_new:N \leftSideLen \int_new:N \rightSideLen
%% Compute length of left and right columns
\int_gset:Nn \leftSideLen {\int_eval:n {2*\int_div_truncate:nn \seqOrigLen {4}}}
\int_gset:Nn \rightSideLen {\int_eval:n {\seqOrigLen-\int_use:N \leftSideLen}}
%% Split sequence in two
\int_do_until:nNnn {\seqLen} = {\int_use:N \rightSideLen} {
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\seq_gpush:Nx \g_gradeLeft_seq \l_tmpa_tl
}
\seq_reverse:N \g_gradeLeft_seq
%% Displays sequences in terminal (debugging purposes)
%\seq_show:N \g_gradeLeft_seq
%\seq_show:N \g_grade_seq
%% Build Table
\int_do_until:nNnn {\seqLeftLen} = {0} {
\tl_gput_right:Nn \g_grade_table_tl {\hline}
\seq_gpop:NN \g_gradeLeft_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl & }
\seq_gpop:NN \g_gradeLeft_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl &&}
\seq_gpop:NN \g_grade_seq \l_tmpb_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpb_tl &}
\seq_gpop:NN \g_grade_seq \l_tmpb_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpb_tl & \\}
}
\seq_if_empty:NF \g_grade_seq {
\tl_gput_right:Nn \g_grade_table_tl {\hline \multicolumn{2}{r}{}& }
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl &}
\seq_gpop:NN \g_grade_seq \l_tmpa_tl
\tl_gput_right:No \g_grade_table_tl {\l_tmpa_tl & \\}
}
%\seq_show:N \g_gradeLeft_seq
%\seq_show:N \g_grade_seq
\tl_show:N \g_grade_table_tl
\renewcommand{\arraystretch}{1.7}
%\tl_gput_right:Nn \g_grade_table_tl {\hline Scantron&\scantronPt &&\\}
\int_gadd:Nn \g_frscore_int {\scantronPt }
\int_gadd:Nn \g_mcscore_int {\g_frscore_int}
\tl_gclear:N \g_grade_table_tl
\begin{center}
\begin{tabular}{|*{6}{c|}} %% This syntax repeats column types
\multicolumn{6}{c}{\textit{\textbf{For~instructor~or~teaching~assistant~use~only.}}}\\[5pt]\hline
\rowcolor{rowGray}
\multicolumn{1}{|r|}{\textbf{Question}} & \multicolumn{1}{r|}{\textbf{Points~Possible}} & \multicolumn{1}{r|}{\textbf{Points~Earned}} & \textbf{Question} & \textbf{Points~Possible} & \textbf{Points~Earned}\\ \hline
%\tl_use:N \g_grade_table_tl \hline
\multicolumn{2}{r}{} & \multicolumn{2}{|r|}{\textbf{Multiple~Choice}} & \int_use:N \g_mcscore_int & \\ \cline{3-6}
\multicolumn{2}{l}{} & \multicolumn{2}{|r|}{\textbf{Free~Response}} & \int_use:N \g_frscore_int &\\ \cline{3-6}
\multicolumn{2}{l}{} & \multicolumn{2}{|r|}{\textit{\textbf{Exam~Total}}} & \int_use:N \g_exscore_int &\\ \cline{3-6}
\end{tabular}
\end{center}
}
\ExplSyntaxOff
\makeatother
\begin{document}
\begin{questions}
\question
What if there were no air?
\begin{parts}
\part[1]
Describe the effect on the balloon industry.
\part[2]
Describe the effect on the aircraft industry.
\end{parts}
\question
\begin{parts}
\part[4]
Define the universe.
Give three examples.
\part[2]
If the universe were to end, how would you know?
\end{parts}
\question
\begin{parts}
\part[2]
\part[4]
\part[4]
\part[1]
\part[1]
\end{parts}
\question[5]
%\question[1]
\end{questions}
\GradeTable{}
\end{document}
At this point, I can see that my \g_grade_table_tl
gives me what I want in the .log file, but when I run this code through pdflatex, it gets stuck at the \GradeTable{}
function.
Best Answer
As you want to put the scores for the question parts in two columns, and as the number of scores is variable, you will need to store the scores somewhere and then generate the whole table at the end. Building on my previous idea, I would put the scores into a LaTeX3 sequence, whilst keeping track of the total score and the number of scores as you go. As in my other post, I would then define a
\GradeBook
command to produce your custom table:I am not sure how "multiple choice" and "free response" questions are coded when using the exam class, which is why I asked for a minimal working example :), so in the code below I have cheated and hard-code these marks using:
Apart from this, everything is automatic. Here is the full code:
EDIT
Here is updated (and streamlined) version of the code above that saves the data to the aux file and reads it back in to construct the grade table. This allows you to put the table anywhere you like, including at the start of the document but also means that you will have to LaTeX the file twice before you see any scores.
After compiling the document two or more times, the updated MWE gives the following output:
This is much the same as before except that the table is now at the top of the document. If you compile the document only once then the grades table will have no scores for individuals questions, or their parts, and the total will be 0. Here is the updated code:
The code is probably simpler than before. The main changes are:
\myquestion
and a new command\__add_to_grades_list:nn
to add the question/part label and the score to tyhe grade list table\g_grades_aux_clist
After compiling the document once you will find the following lines in the aux file:
This the data that is used to construct the grade table.