1

I have been trying to do this for a while now and seem to be getting errors I don't really understand. This is a followup trying to use the suggestions here but I figured this followup was a bit too complex to add to that question so I'm opening a new one here.

I am using a very complicated package located here, but of special importance is the "ProblemSelection.sty" in there, which can be seen here.

I have included a set of "files" for workflow example at the end, but without downloading the package I doubt it would be compilable. If you decide to try, the files should work (in linux) although you may need to have some empty files depending on what else the \Master@Input@Test macro wants. A useful note; if you do get it to work with the actual package, \Verbosetrue triggers (a lot of) detail to write to the output to track the question selection and assignment process to see if there is a breakdown in the workflow somewhere, I put it in as error finding code as I went. Regardless I will try to explain the process here however.

In essence the problem selection package is designed to filter through a very large list of questions spread across a very large list of files and pick some at random based on some filtering properties (filter.sty). Then it saves the problems' content to custom macros that can be used later to display the problems. This is mostly accomplished using the "\latexProblemContent" command to wrap each problem and "\QuestionSelect" to find questions based on filters, shuffle them (\ChooseQuestions) around and assign commands (done inside the \latexProblemContent macro when it detects it's on the correct question), and finally we use \listQuestions to list all the problems selected so far. All of this is done using counters and \roman and \Roman to dynamically name (and later call) commands, and so far all of that works perfectly well.

However, at times it would be nice to be able to recompile an assignment without changing the problems (e.g., if we want to change the assignment name only). In the spirit of this, I asked my question (linked above) and was directed toward phantom labels to contain the content.

Ideally I'd make a label to hold the question contents when I assign it a macro name to be called later in \latexProblemContent. Then I can have a flag like \ifquestions@Static in order to flag if I want to keep the same problems. If I do, I pull problems from labels, if not, then I pull new random problems.

Unfortunately, try as I might, I seem to get all kinds of weird errors trying to do this, none of which make any sense to me. I'm guessing it has to do with some sort of expansion issue since the contents of the problems themselves (the stuff inside the \latexProblemContent") may not expand nicely when it's trying to be written out? Honestly I'm quite out of my depth as I've never dealt with writing out to files (aux or otherwise) to have persistent content. The \write command (and it's variations) seem straight forward but the errors I get are less than helpful most of the time.

To be clear, my goal is to have this work more-or-less the same but have the ability to pass an optional flag or some other item to \QuestionSelect to have it recall the last compiled list, and preferably have this persist until a new compile (without the static flag) replaces those questions. What I'm not sure how to do is set the label contents to be the (entire) contents of \latexProblemContent without forcing anything inside to expand, and moreover the label itself needs to have some expanded \roman and \Roman tokens which seem to be obnoxious, although I think I should be able to figure that out eventually with appropriate use of \expandafter.

Apologies for the ProblemSelect.sty file, it has grown rather organically and could definitely do with some cleanup, but I tried to document it fairly well as I wrote it so I (and others) would be able to figure out what everything was doing.

Sample workflow:

A file called "Series-Compute-0001.tex" saved in /users/local/etc/Series/ would look like:

\ProblemFileHeader{2}% Process how many problems are in this file and how to detect if it has a desirable problem
\ifproblemToFind% If it has a desirable problem search the file.
%%\tagged{Ans@ShortAns, Type@Compute, Topic@Series, Sub@Geometric, Sub@Convergence, File@0001}{
\latexProblemContent{
\begin{problem}
Determine if the series converges or diverges.  If it converges, find its sum. 

\[\sum_{n=0}^\infty {-2 \, \left(\frac{4}{9}\right)^{n}}\]

\begin{multipleChoice}
\choice{Diverges}
\choice[correct]{Converges}
\end{multipleChoice}


\begin{problem}

\[\sum_{n=0}^\infty {-2 \, \left(\frac{4}{9}\right)^{n}} = \answer{-\frac{18}{5}}\]
\end{problem}

\end{problem}}%}

\latexProblemContent{
\begin{problem}
Determine if the series converges or diverges.  If it converges, find its sum. 

\[\sum_{n=0}^\infty {7 \, \left(\frac{5}{8}\right)^{n}}\]

\begin{multipleChoice}
\choice{Diverges}
\choice[correct]{Converges}
\end{multipleChoice}


\begin{problem}

\[\sum_{n=0}^\infty {7 \, \left(\frac{5}{8}\right)^{n}} = \answer{\frac{56}{3}}\]

\end{problem}
\end{problem}}%}
\fi%    This ends the \ifproblemToFind conditional at the top of the problem file.
\ProblemFileFooter

Series-Input.tex in \users\local\etc would look like:

\tagged{ Topic@Series, Type@Compute, Ans@ShortAns, Sub@Convergence, Sub@Geometric, File@0001}{
    \select@Question{/users/local/etc/Series/Series-Compute-0001.tex}
}

The assignmentfile would look like:

\documentclass[]{ximera}

\usepackage{PackageLoader}% Included in the package linked above in the UF-Problem-Filter file.
\renewcommand{\QBank@loc}{/users/local/etc/}
\makeatletter
\begin{document}
\QuestionSelect{Topic@Series}{File@0001}{1}% Get the problem
\listQuestions% List the problems you found

\end{document}
  • You might want to have a look at the xsim package. You can also print random exercises from a collection there and it is pretty robust (Clemens knows what he's doing). – Henri Menke Sep 7 '17 at 23:58
  • Your question about the saving of labels etc. (which I have answered some days ago) is a very 'tiny' excerpt of what you have in mind. Tasks like this require much refined methods and can't be attacked with a more general method like the one I have given there. – user31729 Sep 8 '17 at 6:47
0

After delving into Google much more thoroughly I determined how to do this and figured I would submit an answer in case anyone else stumbles across this for similar reasons.

The magic command I needed was the \unexpanded command, which takes an argument that can be quite long (like an entire problem with multiple commands) and writes it, as-is, to the external file.

Thus I added a \newwrite\ProblemList at the top of the package but (importantly) did not open the file at the top of the package. Then, in the \latexProblemContent command, when it finds the desired problem and assigns it a macro to be called later, I simultaneously write out the problem to the file using the command;

\immediate\write\ProblemList{\unexpanded{#1}}

The \immediate makes sure the problem gets written at the time it was defined, which avoids getting a bunch of duplicate problems at the end. The \unexpanded makes sure that the problem gets written verbatim without expansion which avoids errors.

Then, in the \QuestionSelect command (the user command that triggers everything) I have a conditional to check to see if I am pulling static problems or not. So the beginning of the command looks like;

\ifquestion@Static% Check to see if we want to reuse the old problems.
    \input{ProblemList}
\else
    \immediate\openout\ProblemList=ProblemList.tex
    (Rest of command content)
\fi

Here, if the question@Static flag is active, then I simply input the (presumably) already made problem list. Otherwise, I then open the file ProblemList.tex for writing, which immediately overwrites whatever is there with a blank file which then gets populated by more questions.

As a few notes relevant to use cases I'm not worrying about currently but may matter for others trying to do something similar:

  1. The problem with opening the file to write when I do is that, if multiple \QuestionSelects are called, then the last one will overwrite all the previous ones and you will just get the same group of questions repeatedly, not all your questions. (I will probably fix this with dynamically created write files. Alternatively it would be nice to be able to build the write file in such a way as to check if it is open already, and append with some kind of check to call the right block of questions at the right point).
  2. If you try to call static questions without having compiled first, it will likely error as no file exists to input (should be able to be fixed with a conditional somewhere)
  3. It is somewhat obvious but this only saves questions if you save the custom output file. If you have something that cleans up "extra files" for you, you'll likely delete your static list and this will do nothing productive.

If I (or anyone else) comes up with good solutions to the two use cases above I'll append the answer, but this is functionally doing what I was after currently.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.