Ho to hide the exam ID on the title page of a NOPS exam? - r-exams

I'm using exams2nops() from R/exams to prepare multiple variants of an exam (as part of an open-book-exam). I'd like to obscure the variant/group of exam a test-taker is assigned to (in order to prevent "team work" during the open book exam: "Hey guys, who else is in group 1 ?!").
By default, the exams2nops() function will print the exam ID automatically on the title page (in my case: 20112600001):
It appears there's no argument in the exams2nops() function to prevent the exam ID to be printed on the title page of the PDF exam. I am unsure where to adapt the underlying TeX template.
So my question is: How to suppress the exam ID on the title page of the PDF for NOPS exams?

You are correct that this is not possible in exams2nops(), the simple reason being that the ID is essential for automatically evaluating NOPS exams after scanning them. Thus, if you want to scan the NOPS PDF file you must not remove the ID. The standard strategy for making team work on the same ID impossible would be to simply generate a different random PDF with a different ID for every participant.
If you are not actually scanning the exam, then I would recommend using exams2pdf() rather than exams2nops() and simply "roll your own" LaTeX template. If you want to take inspiration from the NOPS template, then you can create one on the fly (here with 2 exercises) via:
make_nops_template(2, file = "mynops.tex")
Note, however, that this has quite a few options that can be controlled through appropriate header commands in exams2pdf(), e.g.,
exams2pdf(c("anova", "boxplots"),
template = "mynops.tex",
header = list(
nopsinstitution = "Sauer School of Statistics",
nopstitle = "Exam",
nopscourse = " (AWM)",
"newcommand{\\mylogo}" = ""
)
)
In addition to the elements above, one would usually specify Date, ID and the NOPS language comments (see ?nops_language). But rather than using the header argument for this I would recommend to edit mynops.tex "by hand" and hard-code all the relevant aspects, including omitting the ID.

Related

Using two templates with custom name in exams2pdf

I am using exams2pdf() to generate two PDF files as:
exams2pdf(file = "ICvar.Rmd",
name = "icvar",
engine = "knitr",
verbose = FALSE,
texdir = "tmptex",
template = c("exam", "solution")
)
But I get this error:
Error in base::file(out_tex[j], open = "w+", encoding = encoding) : invalid 'description' argument
Any ideas why?
Also, is it possible to use custom templates in exams2nops() like template = c("exam", "solution") to produce two PDF files, the first with the questions; the second with the solutions? I read the vignette but could not find any information and adding template to options in exams2nops() does nothing.
The problem is that you only provide a single name = "icvar" but actually need two distinct names for template = "exam" and template = "solution", respectively. Hence, lacking a second name lead to the somewhat cryptic error message. A simple solution is to provide a vector of two name = c("icex", "icsol"), say.
Additionally, I have just committed a fix to the development version on R-Forge that points this out more clearly in ?exams2pdf, throws an intelligible warning, and provides a workaround. If you use your code above, name is changed to name = c("icvar_exam", "icvar_solution") automatically.
As for exams2nops(): Internally this essentially sets up a standardized template via make_nops_template() and then calls exams2pdf(). No additional templates can be provided. The reason for this is that all the convenient options in the NOPS template (e.g., adding an intro, selecting the language, switching to twocolumn layout, etc.) would only work for the NOPS template but not the other templates provided. Therefore, if you want to produce a solution sheet you have to use another call to exams2pdf() (or exams2html() or exams2pandoc()) after setting the same random seeds as for exams2nops().

Looping variables in the parameters of the YAML header of an R Markdown file and automatically outputting a PDF for each variable

I am applying for junior data analyst positions and have come to the realization that I will be sending out a lot of cover letters.
To (somewhat) ease the pain and suffering that this will entail, I want to automate the parts of the cover letter that is suited for automation and will be using R Markdown to (hopefully) achieve this.
For the purposes of this question, let's say that the parts I am looking to automate is the position applied for and the company looking to hire someone for that position, to be used in the header of the cover letter.
These are the steps I envision in my mind's eye:
Gather the positions of interest and corresponding company in an Excel spreadsheet. This gives and Excel sheet with two columns with the variables position and company, respectively.
Read the Excel file into the R Markdown as a data frame/tibble (let's call this jobs).
Define two parameters in the YAML header of the .Rmd file to look something like this:
---
output: pdf_document
params:
position: jobs$position[i]
company: jobs$company[i]
---
The heading of the cover letter would then look something like this:
"Application for the position as r params$position at r params$company"
To summarize: In order to not have to change the values of the parameters manually for each cover letter, I would like to read an Excel file with the position titles and company names, loop these through the parameters in the YAML header, and then have R Markdown output a PDF for each pair of position and company (and ideally have the name of each PDF include the position title and company name for easier identification when sending the letters out). Is that possible? (Note: the title of the position and the company name does not necessarily have to be stored in an Excel file, that's just how I've collected them.)
Hopefully, the above makes clear what I am trying to achieve.
Any nudges in the right direction is greatly appreciated!
EDIT (11 July 2021):
I have partly arrived at an answer to this.
The trick is to define a function that includes the rmarkdown::render function. This function can then be included in a nested for-loop to produce the desired PDF files.
Again, assuming that I want to automate the position and the company, I defined the rendering function as follows (in a script separate from the "main" .Rmd file containing the text [named "loop_test.Rmd" here]):
render_function <- function(position, company){
rmarkdown::render(
# Name of the 'main' .Rmd file
'loop_test.Rmd',
# What should the output PDF files be called?
output_file = paste0(position, '-', company, '.pdf'),
# Define the parameters that are used in the 'main' .Rmd file
params = list(position = position, company = company),
evir = parent.frame()
)
}
Then, use the function in a for-loop:
for (position in positions$position) {
for (company in positions$company) {
render_function(position, company)
}
}
Where the Excel file containing the relevant positions is called positions with two variables called position and company.
I tested this method using 3 "observations" for a position and a company, respectively ("Company 1", "Company 2" and "Company 3" and "Position 1", "Position 2" and "Position 3"). One problem with the above method is that it produces 3^2 = 9 reports. For example, Position 1 is used in letters for Company 1, Company 2 and Company 3. I obviously only want to match outputs for Company 1 and Position 1. Does anyone have any idea on how to achieve this? This is quite unproblematic for two variables with only three observations, but my intent is to use several additional parameters. The number of companies (i.e. "observations") is, unfortunately, also highly likely to be quite numerous before I can end my search... With, say, 5-6 parameters and 20 companies, the number of reports output will obviously become ridiculous.
As said, I am almost there, but any nudges in the right direction for how to restrict the output to only "match" the company with the position would be highly appreciated.
You can iterate over by row like below.
for(i in 1:nrow(positions)) {
render_function(positions$position[i], positions$company[i])
}

How to have parameters in the LaTeX template?

I have a LaTeX template, which I use with exam2pdf and where I would like to have the date of the exam as a parameter, whose value should be passed from R. Is that possible?
Yes, you can do this in two and a half ways:
(1) Via the header argument
You can set exams2pdf(..., header = ..., template = ...) where the content of the header is inserted into the template by replacing the %% \exinput{header} placeholder. Thus, when writing the template you can decide where exactly the header code ends up in the LaTeX code and you can make sure that the appropriate commands/packages are available. The header can then be specified in the following ways:
LaTeX code:
You can include something like header = "\\command{value}". There could be more complex pieces of LaTeX code, involving multiple lines, etc.
List of commands and values:
Instead of the full LaTeX code it might be more R-like to use a list specification like header = list(command = "value"). This is transformed internally to the LaTeX code mentioned above.
List of functions:
Finally you can also have a specification like header = list(command = valuefun) where valuefun is a function(i) so that you return a different string for the i-th random version of the exam.
List of all of the above:
A list consisting of unnamed character strings, named character string, and named functions can be used as well, combining all three specifications above.
More details are provided in the vignette("exams", package = "exams") which explains the design of exams2pdf() and how it can be leveraged. It also includes some examples which you can also copy to your working directory via exams_skeleton(write = "exams2pdf", ...). You can look at the exam.tex LaTeX template that is shipped with the package to see how you can insert a date and an ID (depending on the i-th iteration) into the PDF. For example:
exams2pdf("capitals.Rmd", template = "exam.tex",
header = list(Date = "2022-02-22", ID = function(i) paste("\\#", i)))
(2) Write a template generator
For the purposes from your question strategy (1) should be sufficient, I guess. However, if you need more control over what is done in the LaTeX template, then my recommendation would be to write a dynamic template generator. This is how exams2nops() is set up. It takes a lot of arguments that can be set by the user and then proceeds as follows:
Depending on the user arguments a corresponding nops.tex template is written in a temporary directory.
Then exams2pdf(..., template = "/path/to/nops.tex") is called to use this custom temporary template.
Some details, especially the counter for the i-th ID is still handled through the header argument.

Is there a knitr strat for passing R content to LaTeX commands?

I'm creating a small R package that will allow a user to create exams with R code for tables and figures, multiple question types, randomly ordered questions, and randomly ordered responses on multiple choice items. Inserting R code into LaTeX is not problematic, but one issue I've run into is the need to "slap" together text ingested via R with LaTeX commands. Consider this example:
I have this in a LaTeX file:
\newcommand{\question}[1]{
\begin{minipage}{\linewidth}
\item
{#1}
\end{minipage}
}
I read the content with readr::read_file and store it in a variable. I then have the contents of the questions in a .json file:
...
{
"type" : "mc",
"section_no" : 1,
"points" : 2,
"question" : "What is the best beer?",
"correct_answer" : "Hamm's",
"lure_1" : "Miller Lite",
"lure_2" : "PBR",
"lure_3" : "Naturdays",
"lure_4" : "Leine's"
},
...
which I read with jsonlite::fromJSON (which converts to a dataframe), do some massaging, and store in a variable. Let's call the questions and their available options questions. What I've been doing is putting the necessary LaTeX content together with the character string manually with
question.tex <- paste0("\\question{", question[i], "\\\\")
to achieve this in the knitted .tex file:
\question{What is the best beer?\\A. PBR\\B. Naturdays\\C. Miller Lite\\D. Leine's\\E. Hamm's\\}
but I'm thinking there has to be a better way to do this. I'm looking for a function that will allow for a more seamless passing of arguments to my LaTeX command, something like knitr::magic_func(latex.command, question[i]) to achieve the result above. Does this exist?
Maybe I am asking for an extra level of abstraction that knitr doesn't have (or wasn't designed to have)? Or perhaps there's a better way? I guess at this point I'm not far away from being able to create a function that reads the LaTeX command name, number of arguments, and inserts text appropriately, but better to not reinvent the wheel! Also, I think this question could be generalized to simpler commands like \title, \documentclass, etc.
Small MWE:
## (more backslashes since we need to escape in R)
tex.command <- "\\newcommand{\\question}[1]{
\\begin{minipage}{\\linewidth}
\\item
{#1}
\\end{minipage}
}"
q <- "What is the best beer?\\\\A. PBR\\\\B. Naturdays\\\\C. Miller Lite\\\\D. Leine's\\\\E. Hamm's\\\\"
## some magic function here?
magic_func(tex.command, q)
## desired result
"\\question{What is the best beer?\\\\A. PBR\\\\B. Naturdays\\\\C. Miller Lite\\\\D. Leine's\\\\E. Hamm's\\\\\\\\"

Is it possible to to export from reporttools?

I am using tableNominal{reporttools} to produce frequency tables. The way I understand it, tableNominal() produces latex code which has to be copied and pasted onto a text file and then saved as .tex. But is it possible to simple export the table produced as can be done in print(xtable(table), file="path/outfile.tex"))?
You may be able to use either latex or latexTranslate from the "Hmisc" package for this purpose. If you have the necessary program infrastructure the output gets sent to your TeX engine. (You may be able to improve the level of our answers by adding specific examples.)
Looks like that function does not return a character vector, so you need to use a strategy to capture the output from cat(). Using the example in the help page:
capture.output( TN <- tableNominal(vars = vars, weights = weights, group = group,
cap = "Table of nominal variables.", lab = "tab: nominal") ,
file="outfile.tex")

Resources