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.
exam
class author, showing grade tables by part is not possible.