Part 2: Custom Tags and Questions in Exam Class

examfor looploopsmacros

This is an expansion of the problem I posted, which @John Kormylo has provided a solution for.

https://tex.stackexchange.com/questions/640726/create-custom-tags-for-questions-in-exam-class

Here is worked solution from John.

\documentclass[addpoints]{exam}
\usepackage{tikz}% foreach and pgfmath

\newcommand{\tag}[1]{\expandafter\gdef\csname tagofq@\roman{question}\endcsname{#1}}

\makeatletter
\newcommand{\taglist}[1]{% #1 = comma delimmited list of tags (in order) for summary
\@ifundefined{exam@numquestions}{}{%
\bgroup% use local definitions
  \lineskip=0pt
  \noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Tag}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Question}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Points}}\newline
  \foreach \x in {#1} {\count1=0
    \let\qlist=\empty
    \edef\pointsum{0}%
    \loop\ifnum\count1<\exam@numquestions\relax
      \advance\count1 by 1
      \@ifundefined{tagofq@\@roman\count1}{}{%
        \edef\thistag{\csname tagofq@\@roman\count1\endcsname}%
        \ifx\x\thistag\relax
          \ifx\empty\qlist\relax
            \edef\qlist{\the\count1}%
          \else
            \edef\qlist{\qlist, \the\count1}%
          \fi
          \edef\thispoints{\csname pointsofq@\@roman\count1\endcsname}%
          \pgfmathparse{int(\pointsum+\thispoints)}%
          \let\pointsum=\pgfmathresult
        \fi}%
    \repeat
    \vskip-\fboxrule\noindent
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\x}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\qlist}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\pointsum}}\newline}%
  \vskip-\fboxrule\noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Total}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut\exam@numpoints}}
\egroup}}
\makeatother

\begin{document}
    
\begin{questions}
\question[1] \tag{Algebra}
This is Question 1. This is tagged against Algebra.

\question[2] \tag{Equations}
This is Question 2.This is tagged against Equations.

\question[2] \tag{Data}
This is Question 3.This is tagged against Data.

\question[1] \tag{Algebra}
This is Question 4. This is tagged against Algebra.

\question[2] \tag{Algebra}
This is Question 5.This is tagged against Algebra.

\question[2] \tag{Data}
This is Question 6.This is tagged against Data.

\end{questions}

\taglist{Algebra,Data,Equations}

\end{document}

I would like to modify John's code to include \parts.

Here is a sample .tex file.

\documentclass[addpoints]{exam}
\begin{document}
    
\begin{questions}
\question[1] \tag{Algebra}
This is Question 1. This is tagged against Algebra.

\question[]
\begin{parts}
\part[1] \tag{Equations} This is Question 2a.This is tagged against Equations.
\part[1] \tag{Algebra} This is Question 2b.This is tagged against Algebra.
\end{parts}

\question[2] \tag{Data}
This is Question 3.This is tagged against Data.

\question[] 
\begin{parts}
\part[1] \tag{Equations} This is Question 4a.This is tagged against Equations.
\part[1] \tag{Data} This is Question 4b.This is tagged against Data.
\end{parts}

\question[2] \tag{Algebra}
This is Question 5.This is tagged against Algebra.

\question[2] \tag{Data}
This is Question 6.This is tagged against Data.

\end{questions}

\taglist{Algebra,Data,Equations}
\end{document}

The desired result for the above code is below.

Results

Update 1:
To include \subparts and suppress \parts point values.

\documentclass[addpoints]{exam}
\usepackage{tikz}% foreach and pgfmath

\makeatletter
\let\normal@parts=\parts
\def\parts{\normal@parts
  \let\normal@part=\@doitem
  \let\@doitem=\my@part}
%
\newcommand{\my@part}[1][\@empty]{% store points by part
  \ifx\@empty#1
    \expandafter\normal@part
  \else
    \stepcounter{partno}%
    \immediate\write\@auxout{\string\gdef\string\pointsofp@\roman{question}@\roman{partno}{#1}}%
    \addtocounter{partno}{-1}%
    \expandafter\normal@part\expandafter[\expandafter#1\expandafter]%
  \fi}

\newcommand{\tag}[1]{\ifnum\@listdepth=1
    \expandafter\gdef\csname tagofq@\arabic{question}\endcsname{#1}%
  \else 
    \expandafter\gdef\csname tagofp@\arabic{question}@\arabic{partno}\endcsname{#1}%
  \fi}
\newcommand{\qlist}{}% reserve global name
\newcommand{\pointsum}{}

\newcommand{\addtoqlist}[1]{% #1 = \thequestion etc
  \ifx\empty\qlist\relax
    \xdef\qlist{#1}%
  \else
    \xdef\qlist{\qlist, #1}%
  \fi}
\newif{\ifpart}

\newcommand{\taglist}[1]{% #1 = comma delimmited list of tags (in order) for summary
\@ifundefined{exam@numquestions}{}{\par%
\bgroup% use local definitions
  \lineskip=0pt
  \noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Tag}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Question}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Points}}\newline
  \foreach \x in {#1} {\setcounter{question}{0}% loop over tags
    \global\let\qlist=\empty
    \xdef\pointsum{0}%
    \loop\ifnum\value{question}<\exam@numquestions\relax% loop over questions
      \stepcounter{question}%
      \@ifundefined{tagofq@\arabic{question}}{% no tag on question
        \setcounter{partno}{0}\parttrue
        \bgroup\loop% llop over parts
          \stepcounter{partno}%
          \@ifundefined{r@part@\arabic{question}@\arabic{partno}}{\partfalse}{}%
        \ifpart
          \edef\thistag{\csname tagofp@\arabic{question}@\arabic{partno}\endcsname}%
          \ifx\x\thistag\relax
            \addtoqlist{\thequestion\thepartno}%
            \edef\thispoints{\csname pointsofp@\roman{question}@\roman{partno}\endcsname}%
            \pgfmathparse{int(\pointsum+\thispoints)}%
            \global\let\pointsum=\pgfmathresult
          \fi
        \repeat
        \egroup
      }{% tag on question
        \edef\thistag{\csname tagofq@\arabic{question}\endcsname}%
        \ifx\x\thistag\relax
          \addtoqlist{\thequestion}%
          \edef\thispoints{\csname pointsofq@\roman{question}\endcsname}%
          \pgfmathparse{int(\pointsum+\thispoints)}%
          \global\let\pointsum=\pgfmathresult
        \fi}%
    \repeat
    \vskip-\fboxrule\noindent
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\x}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\qlist}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\pointsum}}\newline}%
  \vskip-\fboxrule\noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Total}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut\exam@numpoints}}
\egroup}}
\makeatother


\newcommand{\hidepoints}{%
    \pointsinmargin\pointformat{}
}

\newcommand{\showpoints}{%
    \nopointsinmargin\pointformat{(\thepoints)}
} 

\begin{document}
    
\begin{questions}
\question[1] \tag{Algebra}
This is Question 1. This is tagged against Algebra.

\question
\begin{parts}
\hidepoints
\part[2] \tag{Equations}  % I want to suppress and not include in total points this which is the sum of the subparts below.
\showpoints
\begin{subparts}
    \subpart[1]This is Question 2a (2ai).This is tagged against Equations.
    \subpart[1]This is Question 2a (2aii).This is tagged against Equations.
\end{subparts}
\part[1] \tag{Algebra} This is Question 2b.This is tagged against Algebra.
\end{parts}

\question[2] \tag{Data}
This is Question 3.This is tagged against Data.

\question
\begin{parts}
\part[1] \tag{Equations} This is Question 4a.This is tagged against Equations.
\part[1] \tag{Data} This is Question 4b.This is tagged against Data.
\end{parts}

\question[2] \tag{Algebra}
This is Question 5.This is tagged against Algebra.

\question[2] \tag{Data}
This is Question 6.This is tagged against Data.

\end{questions}

\taglist{Algebra,Data,Equations}
\end{document}

But this runs into the following issues when compiling gradetable.

Results with False Total Points

As you can see the total don't add up to the total tags.

Best Answer

This version loops over subparts as well. Questions etc. with no points are not included in \qlist. Question etc. with no tag inherit the tag of their parent. A \tag outside questions will create a default tag.

If all the subparts have the same tag, only the part will be added to list of questions.

\documentclass[addpoints]{exam}
\usepackage{tikz}% foreach and pgfmath
\usepackage{etoolbox}

\newif{\ifquestion}
\newif{\ifpart}
\newif{\ifsubpart}

\makeatletter
\def\@doitem{\@ifnextchar[{\@readpoints}%
                          {\def\@points{0}\item@points@pageinfo}%
}
\patchcmd{\item@points@pageinfo}{\item}{\item\savepoints}{}{FAILED}

\newcommand{\savepoints}{\ifcase\@listdepth
  \or \expandafter\xdef\csname qpoints@\arabic{question}\endcsname{\@points}% 1
  \or \expandafter\xdef\csname ppoints@\arabic{question}@\arabic{partno}\endcsname{\@points}% 2
  \or \expandafter\xdef\csname spoints@\arabic{question}@\arabic{partno}@\arabic{subpart}\endcsname{\@points}% 3
  \fi}

\newcommand{\tag}[1]{\ifcase\@listdepth \gdef\defaulttag{#1}% 0
  \or \expandafter\gdef\csname tagofq@\arabic{question}\endcsname{#1}% 1
  \or \expandafter\gdef\csname tagofp@\arabic{question}@\arabic{partno}\endcsname{#1}% 2
  \or \expandafter\gdef\csname tagofs@\arabic{question}@\arabic{partno}@\arabic{subpart}\endcsname{#1}% 3
  \fi}

\newcommand{\defaulttag}{}
\newcommand{\qlist}{}% reserve global names
\newcommand{\slist}{}
\newcommand{\pointsum}{}
\newcommand{\total@points}

\newcommand{\addtoqlist}[1]{% #1 = \thequestion etc
  \ifx\empty\qlist\relax
    \xdef\qlist{#1}%
  \else
    \xdef\qlist{\qlist, #1}%
  \fi}

\newcommand{\addtoslist}[1]{% #1 = \thequestion etc
  \ifx\empty\slist\relax
    \edef\slist{#1}%
  \else
    \edef\slist{\slist, #1}%
  \fi}

\newcommand{\loopoverquestions}{\setcounter{question}{0}%
  \questiontrue
  \loop
    \stepcounter{question}%
    \@ifundefined{r@question@\arabic{question}}{\questionfalse}%
      {\def\thisquestion{\ref{question@\arabic{question}}}}%
  \ifquestion
    \@ifundefined{qpoints@\arabic{question}}{\def\thispoints{0}}%
      {\def\thispoints{\csname qpoints@\arabic{question}\endcsname}}%
    \@ifundefined{tagofq@\arabic{question}}{\edef\qtag{\defaulttag}}%
      {\edef\qtag{\csname tagofq@\arabic{question}\endcsname}}%
    \ifnum\thispoints>0\relax
      \ifx\thetag\qtag\relax
        \addtoqlist{\thisquestion}%
        \pgfmathparse{int(\pointsum+\thispoints)}%
        \global\let\pointsum=\pgfmathresult
      \fi
    \fi
    \loopoverparts
  \repeat}
    
\newcommand{\loopoverparts}{\bgroup
  \setcounter{partno}{0}%
  \parttrue
  \loop
    \stepcounter{partno}%
    \@ifundefined{r@part@\arabic{question}@\arabic{partno}}{\partfalse}%
      {\def\thispartno{\ref{part@\arabic{question}@\arabic{partno}}}}%
  \ifpart
    \@ifundefined{ppoints@\arabic{question}@\arabic{partno}}{\def\thispoints{0}}%
      {\def\thispoints{\csname ppoints@\arabic{question}@\arabic{partno}\endcsname}}%
    \@ifundefined{tagofp@\arabic{question}@\arabic{partno}}{\edef\ptag{\qtag}}%
      {\edef\ptag{\csname tagofp@\arabic{question}@\arabic{partno}\endcsname}}%
    \ifnum\thispoints>0\relax
      \ifx\thetag\ptag\relax
        \addtoqlist{\thisquestion(\thispartno)}%
        \pgfmathparse{int(\pointsum+\thispoints)}%
        \global\let\pointsum=\pgfmathresult
      \fi
    \fi
    \loopoversubparts{\thispoints}%
  \repeat
\egroup}

\newcommand{\loopoversubparts}[1]% #1 = points for parent
{\bgroup
  \setcounter{subpart}{0}%
  \count1=0 %number of subparts
  \count2=0 %number of mathing tags
  \count3=#1\relax
  \let\slist=\empty
  \subparttrue
  \loop
    \stepcounter{subpart}%
    \@ifundefined{r@subpart@\arabic{question}@\arabic{partno}@\arabic{subpart}}{\subpartfalse}%
      {\def\thissubpart{\ref{subpart@\arabic{question}@\arabic{partno}@\arabic{subpart}}}}%
  \ifsubpart
    \advance\count1 by 1
    \@ifundefined{spoints@\arabic{question}@\arabic{partno}@\arabic{subpart}}{\def\thispoints{0}}%
      {\def\thispoints{\csname spoints@\arabic{question}@\arabic{partno}@\arabic{subpart}\endcsname}}%
    \@ifundefined{tagofs@\arabic{question}@\arabic{partno}@\arabic{subpart}}{\edef\stag{\ptag}}%
      {\edef\stag{\csname tagofs@\arabic{question}@\arabic{partno}@\arabic{subpart}\endcsname}}%
    \ifnum\thispoints>0\relax
      \ifx\thetag\stag\relax
        \advance\count2 by 1
        \addtoslist{\thisquestion(\thispartno)\thissubpart}%
        \pgfmathparse{int(\pointsum+\thispoints)}%
        \global\let\pointsum=\pgfmathresult
      \fi
    \fi
  \repeat
  \ifnum\count2>0
    \ifnum\count1=\count2
      \ifnum\count3=0
        \addtoqlist{\thisquestion(\thispartno)}%
      \fi
    \else
      \addtoqlist{\slist}%
    \fi
  \fi
\egroup}

\newcommand{\taglist}[1]{% #1 = comma delimmited list of tags (in order) for summary
\bgroup% use local definitions
  \par
  \lineskip=0pt
  \noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Tag}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Question}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Points}}\newline
  \def\total@points{0}%
  \foreach \thetag in {#1} {% loop over tags
    \global\let\qlist=\empty
    \gdef\pointsum{0}%
    \loopoverquestions
    \vskip-\fboxrule\noindent
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\thetag}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\qlist}}\hspace{-\fboxrule}%
    \fbox{\parbox[t][1cm][t]{4cm}{\strut\pointsum}}\newline
    \pgfmathparse{int(\total@points+\pointsum)}%
    \global\let\total@points=\pgfmathresult}%
  \vskip-\fboxrule\noindent
  \fbox{\parbox[t][1cm][t]{4cm}{\strut Total}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut}}\hspace{-\fboxrule}%
  \fbox{\parbox[t][1cm][t]{4cm}{\strut\total@points}}
\egroup}
\makeatother

\begin{document}
    
\begin{questions}
\question[1] \tag{Algebra}
This is Question 1. This is tagged against Algebra.

\question
\begin{parts}
\part[1] \tag{Equations} This is Question 2a.This is tagged against Equations.
\part[1] \tag{Algebra} This is Question 2b.This is tagged against Algebra.
\end{parts}

\question[2] \tag{Data}
This is Question 3.This is tagged against Data.

\question
\begin{parts}
\part[1] \tag{Equations} This is Question 4a.This is tagged against Equations.
\part[1] \tag{Data} This is Question 4b.This is tagged against Data.
\part \tag{Algebra}
\begin{subparts}
\subpart[1]
\subpart[1]
\end{subparts}
\end{parts}

\question[2] \tag{Algebra}
This is Question 5.This is tagged against Algebra.

\question[2] \tag{Data}
This is Question 6.This is tagged against Data.

\end{questions}

\taglist{Algebra,Data,Equations}
\end{document}