YAML To LMS and LATEX Processor
General purpose
We are often faced with creating quizzes, assigments, and online tests using tools such as canvas or even simply writing LaTeX files for distribution. Platforms such as blackboard make it possible to realize that objective but their format is unforgiving and does not allow for advanced formatting (including the insertion of math symbols). At the same time, we wish to keep those questions in a raw form so that they can be archived, edited, and reused. Here, I introduce the use of a standard yaml file for this purpose. The goal of yaml2lms is to convert a yaml file into a file usable for multiple-choice questions in LMS. The code also creates LaTeX and PDF files as needed.
In addition to creating neat files, this approach is a good way to archive questions in a flexible format and it also provides a spellchecking utility.
Warning
yaml2lms is a hack. No question about it. It was written as a utility that has saved me a lot of time and headache as a certain pandemic forced online teaching. It is not an example of best practice in coding but it works. It really does (I think).
Functionalities
Creation of files needed for integration with LMS or Canvas, including math symbols.
Creation of latex files using the exam class.
Creation of latex files with answer keys.
Spellchecking.
Installation
There is no installation needed as this is a simple Python script. It can be downloaded from Download.
Just make sure to have:
A working LaTeX environment in place
Python installed
Note
If you wish to use specific LaTeX packages in your mathematical formulas, you will need to edit the python script directly. There is work in progress to make this process simpler.
Hint
Feel free to contact me by email with any question!
Basic usage and configuration file
The process is simply to run the python script (see Download) in a directory where the config.yaml
file described below is located. A question file must also be findable (see yamlfile:
keyword below).
Configuration file
All the necessary configuration information is provided in the config.yaml
input file. The file reads as:
yamlfile: quiz20.yaml
spellcheck: no
createLMS: yes
createLMS_text: no
base: "http://homepages.rpi.edu/~meuniv/Images/TSM_F20/"
dir: "THERMO"
title1: 'PHYS {4420}: Thermodynamics and Statistical Mechanics (Quiz 20)'
title2: "Dr. Vincent Meunier, Fall 2020"
solutionKey: no
The various keywords (ending with a :
) are mostly self-explanatory.
yamlfile
provides the actual file with the list of questions (see here: YAML Format (input))spellcheck
is a yes/no input (more information can be found here: Spellchecking)createLMS
andcreateLMS_text
are yes/no answers (see here: LMS Output)base
is only used if thecreateLMS: yes
is used. It is also described in LMS Output.dir
is only used if thecreateLMS: yes
is used. It is also described in LMS Output.title1
andtitle2
are used to assemble the PDF files (both the raw exam and the version with answer keys when requested)solutionKey
is a yes/no input. This provides the answer Key in a PDF file with highlighted answers (see here Portable Data File creation)
Note
This configuration file was used to create the examples shown in Example of multiple questions.
Portable Data File creation
By default, yaml2lms always creates a PDF file, using latex processing and the exam class. The output file name (<prefix>.pdf) is built based on the yaml file name provided using yamlfile: <prefix>.yaml
in config.yaml.
The document will have a two-line title, provided by the tags title1
and title2
in the config.yaml file.
An example of PDF file output is shown in Example of multiple questions.
Two additional PDF files can be created if requested in the config.yaml files as described below.
Hint
During PDF file creation, a <prefix>.tex is also created. If the PDF file fails to compile (because of an issue with your latex input), you can directly refer to the latex source. You can also fine-tune that file as necessary.
Answer Key PDF file
If solutionKey: yes
is specified, in addition to the questionnaire itself, a file, named <prefix>_solutions.pdf is created where the correct asnwers are highlighted. (See Example of multiple questions for an example).
Spellchecked PDF file
If spellcheck: yes
is specified, yaml2lms performs a rudimentary spell checking of the input. This feature is described in details at Spellchecking.
Note
It is recommended to carefully check the PDF files before starting the process of creating LMS files. The createLMS: yes
option can take a few minutes or so to process, depending on the number of questions. And it is best to perform this once you have checked the spelling and the correctness of the answer keys. Note, also, that the spellchecking is time consuming and once you are done spellchecking, it is best to turn that option off.
LMS Output
One of the goals of yaml2lms is to create an interface between a list of questions you create locally to a format that can be directly imported into an online teaching platform such as blackboard. It is called Learning Management System (LMS) at Rensselaer Polytechnic Institute. However, the same format is used widely in systems such as canvas, used broadly in many different places.
It is possible to import questions on LMS but the format is somewhat difficult to handle. The format can be found at various places online, such as as on the Blackboard website.
The primary goal of this script is to translate the yaml-formatted questions into a file readable by Blackboard. It has now been extended to provide nice latex and rst outputs. The current version is versatile with options such as: randomization, use of multiple files, various output formats, etc.
Text only
If you only have unformatted text in your questions, this is the option you need to use in the config.yaml file (createLMS_text: yes
), as described in Basic usage and configuration file. In this case, all the information is stored in a file that can be directly uploaded onto lms.
Note
the name of the output file is built from the <prefix> of the input file name provided in config.yaml under yamlfile: <prefix>.yaml
to which “_LMS_text.txt” is appended.
Example:
If the input is:
- Type: MC
Text: >-
Consider the exchange operator P12 whose effect is to swap two
particles. What is true among the assertions below?
Answers:
- Choice: The eigenvalues of the operator are complex.
- Choice: The eigenvalues of the operator are positive.
- Choice: >-
The eigenvalues of the operator can only take two values for any
wavefunction describing a pair of Bosons or a pair of Fermions.
Validity: correct
- Choice: 'The operator is unitary but not Hermitian.'
- Choice: 'None of the other claims are correct. '
Note: lecture 29
The output becomes:
MC Consider the exchange operator P12 whose effect is to swap two particles. What is true among the assertions below? The eigenvalues of the operator are complex. incorrect The eigenvalues of the operator are positive. incorrect The eigenvalues of the operator can only take two values for any wavefunction describing a pair of Bosons or a pair of Fermions. correct The operator is unitary but not Hermitian. incorrect None of the other claims are correct. incorrect
You can upload the file <prefix>_lms_text.txt
on LMS when creating a new test (or adding a question to an existing test) and you obtain a result like this:

Note
In this example, we only used one question but if the yaml file contains multiple questions, they will be included into the test on LMS.
LMS using images
The method described above works very well and is very fast in terms of processing time.
However, it does not allow for fancy formatting and, while possible, the inclusion of math symbols is neither straitghforward nor totally satisfactory.
Here, I describe the second method to create an LMS test using more advanced formatting. In this case you need to use this option: createLMS: yes
.
It is important to note that this mode uses a collection of small images to assemble the questions (each image is created by LaTeX). So, one caveat with this method is that you need a place where you will copy the images and that place has to be accessible on the web.
Important
The LMS using images mode is only possible if you have a place where you can copy the images to be accessible on a browser.
The place where the images will be copied is provided in the keyword base: <httpsite>
provided in file config.yaml, as described in Basic usage and configuration file.
Note
The name of the output file is built from the <prefix> of the input file name provided in config.yaml under yamlfile: <prefix>.yaml
to which “_LMS_png.txt” is appended.
Let’s describe the process using an example. The example is similar to the one we used for the text-only option but in this case, we added some LaTeX and some formatting code.
If the input is:
- Type: MC
Text: >-
Consider the exchange operator $\hat{P}_{12}$ whose effect is to swap two
particles. What is \textbf{true} among the assertions below?
Answers:
- Choice: The eigenvalues of the operator are \textit{complex}.
- Choice: The eigenvalues of the operator are positive.
- Choice: >-
The eigenvalues of the operator can only take two values for any
wavefunction describing a pair of Bosons or a pair of Fermions.
Validity: correct
- Choice: 'The operator is unitary but not Hermitian.'
- Choice: 'None of the other claims are correct. '
Note: lecture 29
When using the option createLMS: yes
, yaml2lms creates a file <prefix>_lms_png.txt
along with a directory with a collection of images.
The output stored in <prefix>_lms_png.tx
looks something like this (though you will probably never have to look at it, as the idea of the script is to avoid it!)
MC <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0.png"
height="33" /></p> <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0_0.png"
height="33" /></p> incorrect <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0_1.png"
height="33" /></p> incorrect <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0_2.png"
height="33" /></p> correct <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0_3.png"
height="33" /></p> incorrect <p><img
src="http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3/Q0/Q0_4.png"
height="33" /></p> incorrect
You can see from the example that the script tells LMS that the various image files are stored, in this example, at “http://homepages.rpi.edu/~meuniv/Images/TSM_F20/Questions_THERMO_test3”.This address was created using the base: "http://homepages.rpi.edu/~meuniv/Images/TSM_F20/"
provided in config.yaml and the directory is built from dir: "THERMO"
.
Now that you have completed this, you only need two more steps.
Copy the image directory to the place where you want to move the file. This can be done easily with a method such as
scp -r Questions_THERMO_test3 meuniv@rcs.rpi.edu:~/public_html/Images/TSM_F20/.
This line is provided for your convenience at the end of the script. Of course you need to change the username and the address of the server where you place the file. Here I use the space provided by my university but using a different repository may not be a bad idea (github or even dropbox).Go to LMS and upload the questions, using the file
<prefix>_lms_png.txt
as described above.
After uploading the question, the exam looks like this:

The content is the same as in the example using text only. I personally prefer this approach as it makes for much nicer looking exam, even when no math is required. However, note that the other method is somewhat more straighforward.
Important
Do not turn on the creation of LMS files (using either method) until you have carefully checked the PDF created with the default options. It is also important to check that the asnwers you selected as correct are indeed correct (check the PDF with the answer keys). It is always possible to change that on LMS itself but it is not as easy.
YAML Format (input)
Each question is provided in yaml format. The format is somewhat unforgiving as spacings and alignments need to be correct for the file to be readable. Below is an example of a question using this format. Each line will be described in details.
Question example
1 - Type: MC
2 Text: >-
3 In the screencast we derived an expression for the Fermi-Dirac distribution
4 using the grand canonical ensemble. What is the grand canonical ensemble?
5 Size: Auto
6 Points: 2
7 Answers:
8 - Choice: >-
9 The ensemble of systems with fixed energy and entropy.
10 Validity: incorrect
11 - Choice: >-
12 The ensemble of systems with fixed energy and chemical potential.
13 Validity: incorrect
14 - Choice: 'The ensemble of systems with fixed temperature and chemical potential.'
15 Validity: correct
16 Skip: 'no'
17 Note: question regarding lecture 29
Anatomy of a question
We will now review each line. Basically, all names that are followed by “:” is a key in a dictionary. If you wish to use a “:” in your questions or answers, you need to use quotation marks (or the >-
sign – it is usually used for text with multiple lines).
New question starts with the type.
- Type: MC
Each new question starts with a - Type:
keyword. The options are: MC
(Multiple Choice), MA
(Multiple Answers),…
Description of the question itself.
- Text: 'This is my question'
or
- Text: >-
This is a multiline question with various parts in it.
Try it if you want to.
Tip
The advantage of this approach is that you can use LaTeX commands in both text and math modes in all the text used in the yaml file.
For multiple-choice questions, you then have the list of possible answers:
Answers:
- Choice: >-
The ensemble of systems with fixed energy and entropy.
Validity: incorrect
Each Choice can be entered in a single-line or multiline format. There is a second keyword called Validity
(case sensitive) to assign an incorrect or correct attribute to the choice. Note that you only need to provide the information for a correct asnwer as the script will assign an incorrect attribute by default.
Note 1: You can list as many Choice lines as you need.
Note 2: For some types of questions, only one answer can have the
Validity: correct
attribute.
You can skip the question from the file without deleting it by using:
Skip: 'no'
(this is an optional keyword)To keep things tidy, you can add a note for each question, using the
Note:
keyword.
Note
The easiest way to create a question is to use an existing one as a template!
Hint
A good habit is to check your yaml file using a free online tool such as those provided by onlineyamltools.com (see, here: https://onlineyamltools.com/validate-yaml). After a while you won’t make a mistake anymore but early on, this could be frustrating.
Spellchecking
Note
This features remains in development but can be useful to identify basic typos.
Usage
When you use the spellcheck: yes
in the config.yaml
file, yam2lms will perform a spellcheck of the questions. Currently, the script uses the python library provided in the spellchecker
module.
The script will check all words in the document and provide a corrected version called XXX_SPELLCHECKED.pdf
file. Using the example provided in YAML Format (input), we get the result shown below.
Notes
The process is slow. Once you have run this and corrected your mistake(s), turn this option off to avoid lenghty processing.
yaml2lms does not make any correction; instead it makes suggestions as shown in the example below. In this example, most of the mistakes found are actually not mistakes and it is expected each user will look into this separately. Only one word was correctly flagged as incorrect.
Clearly this utility needs work but it at least avoids making too many obvious mistkaes (oops).
Example

Example of multiple questions
Yaml input
- Type: MC
Text: 'Among the following claims, which one is \textbf{not} true?'
Size: Auto
Points: 2
Answers:
- Choice: Bosons have an integer spin value and Fermions have an half integer spin value.
- Choice: Bosons and Fermions are treated as indistiguishable particles.
- Choice: Multiple Fermions can occupy the same quantum state.
Validity: correct
- Choice: Multiple Bosons can occupy the same quantum state.
- Choice: 'All the other claims are correct. '
Note: lecture 29
- Type: MC
Text: >-
Consider the exchange operator $\hat{P}_{12}$ whose effect is to swap two
particles. What is true among the assertions below?
Size: Auto
Points: 2
Answers:
- Choice: The eigenvalues of the operator are complex.
- Choice: The eigenvalues of the operator are positive.
- Choice: >-
The eigenvalues of the operator can only take two values for any
wavefunction describing a pair of Bosons or a pair of Fermions.
Validity: correct
- Choice: 'The operator is unitary but not Hermitian.'
- Choice: 'None of the other claims are correct. '
Note: lecture 29
- Type: MC
Text: 'At low energy, Fermions and Bosons follow the same statistical distribution.'
Size: Auto
Points: 2
Answers:
- Choice: This is always true.
- Choice: This is sometimes true.
- Choice: This is never true.
Validity: correct
Note: lecture 29
- Type: MC
Text: What can you say about the chemical potential ($\mu$)?
Size: Auto
Points: 2
Answers:
- Choice: It is small when the density of matter ($n$) is small.
Validity: correct
- Choice: It is small when the density of matter ($n$) is large.
- Choice: 'It does not depend on the density of matter ($n$). '
Note: lecture 29
- Type: MC
Text: >-
At high energy (large $E-\mu$ values), we can use the Botzmann,
Fermi-Dirac, or Bose-Einstein disributrions to study any gas of
particles (e.g., photons, electrons,\ldots)
Size: Auto
Points: 2
Answers:
- Choice: 'This is true because this corresponds to the low density limit. '
Validity: correct
- Choice: >-
This is false because the three distributions do not converge to one
another at high energy.
- Choice: >-
This is a very good question. I wil make sure to read page 598 of the
slides posted on SLACK to understand the answer.
Note: lecture 29
- Type: MC
Text: >-
Consider a bosonic particle extracted from a large distribution of the same
particles. A specific measurement shows that its energy is 1~eV below the
chemical potential of the distribution. What can you conclude?
Size: Auto
Points: 2
Answers:
- Choice: 'The particle is very stable. '
Validity: incorrect
- Choice: 'The entropy of the particle is very small. '
Validity: incorrect
- Choice: >-
Researchers who performed the measurements should sign up for PHYS-2350
at RPI: their measurement is clearly wrong.
Validity: correct
Note: lecture 29
Latex Output
The latex output for this entry is thus:

Latex Output with keys
If you used the SolutionKey: yes
option, you would get:

Work in progress and future utilities
Yaml2LMS is a hack. It is not perfect but it gets the job done in many cases.
There are many things that can be improved (send your suggestions to me).
Here is a list of things I’m working on:
Improved spellchecker
Better management of latex packages
Possibility of reading multiple question files
Create readthedocs questionnaires for learning
Download
yam2lms is just a python script. It can be obtained below:
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sat Aug 29 17:49:38 2020
@author: vmeunier
"""
#38add: scp -r Questions_THERMO_quiz14 meuniv@rcs.rpi.edu:~/public_html/Images/TSM_F20/.
import yaml
from textblob import TextBlob
from distutils.util import strtobool
from spellchecker import SpellChecker
with open("config.yaml") as conf:
configuration = yaml.load(conf, Loader=yaml.FullLoader)
yamlfile=configuration['yamlfile']
spellcheck=configuration['spellcheck']
create_LMS=configuration['createLMS']
create_LMS_text=configuration['createLMS_text']
base=configuration['base']
dir=configuration['dir']
title1=configuration['title1']
title2=configuration['title2']
solutions=configuration['solutionKey']
print(yamlfile)
with open(yamlfile) as f:
myset = yaml.load(f, Loader=yaml.FullLoader)
print(myset)
class Question:
def __init__(self, Type, Text, Size, Choice, Valid):
self.Type = Type
self.Text = Text
self.Size = 'Auto'
self.Points = '0'
self.Skip = 'False'
self.TextString = Text
self.Choice = []
self.ChoiceString = []
self.Valid = []
self.ValidString = []
def out(self):
string="%s\t%s" % (self.Type,self.Text)
for i in range(len(self.Choice)):
string+='\t%s \t%s' % (self.Choice[i], self.Valid[i])
string+='\n'
return string
def outpng(self):
string="%s\t%s" % (self.Type,self.TextString)
for i in range(len(self.ChoiceString)):
string+='\t%s \t%s' % (self.ChoiceString[i], self.ValidString[i])
string+='\n'
return string
#here we want to translate input into the question class and populate it
MyQuestions=[]
l=len(myset)
print(l)
for q, question in enumerate(myset):
ThisQuestion=Question('MC','','','','')
if 'Type' in question.keys():
ThisQuestion=Question(question['Type'],'','','','')
ThisQuestion.Text=question['Text']
if 'Size' in question.keys():
ThisQuestion.Size=question['Size']
else:
ThisQuestion.Size='Auto'
nanswers=len(question['Answers'])
for i in range(nanswers):
ThisQuestion.Choice.append(question['Answers'][i]['Choice'])
if 'Validity' in question['Answers'][i].keys():
ThisQuestion.Valid.append(question['Answers'][i]['Validity'])
else:
ThisQuestion.Valid.append('incorrect')
if 'Skip' in question.keys():
ThisQuestion.Skip=strtobool(question['Skip'])
else:
ThisQuestion.Skip=strtobool('no')
if not ThisQuestion.Skip:
MyQuestions.append(ThisQuestion)
#spell checking
def correctstring(string):
splits=string.split()
for i, word in enumerate(splits):
if "$" not in word and "\\" not in word:
#print(word)
wordy=TextBlob(word)
if wordy != wordy.correct():
splits[i]="\\sout{\\textcolor{red}{" + word + "}} \\textcolor{blue}{" + str(wordy.correct())+"}"
newstring=' '.join(splits)
return newstring
def correctstring2(string):
spell=SpellChecker()
splits=string.split()
misspelled=spell.unknown(splits)
for i, word in enumerate(misspelled):
# Get the one `most likely` answer
splits[i]=spell.correction(word)
newstring=' '.join(splits)
return newstring
import os
os.environ["PATH"] += os.pathsep + '/Library/TeX/texbin/'
os.environ["PATH"] += os.pathsep +'/usr/local/bin/'
def latexsize(string,filename):
size='auto'
with open("temporary.tex","w") as myfile:
myfile.write("\\documentclass[preview]{standalone}\n")
myfile.write("\\usepackage[fleqn]{amsmath}\n")
myfile.write("\\usepackage{physics}\n")
myfile.write("\\usepackage[T1]{fontenc}")
myfile.write("\\newcommand{\\dbar}{\\text{\\dj}}")
myfile.write("\\DeclareUnicodeCharacter{2212}{-}")
myfile.write("\\begin{document}\n")
myfile.write("\\setlength{\\mathindent}{3pt}\n")
#myfile.write("\\setlength{\\abovedisplayskip}{3pt}\n")
myfile.write("\\newcommand{\\makenonemptybox}[2]{%\n")
myfile.write("\\par\\nobreak\\vspace{\\ht\\strutbox}\\noindent\n")
#FIXME: make this part of option
#put back if you want a box around the box
# myfile.write("\\fbox{%\n")
myfile.write("\\parbox[c][\\dimexpr#1-2\\fboxsep][c]{\\dimexpr\\linewidth-6\\fboxsep}{\n")
myfile.write("\\hrule width \\hsize height 0pt\n")
myfile.write(" #2\n")
#put back if you want a box around the box
# myfile.write(" }%\n")
myfile.write("}%\n")
myfile.write("\\par\\vspace{\\ht\\strutbox}\n")
myfile.write("}\n")
myfile.write("\\makeatother\n")
string=string.replace("$$","\\begin{equation*}",1)
string=string.replace("$$","\\end{equation*}",1)
size2=size.strip()
if size2.lower()=="auto": #automatic height is size is negative
#print(string)
myfile.write("\\parbox[c]{\\textwidth}{\\begin{flushleft}\n%s\n\\end{flushleft}} \n" % string)
else: #fixed height if size is provided (Auto is default)
myfile.write("\\makenonemptybox{%s}{%s} \n" % (size, string))
myfile.write("\\end{document}\n")
myfile.close()
x = os.system("pdflatex temporary.tex > latex.log 2>&1 && gs -sDEVICE=pnggray -sBATCH -sOutputFile=%s -dNOPAUSE -r1200 temporary.pdf > latex.log 2>&1" % filename)
if x !=0:
print ('Exit code not 0, check result!')
else:
#os.system('open %s' % filename)
os.system('rm temporary.tex')
def latexquestionnaire(Questions,solutions,yamlfile):
if solutions:
answers='answers'
filepdf=yamlfile.rsplit(".",1)[0]+"_solutions.pdf"
filelatex=filepdf.rsplit(".",1)[0]+".tex"
else:
answers=''
filepdf=yamlfile.rsplit(".",1)[0]+".pdf"
filelatex=filepdf.rsplit(".",1)[0]+".tex"
with open(filelatex,"w") as flatex:
flatex.write("\\documentclass[%s]{exam}\n" % answers)
flatex.write("\\usepackage{physics}\n")
flatex.write("\\usepackage[T1]{fontenc}\n")
flatex.write("\\usepackage[normalem]{ulem}\n")
#below is special macro for Thermo!
flatex.write("\\newcommand{\\dbar}{\\text{\\dj}}\n")
flatex.write("\\usepackage[margin=.75in]{geometry}\n")
flatex.write("\\usepackage{lastpage}\n")
flatex.write("\\usepackage{color}\n")
flatex.write("\\usepackage[T1]{fontenc}\n")
flatex.write("\\DeclareUnicodeCharacter{2212}{-}")
flatex.write("\\firstpageheader{% left\n")
flatex.write("%s\\\\\n" % title1)
flatex.write("%s\n" % title2)
flatex.write("}{% center\n")
flatex.write("}{% right\n")
flatex.write("\ifprintanswers \\textbf{Answer Key}\n")
flatex.write(" \\fi}\n")
#flatex.write("\\runningheader{}{}{}\n")
#flatex.write("\\firstpagefooter{}{\\thepage/\pageref{LastPage}}{}\n")
#flatex.write("\\runningfooter{}{\\thepage/\pageref{LastPage}}{}\n")
#flatex.write("\\frenchspacing\n")
flatex.write("\\unframedsolutions\n")
flatex.write("\\SolutionEmphasis{\\sffamily}\n")
flatex.write("\\renewcommand{\\solutiontitle}{Answer:~}\n")
flatex.write("\\makeatother\n")
flatex.write("\\extraheadheight{.35in}\n")
flatex.write("\\extrafootheight{.15in}\n")
flatex.write("\\setlength{\\marginparwidth}{1.5in}\n")
flatex.write("\\nopointsinmargin\n")
flatex.write("\\pointformat{}\n")
flatex.write("\\CorrectChoiceEmphasis{\\color{red}\\bfseries}\n")
flatex.write("\\begin{document}\n")
flatex.write("\\begin{questions}\n")
for q, question in enumerate(Questions):
flatex.write("\\question %s\n" % question.Text)
flatex.write("\\begin{choices}\n")
for i in range(len(question.Choice)):
if(solutions and question.Valid[i].lower() == "correct"):
flatex.write("\\CorrectChoice %s\n" % question.Choice[i])
else:
flatex.write("\\choice %s\n" % question.Choice[i])
flatex.write("\\end{choices}\n")
flatex.write("\n")
flatex.write("\\end{questions}\n")
flatex.write("\\end{document}\n")
flatex.close()
x = os.system("/Library/TeX/texbin/pdflatex %s > latex.log2 2>&1" % filelatex)
#need to run twice to get total number of points correctly added
x = os.system("/Library/TeX/texbin/pdflatex %s > latex.log2 2>&1" % filelatex)
if x !=0:
print ('Exit code not 0, check result! %s ' % filepdf)
os.system("open %s" % filepdf)
if spellcheck:
#spell checking and provide latex file with proposed corrections
#we do not touch the original questions
#the users must make the changes themselves
import copy
MyNewQuestions=copy.deepcopy(MyQuestions)
for question in MyNewQuestions:
question.Text=correctstring(question.Text)
for i, choice in enumerate(question.Choice):
question.Choice[i]=correctstring(question.Choice[i])
filepdf=yamlfile.rsplit(".",1)[0]+"SPELLCHECKED.pdf"
latexquestionnaire(MyNewQuestions,False,filepdf)
#now the original file
latexquestionnaire(MyQuestions,solutions,yamlfile)
if create_LMS_text:
#This is for un-latexized version of the questions
filename=yamlfile.rsplit(".",1)[0]+"_LMS_text.txt"
f = open(filename, "w")
for q in MyQuestions: #run over all questions
f.write(q.out().replace("$","$$"))
f.close()
#ID = "THERMO_"+yamlfile.rsplit(".",1)[0] #we remove suffix but only last one
ID = dir+"_"+yamlfile.rsplit(".",1)[0] #we remove suffix but only last one
if create_LMS:
myfolder='Questions_%s' % ID
scp_string='scp -r '+myfolder+' meuniv@rcs.rpi.edu:~/public_html/Images/TSM_F20/.'
print("PNG files will be stored at %s " % myfolder)
if not os.path.exists(myfolder):
print("Directory does not exist, creating it\n")
os.makedirs(myfolder)
else:
print("Directory already exists. Files will be replaced.\n")
#this is for latexized version of the questions
filename=yamlfile.rsplit(".",1)[0]+"_LMS_png.txt"
fpng = open(filename, "w")
j=0
for q in MyQuestions: #run over all questions
p=j+1
# one directory per question
myfolder2=myfolder+"/Q%s" % j
if not os.path.exists(myfolder2):
os.makedirs(myfolder2)
print("\t Question type: %s" % q.Type)
filename=myfolder2+"/Q%s.png" % j
latexsize(q.Text,filename)
print(q.Text)
filename=base+filename
q.TextString="<p><img src=\"%s\" height=\"33\" /></p>" % filename
for i in range(len(q.Choice)):
filename=myfolder2+"/Q%s_%s.png" % (j, i)
latexsize(q.Choice[i],filename)
filename=base+filename
q.ChoiceString.append("<p><img src=\"%s\" height=\"33\" /></p>" % filename)
if q.Type.lower() == "mat" :
filename=myfolder2+"/S%s.png" % i
latexsize(q.Valid[i],filename)
filename=base+filename
q.ValidString.append("<p><img src=\"%s\" height=\"33\" /></p>" % filename)
else :
q.ValidString.append(q.Valid[i])
fpng.write(q.outpng())
j=j+1
fpng.close()
print("To copy for LMS use, executive: %s" % scp_string)