Type: | Package |
Title: | Personalizes and Randomizes Exams Written in 'LaTeX' |
Version: | 1.2.7 |
Author: | Alejandro Gonzalez Recuenco |
Maintainer: | Alejandro Gonzalez Recuenco <alejandrogonzalezrecuenco@gmail.com> |
Description: | Randomizing exams with 'LaTeX'. If you can compile your main document with 'LaTeX', the program should be able to compile the randomized versions without much extra effort when creating the document. |
URL: | https://github.com/alexrecuenco/TexExamRandomizer, https://alexrecuenco.github.io/TexExamRandomizer/ |
BugReports: | https://github.com/alexrecuenco/TexExamRandomizer/issues |
Encoding: | UTF-8 |
LazyData: | true |
Imports: | Rcpp (≥ 0.12.13), assertthat, stringr, jsonlite, stats, utils |
Suggests: | optparse, knitr, rmarkdown, testthat |
License: | MIT + file LICENSE |
LinkingTo: | Rcpp |
RoxygenNote: | 7.3.0 |
ByteCompile: | true |
VignetteBuilder: | knitr |
NeedsCompilation: | yes |
Packaged: | 2024-01-23 01:58:58 UTC; alexrecuenco |
Repository: | CRAN |
Date/Publication: | 2024-01-23 08:22:50 UTC |
Generating Random Exams from 'LaTeX' documents
Description
This package is designed with exams and homework created in 'LaTeX' in mind. It allows to randomize and personalize exams and homework and it aids the user with grading them.
Details
If you are using the exam class from 'LaTeX' already, it is likely that this program works as it is.
If you just want to randomize your exams,
Look at
vignette("BasicUse", package = "TexExamRandomizer")
for an introduction of the concept behind this library and a quick way to start using it.Look at
vignette("ExamOptions", package = "TexExamRandomizer")
for a more detailed explanations of what options can be used on a document.
If instead you are trying to use the library to create your own randomizer for a certain use you might have, you should start by looking at CreateRandomExams
and GenerateHomework
.
Author(s)
Alejandro Gonzalez Recuenco
e-mail: alejandrogonzalezrecuenco@gmail.com
See Also
Useful links:
Report bugs at https://github.com/alexrecuenco/TexExamRandomizer/issues
Compile Document
Description
Function that takes a set of lines, x
, that represent a file or a document. And divides it in subsequent layers, structures as a list, as described on the detail section.
It assumes x
is representing a 'LaTeX' file that can be compiled as it is before we make any modifications.
Usage
CompileDocument(x, layersNames, layersCmd)
Arguments
x |
A character vector, each element represents one line of the latex document |
layersNames |
A character vector, with each element representating the environment name to be searched as |
layersCmd |
A character vector, with the same length as |
Details
Both layersNames
and layersCmd
must have the same length, since for each index, i
, layersNames[i]
and layersCmd[i]
refer to one layer of the tree structure of the document. Consequent layers must be found inside previous layers.
If it finds the structure of the document to not be completed, it will throw an error.
Value
It returns a list, with each element having a name. Recreating the tree structure identified by layersNames
and layersCmd
in the text file x
.
It first divides the document into two lists:
- preamble
Contains a character vector identifying everything before the \begin{document}
- document
Contains the tree structure identifying the document
Now, the naming convention for each layer of the document is as follows. We will use the convention <layerName>
, <layerCmd>
.
Note the convention first, everything that it finds prior to the first environment, it throws it into a character vector that it calls prior_to_<layesName>
.
After the first environment <layerName>
ends, it assumes that everything from that \end{<layerName>}
onwards corresponding to the next environment, and it will throw it to the prior part of that one.
post_to_<layerName>
prior_to_layersName
Includes everything up to the first
\begin{<layerName>
without including that line1_<layerName>_begin_<layerName>
-
Includes the
\begin{layerName}
for the 1st section, and everything until it finds the first\<layerCmd>
1_<layerName>_1_<layerCmd>
-
Includes everything from the 1
^{st}
\<layerCmd>
until the second\<layerCmd>
, without including the line in which the second command is found 1_<layerName>_2_<layerCmd>
-
Same thing... and it keeps going until the last
\<layerCmd>
is found 1_<layerName>_end_<layerName>
-
It includes the
\end{<layerName>}
for the 1st section. - ...
-
It then repeats the same structure for the next environment, changing the naming convention to start with 2_<...> and so on until it does the last environemt
post_to_<layerName>
-
After the last layer ends with
\end{layerName}
, it throws the rest of the lines into this last character vector
This structure is applied recursively to each i_<layerName>_j_<layerCmd>
of the previous layer to find the structure for the next layer.
The result is a tree of lists, with names that identify the whole structure, and the ending node of each branch is always a character vector
IMPORTANT NOTE: Note that this function only rearranges the lines of the document, it can't split a document between a line. So if you want to make sure something always stays together, put them both in the same line. This is intentional, to force a more clear structure on the document that will be parsed
In Summary, the sketch of the tree structure would be:
preamble
Document
prior_to_LayerName[1]
1_layerName[1]_begin_layerName[1]
1_layerName[1]_1_layerCmd[1]
prior_to_LayerName[2]
1_layerName[2]_begin_layerName[2]
1_layerName[2]_1_layerCmd[2]
Continues...
1_layerName[2]_2_layerCmd[2]
Continues...
...
post_to_layerName[2]
2_layerName[1]_begin_layerName[1]
2_layerName[1]_1_layerCmd[1]
...
...
n_layerName[1]_end_layerName[1]
post_to_layerName[1]
If a \<layerCmd>
is not found inside an environment, everything inside that environment is thrown into the begin_layerName part and instead of the numbered environments, an empty character list is added in the middle, with name empty_<layerCmd>
section.
See Also
FindStructure for more information on the details of how the layers are found.
Other Structuring Document:
DivideFile()
,
FindStructure
,
IsWellSectioned()
,
StructureDocument()
Compiling function
Description
This function calls latexmk, which must be part of the system commands, a directory where tex files are found and outputs their pdf and other things in the pdf.dir.out
The functions CompileLatexDirEXAM
and CompileLatexDirHW
are identical wrappers of the same function, CompileLatexDir
. Do not use them, they are just kept for "backwards" compatibility
Usage
CompileLatexDir(
pdf.dir.out,
latex.dir.in,
engine = "xelatex",
compile.dir = NULL,
extracmdoptions = NULL
)
CompileLatexDirEXAM(
pdf.dir.out,
latex.dir.in,
engine = "xelatex",
compile.dir = NULL,
extracmdoptions = NULL
)
CompileLatexDirHW(
pdf.dir.out,
latex.dir.in,
engine = "xelatex",
compile.dir = NULL,
extracmdoptions = NULL
)
Arguments
pdf.dir.out |
Directory where the pdf output will be sent to |
latex.dir.in |
Directory where all the tex files are found. |
engine |
Engine to use when compiling. Currently the options are
|
compile.dir |
Directory from which compilation is invoked, if not specified, it defaults to the latex.dir.in. |
Details
Write the tex files relative paths to other files as to be read from the directory in which latex.dir.in is found This function is intended to be use to compile a bunch of files which are stemmed from an original one. That is why the directory
Value
None
Author(s)
Alejandro Recuenco alejandrogonzalezrecuenco@gmail.com
Examples
input_folder <- system.file(
"extdata",
"ExampleTexDocuments",
package = "TexExamRandomizer")
TexExamRandomizer::CompileLatexDir(
pdf.dir.out = tempdir(),
engine= "pdf",
latex.dir.in = input_folder,
extracmdoptions = "-C")
# The "-C" option makes sure we simulate we use the command, but the command
# doesnt run compiling the document, it only tries to clean up
ConstructAnswerSheet
Description
Constructs an answer sheet given a document as generated by StructureDocument
by finding in the items the correct and wrong tags and describing where it found them.
Note that you must provide the document part only, StructureDocument
gives back a $preamble
and $document
.
If wrongTag
is left NULL
, the answer sheet only shows information of the correct answers.
This answer sheet provides information for what answers are correct or incorrect, as well as their position within the original document, before any shuffling was done. (It uses the names of the document to decide whether the document was shuffled or not, since subsetting a list removes all attributes except for the names, this is the "safest" way to do it)
The intent of this function is to make it easy to find the answers for a randomized version of an exam.
Usage
ConstructAnswerSheet(Document, correctTag, wrongTag = NULL)
Arguments
Document |
Document, as defined in |
correctTag |
Tag to identify the correct items. |
wrongTag |
Tag that identifies the wrong items. |
Details
The tags are just command of the type "\Tag
" that must be found somewhere that is not commented out inside the last item at the end of the tree structure. Usually you will want to use the tags that already identify the document items for this.
(For example, in the exam class, the tags \choice
and \CorrectChoice
could be used naturally, without having to introduce extra commands in the document)
Value
Data Frame. With the following columns
- index
Just an index running from 1 to
n
, wheren
is the numbe of rows- For each layer of depth in the document:
-
Four columns,
<name of section>_original
Contains an integer identifying the numbering of this section in the original layer, as identified by the naming convention
<name of section command>_original
Contains an integer identifying the numbering of this item in the original section, as identified by the naming convention
<name of section>
Contains an integer identifying the numbering of this section in the current layer, as identified by the ordering of the document inputted on this function
<name of section command>
Contains an integer identifying the numbering of this item in the current section, as identified by the ordering of the document inputted on this function
- For the last layer of depth
-
5 columns if the wrongTag is not NULL, 4 columns otherwise,
<name of section>_original
Contains an integer identifying the numbering of this section in the original layer, as identified by the naming convention
<name of section command>_original
Contains an integer identifying the numbering of this item in the original section, as identified by the naming convention
<name of section>
Contains an integer identifying the numbering of this section in the current layer, as identified by the ordering of the document inputted on this function
<correctTag>
-
Contains an integer identifying the numbering of this item in the current section, , as identified by the ordering of the document inputted on this function
If the
correctTag
wasn't found in this item, it will showNA
instead. (This will only happen ifwrongTag
is notNULL
, since otherwise this elements are omitted) <wrongTag>
-
Contains an integer identifying the numbering of this item in the current section, as identified by the ordering of the document inputted on this function
If the
wrongTag
wasn't found in this item, it will showNA
instead. (This will only happen ifwrongTag
is notNULL
, since otherwise this elements are omitted)
See Also
FindExamAnswers
for the exact underlying messy algorithm that controls how the table is created.
Other Extracting information:
CountNumberOfSections()
,
FindExamAnswers()
,
GenerateShortAnswerSheet()
Examples
ConstructAnswerSheet(
TexExamRandomizer::testdoc$document,
"CorrectChoice",
"choice"
)
CountNumberOfSections
Description
It counts the number of subparts in each section and outputs the result as a table. It doesn't act recursively, it only does the outermost layer.
Usage
CountNumberOfSections(Document)
Arguments
Document |
Document, as defined in |
Details
The regular expression that defines this methods behaviour is the following
"^[^[:digit:]]*([[:digit:]]+)_([^[:digit:]]+)_([[:digit:]]+)_(.*)"
The replacement is simply "\1"
.
It tries to first find whether there exist attributes command and section that explain the command and section, before starting to use regexs on the names.
Value
A table, counting the number of "\cmdName
" items in which the document was divided when parsed for every begin-end section. It doesn't act resursively.
It will return a table with an integer that identifies the section, and a count, with how many items it found on that section. If it doesn't find any items or sections, it will return an empty table.
See Also
Other Extracting information:
ConstructAnswerSheet()
,
FindExamAnswers()
,
GenerateShortAnswerSheet()
CreateRandomExams
Description
This function creates a series of randomized exams from a tex document and personalizes the information from a table (if a table is given) and a series of command names where thae information shoudl be replaced.
Usage
CreateRandomExams(
x,
layersNames = c("questions", "choices"),
layersCmd = c("question", "(choice|CorrectChoice)"),
outputBaseName,
outputDirectory,
cmdReorder = rep_len(TRUE, length(layersNames)),
sectionReorder = FALSE,
infoTable = NULL,
colNames = NULL,
cmdNames = NULL,
nOutputVersions = nrow(infoTable),
nOutputQuestions = "max",
answerSheetCorrectTag = NULL,
answerSheetWrongTag = NULL,
optionList = NULL
)
Arguments
x |
A character vector, each element represents one line of the latex document |
layersNames |
A character vector, with each element representating the environment name to be searched as |
layersCmd |
A character vector, with the same length as |
outputBaseName |
String, The basename for the output files. |
outputDirectory |
String, The output directory. |
cmdReorder , sectionReorder |
Logical vector, the length of |
infoTable |
Table with information, if NULL, no information is added to the exams |
colNames |
Character vector, Column names from the It first tries to find the column names literally, if ti couldn't find them like that, it will try to use them as a regular expression to find a column that matches the column. |
cmdNames |
Character vector, Names of the commands on the tex file, |
nOutputVersions |
Number of different random versions of the exam to be outputted |
nOutputQuestions |
Number of "questions" on the output exams. If the input is a scalar, the program will decide how to more evenly split the questions between all the sections, otherwise one can directly provide an integer vector specifying how many questions from each section are needed. (this only searches the "items" of the outermost layer) |
answerSheetCorrectTag , answerSheetWrongTag |
If the tags are not given, the output answersheet will be |
optionList |
Instead of writing the options on the function. Options could be given to optionList, and it will add those options. As long as the names are correct |
Details
All the output exams are named with outputBaseName
followed by 00i identifying the number of the exam (The number of zeros is the minimum that allows for all the exams to have a different number) and "_Version_"
followed by the version number of the exam and ".tex
". That is:
<outputDirectory>/<outputBaseName>00i_Version_j.tex
The number of exams outputted will always be the same as the number of versions if no table is given. However, if a table is added as input. It will create one exam for each row of the table, and it will try to divide as evenly as possible how to give the versions between the different rows. (Having one exam for each row, which will probably represent a student)
Value
A list that contains
outputDirectory
The output directory
outputFiles
A character vector that contains all the output names
FullAnswerSheet
The full answer sheet of all the exams.
Each answer sheet is created as described by
ConstructAnswerSheet
, and all the answer sheets are joined together with a version number in front as an added column to bind them all together. The original version has the number 0, all the output versions have sequential numbers as VersionThis wrapper function assumes equal depth on all branches of the tree structure, so that the number of columns is always identical in the answer sheet
See Also
ConstructAnswerSheet
, ReplaceFromTable
, RandomizeDocument
for extra details. . To see examples of how to use it, look at the code in jsonhwparser
DivideFile
Description
Function that takes a vector of text lines, x
, and divides it in preamble and document.
Usage
DivideFile(x)
Arguments
x |
A character vector, each element represents one line of the latex document |
Details
It ignores everything after the first end document command and it will throw and error if it finds more than one begin document command before that
Value
Returns a list with two character vectors:
- preamble
A character vector that includes every line of
x
up the begin document command- document
A character vector that includes every line from the begin document command to the first end document command
See Also
Other Structuring Document:
CompileDocument()
,
FindStructure
,
IsWellSectioned()
,
StructureDocument()
Examples
file <- system.file(
"extdata",
"ExampleTexDocuments",
"exam_testing_jsonparser.tex",
package = "TexExamRandomizer"
)
x <- readLines(file)
DivideFile(x)
FindExamAnswers
Description
It finds the answer for a certain document, given a correct and wrong tag. The output character vector is a collection of all those matches, with the text identifying the section and item that it was found inside the tree structure.
Usage
FindExamAnswers(Document, correctTag, wrongTag = NULL, OutputStartingName = "")
Arguments
Document |
Document, as defined in |
correctTag |
String, it should be the name (or regular expression) defining the tag that items that hold a correct answer will have. |
wrongTag |
String, leave as |
OutputStartingName |
Internal argument, (it should really be removed after testing that it really does nothing). In theory, this argument starts up the recursive search into the tree for matches. Since the output name will start with this string and a dash afterwards. |
Details
In the document, a correct or wrong item should be identified with a tag. Which shall be a latex command. "\correct
" "\wrong
" or whichever. It must be placed somewhere that is not commented-out. (Similar to how the exam class uses the \CorrectChoice
command to identify a correct answer, instead of using a \choice
, both tags could be used for this class).
(This is internally used by ConstructAnswerSheet
to construct that DF of answers by then parsing the vector output from this function and getting that way the output)
If wrongTag
is not null it also provides the information of where a wrong tag is found.
Each output character vectors has a name that identifies all the information necessary to understand where the match was found, relative to both the original document, and the current document we are analizing even if the order of the current document is different.
The name of each element starts with <OutputStartingName>
. After that, for each layer that it digs into, it pastes the following name on the right of the name that it already has:
_<originalname>_<addedname>
-
Where
<originalname>
is the name that identifies that list at that depth as the naming convention described inStructureDocument
defines. It identifies therefore the section and subsection refering to the original document. The string"_original"
is added at the end of the name of the environment and the name of the command name to differentitate it. -
And where
<addedname>
is the name that identifies that list at that depth as the naming convention described inStructureDocument
defines. However, this name has section and item numbers refering to the current ordering of the document, not the original ordering.
In the last layer, when it finally find the correct or wrong tag. It modifies the <addedname>
that should look like i_secName_j_cmdName
and it replaces cmdName with the correct or wrong tag respectively.
Therefore, each element is a pretty long string identifying all the layers that it took to traverse to get down to the answer. This function was basically used to prevent the use of attributes that bug out unexpectedly, since when passing functions and parsing things looses the attributes.
Value
Character vector. Each element identifies one match. The text of the element identifies where that match was found, in terms of the path walked on the tree that it took to get here. The naming convention is specified on details.
See Also
Other Extracting information:
ConstructAnswerSheet()
,
CountNumberOfSections()
,
GenerateShortAnswerSheet()
FindMatchingRow
Description
It outputs a logical vector identifying which rows in DF
have the same values as the values on rowtoMatch
. It ignores all columns that are not found on rowtoMatch
when doing the matching.
Usage
FindMatchingRow(rowtoMatch, DF)
Arguments
rowtoMatch |
One row of a data frame. If the length is longer, it won't output it. |
DF |
Dataframe in which we want to find the matches. It doesn't search that both the row of Df and rowtoMatch matches exactly, it only checks whether the columns that are found on rowtoMatch are found on DF with the same values (It decides which columns are "the same column" by looking at the names of the columns). It will throw an error if the names of the columns on |
Details
Note: It matches using the name of columns from rowtoMatch
, matching them to the names of the DF
.
It will output logical(0)
if the rowtoMatch
is null, or if is a data frame with 0 rows.
Value
Logical vector, with the same length as the number of rows in DF
. It outputs TRUE
if that row matched the rowtoMatch
, FALSE
otherwise
Structuring functions
Description
These internal functions provide functionality to find environment names and their use
Usage
FindBegin(x, cmdName)
FindEnd(x, cmdName)
FindCommand(x, cmdName)
Arguments
x |
string vector, each line should represent one line of a text file or a section fo a text file. |
cmdName |
Command to search for in |
Details
In FindBegin
and FindEnd
, cmdName
refers to the name of the command that would start an environment. Following the 'LaTeX' convention of "\begin{cmdName
}" or "\end{cmdName
}" respectively. However, it is not a full throrough check.
They will only be found by this class if they are only preceded by alphanumeric characters and spaces, this is to force the user to use begin and end environments at the start of a new line. TODO: CONSIDER AYBE CHANGING THIS THIS, IT IS EASY.
On the other hand, in the function FindCommand
. It finds the command "\cmdName
". And in this case it is less rescrittive, as long as the line is not commented, it will find it.
Make sure to not write slashes before the "\cmdName
", since you might bug the program if it thinks you wrote the command but you just wrote some slashes and then the command name afterwards.
All functions don't search for commands if the commands have been commented with the latex comment command in the same line... don't try to use multiple line comments on latex please...
IMPORTANT, instead of just writing something alphanumeric, these function actually use the cmdName
as a regular expression, which might be useful in many cases, but be careful with this.
TODO: Implement options for regular expressions
Value
Returns a numeric vector, indicating each occurrance of a start of a environment that looks like \begin{cmdName
}
Returns a numeric vector, indicating each occurrence of a start of a environment that looks like \end{cmdName
}
Returns a numeric vector, indicating each occurrence of the command \cmdName
found in the document.
See Also
Other Structuring Document:
CompileDocument()
,
DivideFile()
,
IsWellSectioned()
,
StructureDocument()
Generate Homework
Description
This function personalizes a 'LaTeX' document with data from a table,
generating a new file for each row which is saved on the outputDirectory
.
Usage
GenerateHomework(
x,
Table,
CommandNames,
ColumnNames,
outputDirectory,
outputBaseName
)
Arguments
x |
A character vector, each element represents one line of the latex document |
Table |
Data frame from which to extract the information |
CommandNames |
Character vector with the same length as |
ColumnNames |
Character vector with the names of the columns to be used |
outputDirectory |
The directory in which the output will be placed |
outputBaseName |
The starting name for the output files The files will look like
Where the number of zeros is the minimum number of zeros required to have a different version number for each file. (i.e., if there is only 45 files, it is 01-45; but with 132 files, it would be 001-132) |
Details
The command names should be 'LaTeX' commands that are being defined through
\newcommand{\<CommandNames[i]>}{<previous definition>}
The definition of these commands will be changed to be
\newcommand{\<CommandNames[i]>}{<Table[ColumnNames[i]][file #]>}
And it will output one file for each command.
The intent of this function was to populate information into a generic homework to personalize it for every student using 'LaTeX'. (It actually generalizes to maybe other problems).
Value
Character vector with the file names of the output.
See Also
ReplaceFromTable
to get a better idea of how the replacement is made. To see examples of how to use it, look at the code in jsonhwparser
Generating a short answer sheet
Description
Given a number of answer sheets generated by ConstructAnswerSheet
that have been binded together. And that have a column, versionColName
, that identifies each version. It collects all the answers together and places all the answers together for each exam.
Usage
GenerateShortAnswerSheet(
ExamSheet,
versionColName = "Version",
correctColName = "CorrectChoice"
)
Arguments
ExamSheet |
a exam sheet that contains all versions, similar to |
versionColName |
The name of the column in the original exam that contains the version number |
correctColName |
The name of the column that contains the last index for the correct tag, or NA if it is not a correct choice. |
Details
Note that if the version number is 0, it is ignored, since it understands that version 0 is the reference version.
If the document has more than two layers, keep in mind that it just shows the top most layer numbering and then the inner most number of the correct answers.
Note how this implies as well that an exam with more than one possible answer can not be simplified into a short answer sheet.
IMPORTANTLY, If a certain exam has less answers than other exams, the are just cited sequentially. Which may cause confusion. To clarify. This may happen if a certain question has more than one solution marked as "correct", or if a certain question has no solutions marked as correct. In that case, The short answer sheet just sequentially names all the correct answers, disregarding which questions they are referring to. (This is a very special case that will only come up in a real scenario if you are writing a short answer question in the middle of a multiple choice test. Or if you are writing some questions to have multiple correct answers, but only a few of them, and those questions are not included in all exams... (So evil))
Value
A data frame
Each row identifies one version of the answer sheet
the first column is the version number, the rest of the columns are the questions,
See Also
Other Extracting information:
ConstructAnswerSheet()
,
CountNumberOfSections()
,
FindExamAnswers()
Examples
csvfile <- system.file(
"extdata",
"ExampleTables",
"ExampleAnswerSheet.csv",
package = "TexExamRandomizer"
)
testASheet <- read.csv(
csvfile,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE
)
GenerateShortAnswerSheet(testASheet)
GetLayerSampleIndexes
Description
Function to randomize the names of a list to sample, as provided by StructureDocument
. It takes the names of the list, not the list itself, and it provides the indexes needed to resample the original list judging by those names. It doesn't output a resampled list.
Usage
GetLayerSampleIndexes(
section_vector,
command_vector,
sampleSectionOrder = FALSE,
randomizeSection = TRUE
)
Arguments
command_vector |
The names of each element on the list we want to sample, with the original order. The usual use would be Being specific, the names that are provided will be matched with the regular expression |
sampleSectionOrder |
Should it also move around the sections or not? Look at the details for a more detailed explanation |
randomizeSection |
If this is set to false, it will not randomize the list, it will just output |
Details
Following the prescription from StructureDocument
, it keeps the "prior_to" and "post_to" parts fixed. And within each section it keeps the "begin_" and "end_" parts fixed. Then, it resamples everything within each section, and afterwards resamples the order of the sections if sampleSectionOrder
is true
Note how the names of the list are expected to represent the structure described in StructureDocument
Value
An integer vector, that would provide a random reordering of the list.
GradeExams
Description
Grades an exam given a parsed list by WhichAnswerOriginal
Usage
GradeExams(
ExamAnswerParsedList,
name.ColCorrect,
name.ColIncorrect,
MaxOutputGrade = 100,
ExtraPoints = 0,
ExtraPointsForAll = 0
)
Arguments
ExamAnswerParsedList |
List parsed by |
name.ColCorrect , name.ColIncorrect |
The names of the correct and incorrect columns in each answer sheet of the |
MaxOutputGrade |
Maximum score that one should get if you get a perfect score, before couning the |
ExtraPoints |
Extra points to be added after scoring the exam. This points are added after the scaling is done with |
ExtraPointsForAll |
Scalar numeric value, extra points to be given to all student. |
Details
The score is first added on the base of the number of questions that are found on every parsed list.
If a question is removed from an exam, not all students may have that question as explained in the "Removing questions from the exam" section. If the total rows of a certain student list is n
, the score is
c / n * MaxOutputGrade
, where c
is the number of correct answers.
After that is done, the ExtraPoints
are added.
Value
It returns the StudentInfo
attribute of the parsed list adding the following columns to it
$addedPoints
Individual part of ExtraPoints
$addedAllPoints
Extra Points For All
$maxGrade
Max number of questions for the exam. (It would be different if when removing a question, some students didn't have a question in that exam)
$Grade
Number of correct answers that a student wrote in an exam
$Grade_Total_Exam
This is the
total_grade
as explained on the Extra Points section.
Extra Points
The structure of ExtraPoints
and the convention on how the score is calculated taking it into account is worth mentioning in it's own section.
The score is calculated as:
total_{grade} = (c + extra_{all}) / (maxn + extra_{all}) * MaxOutputGrade + extra_{individual}
Where
c
Number of correct questions
extra_all
Number of extra points for all.
This is thought of to be used as a question that you removed from the exam last minute, but that you want to actually count it as correct for every single student. I.e., a question that everyone got correct but it is not taken into consideration in the grading.
extra_individual
Number of extra points for that student.
max_n
Maximum number of questions in the students exam, which may differ from other students if you had to removed a bugged questions that not everyone had
MaxOutputGrade
The scaling to be done. This should be the maximum grade any student "should" get. (The individual extra points are added after the scaling is done)
Removing Questions from the exam
Note that if after creating the exam, you found that a question is bugged and can't be used to grade the exam, all you have to do is tell the student to answer "something" and you only have to remove it from the original/reference version in the Full Answer Sheet. When you apply the grading function, that question will then be ignored.
Notice how this creates output lists with different lengths in the case that two students didn't have that same question in their exam.
For example, if a exam has 15 questions out of a 50 question document. If student A has a bugged question and student B doesn't, the answer sheet produced for student A will have 14 rows while the one for student B will have 15 rows.
See Also
Other Grading Exams:
ObtainExamStats()
Examples
#First part coming from FindMatchingRow example
asheet_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleAnswerSheet.csv",
package = "TexExamRandomizer")
responses_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleResponses.csv",
package = "TexExamRandomizer")
FullAnswerSheet <-
read.csv(
asheet_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
Responses <- read.csv(
responses_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
compiledanswers <-
WhichAnswerOriginal(
StudentAnswers = Responses,
FullExamAnswerSheet = FullAnswerSheet,
names.StudentAnswerQCols = grep(
names(Responses),
pattern = "^Q.*[[:digit:]]",
value = TRUE),
names.StudentAnswerExamVersion = grep(
names(Responses),
pattern = "Version",
value = TRUE),
OriginalExamVersion = 0,
names.FullExamVersion = "Version",
names.FullExamOriginalCols = grep(
names(FullAnswerSheet),
pattern = "_original",
value = TRUE),
names.CorrectAndIncorrectCols = c(
"choice",
"CorrectChoice")
)
# Actual Code
ExtraPoints_individual <- runif(nrow(Responses), min = 1, max = 10)
ExtraPoints_forall <- 2
GradedStudentTable <-
GradeExams(
compiledanswers,
name.ColCorrect = "CorrectChoice",
name.ColIncorrect = "choice",
MaxOutputGrade = 100,
ExtraPoints = ExtraPoints_individual,
ExtraPointsForAll = ExtraPoints_forall
)
IsWellSectioned
Description
Function to assure a set of sections is well sectioned.
Usage
IsWellSectioned(u, v)
Arguments
u |
Vector, it assumes it is ordered in ascending ordered |
v |
Vector, it assumes it is ordered in ascending ordered |
Details
Basically it makes sure that, u[1]<v[1]<u[2]<v[2]
, etc
Value
Logical value, TRUE if it is well ordered, FALSE it is not
Author(s)
Alejandro Recuenco alejandrogonzalezrecuenco@gmail.com
See Also
Other Structuring Document:
CompileDocument()
,
DivideFile()
,
FindStructure
,
StructureDocument()
Obtaining exam statistics
Description
This function gets an answer sheet of the original version of the exam as a data frame, and a parsed list, which is obtained from GradeExams
and it outputs the statistics of how many answers are parsed exam, that is graded and obtains from there
Usage
ObtainExamStats(
OriginalExamAnswerSheet,
ExamAnswerParsedList,
names.FullExamOriginalCols
)
Arguments
OriginalExamAnswerSheet |
The answer sheet of the original exam. (In this package the convention is the exam version "0") |
ExamAnswerParsedList |
A parsed list for every student, as outputted by |
names.FullExamOriginalCols |
Names of those columns that in the answer sheet identify for all versions where that item is found on the original columns, (i.e., as ordered from the original version exam) |
Value
Returns the OriginalExamAnswerSheet
with a column added to it, named "ExamAnswerCount
" that counts the number of answers for each question
See Also
Other Grading Exams:
GradeExams()
Examples
asheet_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleAnswerSheet.csv",
package = "TexExamRandomizer")
responses_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleResponses.csv",
package = "TexExamRandomizer")
FullAnswerSheet <-
read.csv(
asheet_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
Responses <- read.csv(
responses_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
compiledanswers <-
WhichAnswerOriginal(
StudentAnswers = Responses,
FullExamAnswerSheet = FullAnswerSheet,
names.StudentAnswerQCols = grep(
names(Responses),
pattern = "^Q.*[[:digit:]]",
value = TRUE),
names.StudentAnswerExamVersion = grep(
names(Responses),
pattern = "Version",
value = TRUE),
OriginalExamVersion = 0,
names.FullExamVersion = "Version",
names.FullExamOriginalCols = grep(
names(FullAnswerSheet),
pattern = "_original",
value = TRUE),
names.CorrectAndIncorrectCols = c(
"choice",
"CorrectChoice")
)
OriginalAnswerSheet <- FullAnswerSheet[FullAnswerSheet$Version == 0,]
ExamStats <-
ObtainExamStats(
OriginalExamAnswerSheet = OriginalAnswerSheet,
ExamAnswerParsedList = compiledanswers,
names.FullExamOriginalCols = grep(
names(FullAnswerSheet),
pattern = "_original",
value = TRUE)
)
ParsePreambleForOptions
Description
This function parses a preamble of a document trying to read options handed to the package TexExamRandomizer to be used in compiling.
Usage
ParsePreambleForOptions(preamble)
Arguments
preamble |
character vector identifying the preamble from which to pass the JSON readon through |
Details
It find all %!TexExamRandomizer = {}
lines. It then uses the function fromJSON
to parse them, and it concatenates all those options.
If more than one option with the same name is given, it tries to concatenate those. However, it doesn't do that recursively, only if the names of the outer layer are the same... therefore, in nested structure you might end up with a list that have twice the same name. Keep in mind that in those cases, the default behaviour of R is to select the first one.
Value
Returns a list, that concatenates all the lists of options described on the file.
See Also
Other jsoncompiler:
compilation_options()
,
jsonexamparser()
,
jsonhwparser()
Randomizing documents.
Description
Function to randomize a Document, as created by StructureDocument
.
It Randomizes each layer according to the prescriptions involved in the internal function GetLayerSampleIndexes
. Which, in summary, randomizes each section inside, and then randomizes the orders of the sections.
Important note: One must provide to this function the document part of the structure. Since StructureDocument
provides as the outer most layer a split between the preamble and the document, one must just supply the document part to this function, (or a subsection of it).
Usage
RandomizeDocument(
Document,
isSectionReordered.vector,
isLayerRandomized.vector
)
Arguments
Document |
Document to randomize, as generated by |
isSectionReordered.vector |
Logical vector, specifying if the order of sections should be also randomized at a certain depth level. Note that if |
isLayerRandomized.vector |
Logical vector, specifying if you should randomize the order of the items, (denoted by This vector should have the same length as the depth at most, otherwise it will raise an error if you try to "dig deeper than it can". And isSectionReordered.vector should have matching elements for each element of isLayerRandomized.vector (Maybe we could change this to a warning instead? To allow for structures with different depths within different branches of the tree) |
Details
It keeps randomizing recursively inner layers of the structure until it runs out of elements on the logical vectors isSectionReordered.vector
and isLayerRandomized.vector
.
A "section" denotes the content within a begin-end environment in the document.
Each section is then assumed to be divided in a beginning and end parts, that should be fixed in place, and the parts denoted by the command \cmdName
as explained on StructureDocument
.
We will denote those parts as "items." Analogously to itemize environments in 'LaTeX'.
The purpose of this function is therefore to randomize the items from the structure, fixing the begin and end parts within a section. And then to reorder each section while keeping the pre- and post- parts fixed, and to do so recursively until we exhaust the isLayerRandomized.vector
isSectionReordered.vector
specifies whether to order sections for a certain depth, while isLayerRandomized.vector
specifies whether to order the items within a section of that same depth.
In some cases you may want to reorder the sections, for example, using the examdesign class. Over there, questions use the begin-end question format.
In others cases you may want to preserve the order of sections while still modifying the order of the items, like when you are using the exam class, or when creating your own list of questions with an \itemize
environment.
For efficiency, if you don't want to randomize to the full depth of your tree, just make those logical vectors of your desired length, rather than making them of length n
and then setting every layer after the last one you want to randomize to false. That will prevent the program from walking down the whole tree checking everything.
Value
A document structure, as provided by StructureDocument
.
However, the names of the structure will no longer be sequential, the naming convention in the new structure will refer to the original structure that was inputted into this function. Which is very useful when you want to keep track of where things have moved.
See Also
StructureDocument
, TODO: Add reference to extracting info functions
Examples
rndDoc <- RandomizeDocument(
TexExamRandomizer::testdoc$document,
c(FALSE,TRUE),
c(TRUE, TRUE)
)
ReplaceFromTable
Description
Given a 'LaTeX' file represented as a character vecotr with x
, it replaces from a table the commands given by commandNames
. for the values found on the table.
\newcommand{\commandName[i]}{table[tableRow, columnName[i]]}
.
Usage
ReplaceFromTable(x, table, tableRow, columnNames, commandNames)
Arguments
x |
A character vector, each element is suppose to represent a line |
table |
Data frame from which to extract the information |
tableRow |
Integer, row of the |
columnNames |
Character vector with the names of the columns to be used |
commandNames |
Character vector with the same length as |
Details
To do the replacement for each item, it uses the function ReplacePreambleCommand
. See the details in that function for more information.
Value
A character vector, representing the text x
, where all instances of
\newcommand\commandNames[i]{<random text>}
have been replaced with
\newcommand\commandNames[i]{table[tableRow, columnName[i]}
.
See Also
Other Preamble adjustment:
ReplacePreambleCommand()
Examples
custom_preambles <- list()
for (i in 1:nrow(TexExamRandomizer::testclass)) {
custom_preambles <-
c(
custom_preambles,
list(
TexExamRandomizer::ReplaceFromTable(
TexExamRandomizer::testdoc$preamble,
table = TexExamRandomizer::testclass,
tableRow = i,
columnNames = c("Class", "Roll.Number", "Nickname"),
commandNames = c("class", "rollnumber", "nickname")
)
)
)
}
ReplacePreambleCommand
Description
This functions gets a character vector in which each element represents a line of
a preamble of a 'LaTeX' document, and it replaces the definition of the command \commandName
to have the value commandValue
.
Usage
ReplacePreambleCommand(x, commandName, commandValue)
Arguments
x |
A character vector, each element is suppose to represent a line |
commandName |
A string identifying either the command name |
commandValue |
Replacement for the definition of commandName |
Details
It only modifies the value of the command by replacing instances of
\newcommand{\commandName}{<previous definition>}
with instances of
\newcommand{\commandName}{<commandValue>}
.
Keep in mind that both commandName
and commandValue
are placed directly inside a regex.
If you want to "hide" a certain definition of a command from being found and replaced by this function, simply define it by using \def
or \newcommand*
or a \renewcommand
when you define them.
Make sure you are using a one-line definition in commands that you want replaced, since this won't be able to detect commands that are defined in multiple lines in 'LaTeX'.
Also, note how certain invalid things in 'LaTeX' would still be matched by this regex, however you should find those errors before you start using this program since those errors would not allow you to compile the 'LaTeX' document on the first place.
Lastly, if it doesn't find a command on the document, it silently ignores it.
Value
A character vector, with the preamble, replacing all instances of
\newcommand\commandName{<random text>}
with
\newcommand\commandName{commandValue}
See Also
Other Preamble adjustment:
ReplaceFromTable()
Examples
new_preamble <- ReplacePreambleCommand( TexExamRandomizer::testdoc$preamble, "nickname", "Alex")
Structure Document
Description
Function that takes a character vector, x
, representing a 'LaTeX' file and it outputs a tree structure with the structure specified by layersNames
and layersCmd
.
It assumes x
is representing a 'LaTeX' file that can has been checked it compiles apropitaly before we make anymodification.
Note however that this function only moves lines around, it doesn't split a line in two.
Usage
StructureDocument(x, layersNames, layersCmd)
Arguments
x |
A character vector, each element represents one line of the latex document |
layersNames |
A character vector, with each element representating the environment name to be searched as |
layersCmd |
A character vector, with the same length as |
Details
Both layersNames
and layersCmd
must have the same length, since for each index, i
, layersNames[i]
and layersCmd[i]
refer to one layer of the tree structure of the document. Consequent layers must be found inside previous layers.
If it finds the structure of the document to not be completed, it will throw an error.
Value
It returns a list, with each element having a name. Recreating the tree structure identified by layersNames
and layersCmd
in the text file x
.
It first divides the document into two lists:
- preamble
Contains a character vector identifying everything before the \begin{document}
- document
Contains the tree structure identifying the document
Now, the naming convention for each layer of the document is as follows. We will use the convention <layerName>
, <layerCmd>
.
Note the convention first, everything that it finds prior to the first environment, it throws it into a character vector that it calls prior_to_<layesName>
.
After the first environment <layerName>
ends, it assumes that everything from that \end{<layerName>}
onwards corresponding to the next environment, and it will throw it to the prior part of that one.
post_to_<layerName>
prior_to_layersName
Includes everything up to the first
\begin{<layerName>
without including that line1_<layerName>_begin_<layerName>
-
Includes the
\begin{layerName}
for the 1st section, and everything until it finds the first\<layerCmd>
1_<layerName>_1_<layerCmd>
-
Includes everything from the 1
^{st}
\<layerCmd>
until the second\<layerCmd>
, without including the line in which the second command is found 1_<layerName>_2_<layerCmd>
-
Same thing... and it keeps going until the last
\<layerCmd>
is found 1_<layerName>_end_<layerName>
-
It includes the
\end{<layerName>}
for the 1st section. - ...
-
It then repeats the same structure for the next environment, changing the naming convention to start with 2_<...> and so on until it does the last environemt
post_to_<layerName>
-
After the last layer ends with
\end{layerName}
, it throws the rest of the lines into this last character vector
This structure is applied recursively to each i_<layerName>_j_<layerCmd>
of the previous layer to find the structure for the next layer.
The result is a tree of lists, with names that identify the whole structure, and the ending node of each branch is always a character vector
IMPORTANT NOTE: Note that this function only rearranges the lines of the document, it can't split a document between a line. So if you want to make sure something always stays together, put them both in the same line. This is intentional, to force a more clear structure on the document that will be parsed
In Summary, the sketch of the tree structure would be:
preamble
Document
prior_to_LayerName[1]
1_layerName[1]_begin_layerName[1]
1_layerName[1]_1_layerCmd[1]
prior_to_LayerName[2]
1_layerName[2]_begin_layerName[2]
1_layerName[2]_1_layerCmd[2]
Continues...
1_layerName[2]_2_layerCmd[2]
Continues...
...
post_to_layerName[2]
2_layerName[1]_begin_layerName[1]
2_layerName[1]_1_layerCmd[1]
...
...
n_layerName[1]_end_layerName[1]
post_to_layerName[1]
If a \<layerCmd>
is not found inside an environment, everything inside that environment is thrown into the begin_layerName part and instead of the numbered environments, an empty character list is added in the middle, with name empty_<layerCmd>
section.
See Also
FindStructure for more information on the details of how the layers are found.
Other Structuring Document:
CompileDocument()
,
DivideFile()
,
FindStructure
,
IsWellSectioned()
Examples
file <- system.file(
"extdata",
"ExampleTexDocuments",
"exam_testing_jsonparser.tex",
package = "TexExamRandomizer"
)
x <- readLines(file)
layersNames <- c("questions", "choices")
layersCmd <- c("question", "(choice|CorrectChoice)")
doc <- StructureDocument(x, layersNames, layersCmd)
Subsetting Document
Description
If you use a function such as A[vector], it only preserves the names attribute, it doesn't preserve any other attribute. This Function attempts to "fix" that manually, for lack of a better solution, instead of using S3 classes. Therefore, it can be used with other documents.
It subsets the attributes that have the same length as the Document itself and preserve all other attributes with length 0
Usage
SubsetWithAtributes(Document, vector)
Arguments
Document |
Document that we want to subset |
vector |
The vector that provides the subsetting, like |
Details
Although this function was created with documents in mind, it can work with any R array that holds attributes and has a subsetting `[`
function call.
WhichAnswerOriginal
Description
Given the answers of the students gathered in a table, and a full answer sheet of all versions (Including a "reference/original" version), it finds where those answers are found in the original exam, by copying from the original version the matching rows and binding them in order for every student. It then combines all of them in a list, and includes as well all the remaining student information in the attribute "StudentInfo".
It is intended as an internal function to generate the grades, and to identify in a very general way where the answers of the students are (relative to the reference/original version).
Usage
WhichAnswerOriginal(
StudentAnswers,
FullExamAnswerSheet,
OriginalExamVersion = 0,
names.FullExamVersion = "Version",
names.FullExamOriginalCols,
names.CorrectAndIncorrectCols,
names.StudentAnswerQCols,
names.StudentAnswerExamVersion
)
Arguments
StudentAnswers |
DataFrame, each row is a student, each column is some information about said student. Any column not included in |
FullExamAnswerSheet |
Answer sheet of all the exam versions, following the conventions of the |
OriginalExamVersion |
The version of the original exam, without randomization, as stored on the |
names.FullExamVersion |
The name of the column in which the version of the exam is stored on the |
names.FullExamOriginalCols |
The names of the columns that contain the information of the items relative to where they were positioned in the original ordering of the exam, before randomizing the exam. The convention from |
names.CorrectAndIncorrectCols |
It should be a character vector. The names of the columns in the |
names.StudentAnswerQCols |
The names in the |
names.StudentAnswerExamVersion |
The name of the column in the |
Details
The StudentAnswers
should be a data frame with one student answers represented by every row. The answers of the student to the exam should be ordered.
It is important that the colums named names.StudentAnswerQCols
should contain all their answers, if a student didn't answer a question leave a NA
or an invalid integer value as an answer, like 0, or a number larger than the number of answers to that question, so that is is found as out of bounds.
Value
It returns a list. Each element of the list is a dataframe, and there is one dataframe for each student in the StudentInfo
table provided.
All the columns that are not in the columns names.StudentAnswerQCols
are regarded as "StudentInfo
", and they are added to the attribute "StudentInfo
" of the output as a data frame.
- List elements:
-
They are outputted in order, that is to say, for
StudentAnswers[i,]
the list that provides the information for that row will beoutputlist[[i]]
.outputlist[[i]]
is a dataframe that identifies the rows that the student answered as they are found on the original/reference version. Therefore, if a student answeres a certain value, and that value is not reflected on the original version, it get's ignored. StudentInfo
attributeA dataframe containing all the student information that wasn't their answers.
Underlying algorithm
To identify the rows on the original exam it does the following:
It first finds their exam in the full answer sheet by their exam version.
After that, it removes from their exam the rows that identify the correct/incorrect choices.
By trying to match that row with a row on the reference exam it can tell where that quesiton is found on the original exam.
Then it identifies where that question is found on the original version, and it finds there which of the possible correct/incorrect choices is found.
If it didn't find any correct/incorrect choice matching the value given by the student, it marks it as out of bounds and replaces both correct and incorrect columns with
NA
.If it still doesn't find the row, it simply ignores it, and the output will have one less row.
Now you can tell how many questions the student answered correctly by looking at how many values are not NA in the correct choice column of the output list.
Removing Questions from the exam
Note that if after creating the exam, you found that a question is bugged and can't be used to grade the exam, all you have to do is tell the student to answer "something" and you only have to remove it from the original/reference version in the Full Answer Sheet. When you apply the grading function, that question will then be ignored.
Notice how this creates output lists with different lengths in the case that two students didn't have that same question in their exam.
For example, if a exam has 15 questions out of a 50 question document. If student A has a bugged question and student B doesn't, the answer sheet produced for student A will have 14 rows while the one for student B will have 15 rows.
Notes
Note1: Remember that in the original answer sheet there are two columns, one with correctchoice, another one with wrong choice. If the value is NA of one of those two columns it SHOULD NOT be NA on the other row.
Note2: The idea is that the data frames can be read to know the score of the student by counting the number of values that are not NAs on the correct choice column. (The numbers on the correct/incorrect columns themselves can be used for statistical purposes, to tell how many students answered each question).
Note3: The data frames can be used for many other statistical purposes very easily.
See Also
GradeExams
and ObtainExamStats
for examples on how to use the output of this function to obtain more detailed information.
Examples
asheet_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleAnswerSheet.csv",
package = "TexExamRandomizer")
responses_file <-
system.file(
"extdata",
"ExampleTables",
"ExampleResponses.csv",
package = "TexExamRandomizer")
FullAnswerSheet <-
read.csv(
asheet_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
Responses <- read.csv(
responses_file,
header = TRUE,
stringsAsFactors = FALSE,
na.strings = c("", "NA", "Na"),
strip.white = TRUE)
compiledanswers <-
WhichAnswerOriginal(
StudentAnswers = Responses,
FullExamAnswerSheet = FullAnswerSheet,
names.StudentAnswerQCols = grep(
names(Responses),
pattern = "^Q.*[[:digit:]]",
value = TRUE),
names.StudentAnswerExamVersion = grep(
names(Responses),
pattern = "Version",
value = TRUE),
OriginalExamVersion = 0,
names.FullExamVersion = "Version",
names.FullExamOriginalCols = grep(
names(FullAnswerSheet),
pattern = "_original",
value = TRUE),
names.CorrectAndIncorrectCols = c(
"choice",
"CorrectChoice")
)
nicknames <- attr(compiledanswers, "StudentInfo")$Nickname
for (i in 1:length(compiledanswers)) {
cat("Student\t", nicknames[i], " got\t",
sum(!is.na(compiledanswers[[i]]$CorrectChoice)),
" questions correctly\n", sep = "")
}
Output with listed documents
Description
Behaves like cat
, but it first automatically unlists the exam to print the document.
Since the document is kept as a tree of lists, it simply abstract the idea of outputting the document. with one document.
Usage
catDocument(FullDocument, sep = "\n", ...)
Arguments
FullDocument |
Document as structure by |
sep |
The separation character(s) between each line. |
... |
all extra arguments get passed along to the command " |
Examples
catDocument(TexExamRandomizer::testdoc)
Define compilations options
Description
This function provides the compilation options that can be passed to the jsonexamparser
Usage
compilation_options(
file = NULL,
table = NULL,
noutput = NULL,
nquestions = NULL,
seed = NULL,
compile = NULL,
xelatex = NULL,
debug = NULL
)
Arguments
file |
Input file name |
table |
Input table with student name and information |
noutput |
Number of *different* exams/homeworks produced |
nquestions |
Number of questions on each exam (Only on exams) |
seed |
Pseudorandom seed to be used (This allows the result to be deterministic) |
compile |
If TRUE, it tries to compile |
xelatex |
If TRUE, it uses 'XeLaTeX' |
debug |
If TRUE, it doesn't remove auxiliary files generated by 'LaTeX' when compiling |
Value
A list of options to be passed to jsonexamparser
, jsonhwparser
.
See Also
Other jsoncompiler:
ParsePreambleForOptions()
,
jsonexamparser()
,
jsonhwparser()
Examples
## Not run:
file <-
system.file(
"extdata",
"ExampleTexDocuments",
"exam_testing_nquestions.tex", #Test exam that doesn't require a table
package = "TexExamRandomizer")
temporalfile <- paste(tempfile(), ".tex", sep = "")
file.copy(file, temporalfile)
opt <- compilation_options(file = temporalfile)
jsonhwparser(opt)
## End(Not run)
Compile LatexDirectory
Description
Internal function wrapping the options to compile a directory holding some text files into pdf files.
Usage
compile_latex_directory(
options = list(),
input_directory,
output_directory = input_directory,
main_directory
)
Arguments
options |
See details |
input_directory |
Input directory where the tex files are found |
output_directory |
Output directory where the pdf files will be placed |
main_directory |
Directory we will compile from |
Details
'options' is a list containing the possible options. The possible options are
compile: Should it compile the tex files
debug: Should we use debugging information. Otherwise, it will clean up the temporary files created by latex.
xelatex: Should it use the xelatex engine
The options can be defined directly given by using the other parameters to this function. (Note that those paramenters have precedence)
Value
No return value
Apply function within a folder
Description
It executes the function fun
by first switching directories temporarily to the folder folder
and then returning to the working directory.
Usage
fun_from_folder(folder, fun, ...)
Arguments
folder |
The folder of execution that the function is switched to before executing |
fun |
Function to be executed from the relative path |
... |
Options to be passed to |
Value
The return value of fun(...)
Examples
list.files()
fun_from_folder(system.file("data", package = "TexExamRandomizer"), list.files)
list.files()
Json Exam Document Parser
Description
This function takes a series of options as obtained from parse_args
through the parameter opt
. The "examples" section provides all the options that it can parse.
From within those options, a --file
option is mandatory.
The file option provides a 'LaTeX' file name in which to search for lines on the preamble %!TexExamRandomizer
within the first 200 lines.
With those options that it finds through tags, it passes the function CreateRandomExams
.
Note that the tags must respect the JSON format, that is. It needs to be written within double quotes.
Usage
jsonexamparser(opt)
Arguments
opt |
Options as parsed from |
Details
All the options can be found on
vignette("ExamOptions", package = "TexExamRandomizer")
The options that are called "command line" options in the vignette are those that are given to the function through opt
, the rest of the options are read directly from the document specified with --file <filename>
See Also
Other jsoncompiler:
ParsePreambleForOptions()
,
compilation_options()
,
jsonhwparser()
Examples
## Not run:
#!/bin/Rscript
#This example showcases the type of script this jsonparser might be used on.
# You can still use it without a script,
# just by adding a list that has the same names as the list provided in opt
library(optparse)
option_list <- list(
make_option(
c("--file"),
action = "store",
default = NULL,
type = 'character',
help = "Filename of the Tex File"
),
make_option(
c("--table"),
action = "store",
default = NULL,
type = 'character',
help = "Filename of the table to break down. It overwrites the values written on the file"
),
make_option(
c("-n", "--noutput"),
action = "store",
default = NULL,
type = "integer",
help = "Number of output Versions"
),
make_option(
c("-q", "--nquestions"),
action = "store",
default = NULL,
type = "character",
help = "Number of output questions"
),
make_option(
c("-s", "--seed"),
action = "store",
default = NULL,
type = "integer",
help = "Seed for any randomization done"
),
make_option(
c("-c", "--compile"),
action = "store_true",
default = FALSE,
type = "logical",
help = "Should the output folder be compiled or not"
),
make_option(
c("--xelatex"),
action = "store_true",
default = FALSE,
type = "logical",
help = "Should we use xelatex to compile or not"
),
make_option(
c("-d", "--debug"),
action = "store_true",
default = FALSE,
type = "logical",
help = "If debugging, it doesn't remove auxiliary files"
)
)
#### PARSING OPTIONS ####
####
opt <-
parse_args(
OptionParser(option_list = option_list),
positional_arguments = TRUE
)
# Let's assume the file was the example file
testfile <-
system.file(
"extdata",
"ExampleTexDocuments",
"exam_testing_nquestions.tex", #Test exam that doesn't require a table
package = "TexExamRandomizer")
# To prevent modifying the file system in examples
temporalfile <- paste(tempfile(), ".tex", sep = "")
file.copy(testfile, temporalfile)
opt$options$file <- temporalfile
jsonexamparser(opt)
## End(Not run)
Json Homework Parser
Description
This function takes a series of options as obtained from parse_args
through the parameter opt
. The "examples" section provides all the options that it can parse.
From within those options, a --file
option is mandatory.
The file option provides a 'LaTeX' file name in which to search for lines on the preamble %!TexExamRandomizer
within the first 200 lines.
With those options that it finds through tags, it passes the function GenerateHomework
.
Note that the tags must respect the JSON format, that is. It needs to be written within double quotes.
Usage
jsonhwparser(opt)
Arguments
opt |
Options as parsed from |
Details
It acts similarly to link{jsonexamparser}
, but with the exception of not providing any randomiation option, it only provides the personalization options.
Look at vignette("ExamOptions", package = "TexExamRandomizer")
to see the details of the options that it accepts.
See Also
Other jsoncompiler:
ParsePreambleForOptions()
,
compilation_options()
,
jsonexamparser()
Examples
## Not run:
#!/bin/Rscript
#This example showcases the type of script this jsonparser might be used on.
# You can still use it without a script,
# just by adding a list that has the same names as the list provided in opt
library(optparse)
option_list <- list(
make_option(
c("--file"),
action = "store",
default = NULL,
type = 'character',
help = "Filename of the Tex File"
),
make_option(
c("--table"),
action = "store",
default = NULL,
type = 'character',
help = "Filename of the table to break down. It overwrites the values written on the file"
),
make_option(
c("-s", "--seed"),
action = "store",
default = NULL,
type = "integer",
help = "Seed for any randomization done"
),
make_option(
c("-c", "--compile"),
action = "store_true",
default = FALSE,
type = "logical",
help = "Should the output folder be compiled or not"
),
make_option(
c("--xelatex"),
action = "store_true",
default = FALSE,
type = "logical",
help = "Should we use xelatex to compile or not"
),
make_option(
c("-d", "--debug"),
action = "store_true",
default = FALSE,
type = "logical",
help = "If debugging, it doesn't remove auxiliary files"
)
)
#### PARSING OPTIONS ####
####
opt <-
parse_args(
OptionParser(option_list = option_list),
positional_arguments = TRUE
)
# Let's assume the file was the example file
testfile <-
system.file(
"extdata",
"ExampleTexDocuments",
"exam_testing_nquestions.tex", #Test exam that doesn't require a table
package = "TexExamRandomizer")
# To prevent modifying the file system in examples
temporalfile <- paste(tempfile(), ".tex", sep = "")
file.copy(testfile, temporalfile)
opt$options$file <- temporalfile
jsonhwparser(opt)
## End(Not run)
Sample class table
Description
Sample class for testing with five students. The variables stored for each student are as follows
Usage
testclass
Format
A dataframe with 5 rows and 4 columns
Details
Class
Roll.Number
Nickname
Name
Source
self
Test document
Description
A simple sample TeX document to test the package easily before deploying solutions.
Usage
testdoc
Format
A list with the format described in StructureDocument
Source
Created between me and my students in Suankularbwittayalai Rangsit School