Building PDFs

Now that we have our 6-page 3-question 2-version source PDFs created we need to build the actual PDFs that we’ll print. But before we do that, we should create a directory in which all our Plom work is going to live, and move into it. For example

$ mkdir midterm
$ cd midterm

We’ll refer to this as our project directory.

Now we have to start interacting with Plom. In particular, we need to tell Plom all about the structure of our test and how many papers it needs to produce. We do that through a test specification.

Once you are in your project directory we can run plom-build which will give a summary of available commands:

$ plom-build
usage: plom-build [-h] {new,parse,class,make} ...

By default the command doesn’t do anything, but it does tell you that it has 4 sub-commands: new, parse, class and make.

Creating a specification

The test specification needs to be precise, so rather than making it from scratch each time, Plom will provide you with a template to edit. Run the new subcommand:

$ plom-build new
Creating specification file with name "testSpec.toml"
Creating "sourceVersions" directory for your test source PDFs.
Please edit the template specification file and copy your source test PDFs into the sourceVersions directory as version1.pdf, version2.pdf etc.

Plom tells you that it has done 2 things:

  • it has created a specification file testSpec.toml for you to edit, and
  • it has created a subdirectory sourceVersions into which you need to place your source PDFs and you have to name them version1.pdf and version2.pdf.

So, after you have copied your source versions into place, take a look at testSpec.toml file. The specification is written in the toml format which is human-readable and easy to edit in your favourite text-editor (vi, emacs, atom etc). The start of the file (minus some license preamble stuff you can ignore) should be:

# >>> Edit the data below <<

# the human-readable names of the test - one long, one short.
name = "m101mt1"
longName = "Midterm for Mathematics 101"
# how many source versions
numberOfVersions = 3
# how many pages
numberOfPages = 8
# total number of papers to produce
# how many of those papers should be named - ie from classlist - always <= numberToProduce

and then the file continues with information about the number of questions, ID-pages and so on.

Names and numbers

We’ll start editing these to match our test.

  • name: this should be a short name that will be printed on the test. Something simple like mABCmt1 to indicate “Mathematics ABC Midterm 1”.
  • longName: a longer name that will be used to help build a test-return website. Hence we should write something like Midterm 1 for Mathematics ABC 2318. Including the year or term is a good idea to avoid potential confusion.
  • numberOfVersions: in our running example, we have 2 versions of the test.
  • numberOfPages: in our running example, each test is 6 pages.
  • numberToProduce: this is the total number of test PDFs we will produce. Each test we print must be unique, and cannot be reused. Hence this number should always be some more than the total number of students you expect to sit the test. The developers have typically produced something like 10% more the number of students. Remember that you don’t actually have to print all those PDFs, but you can keep them in reserve just in case.
  • numberToName: Plom can produce tests with student names and IDs printed on them. This is very useful when you have assigned seating for example. We show an example of this below.

We’re going to assume that we will have around 30 students sitting (15 in each section), so we’ll actually produce 40. Then one section wants named tests and the other doesn’t, so we’ll tell Plom to name 15. So now the start of our specification looks like

name = "mABCmt1"
longName = "Midterm for Mathematics ABC"
numberOfVersions = 2
numberOfPages = 6

Pages and questions

Now let us turn to the rest of specification – its job is to tell Plom the details of ID-pages, Do-no-mark pages, and our test questions. Take a look at the rest of specification:

# How many questions to mark - these will be named [question.1], [question.2], ... [question.n]

# the id-pages - always taken from Version1
pages=[1] # must contain at least 1 page and be contiguous

# pages that are not marked (like instructions, formula sheets, examination rules etc) - always taken from Version1
pages=[2,7] # must exist, but can be an empty list.

# now the actual questions (groups of pages) to be marked
# [question.n] with n a positive integer [1,2,3... etc] - contiguous please.
# pages = [1,2,3] - contiguous ordered list of positive integers
# mark = positive integer
# select = "fixed" or "shuffle"




The rest of the specification tells Plom how to put pages together into questions, ID-pages, and do-not-mark pages (ie formula sheets and instructions).

  • numberOfQuestions: For our example this is 3.
  • [idPages]: This subsection of the specification tells Plom which pages belong together and constitute the ID pages for the test. In our case this is just page 1. However you have the flexibility to set any contiguous set of pages to be the ID pages. Note that these pages are shown while identifying papers, but will not be shown while marking/annotating papers.
  • [doNotMark]: These are pages that are neither ID pages nor questions. Typically these will be used for instructions or formula sheets. For our example it is just page 2.

To specify each of our questions we need to create subsections [question1], [question.2], [question.3] and so forth. Since we have 3 questions, we can just use the ones already in the template. For each question we must specify the pages, the total mark, and how that page is to be selected from the source versions. A selection is either fixed or shuffle. When fixed that question will be taken from source-version 1 in every single test produced. When shuffle, the pages of that question will be taken from a randomly chosen source-version. For our example, lets use shuffle for all the questions.

After making these edits, our specification now reads:

name = "mABCmt1"
longName = "Midterm for Mathematics ABC"
numberOfVersions = 2
numberOfPages = 6






Checking that specification

To make sure that the specification is consistent and satisfies some simple “sanity checks” we run plom-build again, now with the parse sub-command:

$ plom-build parse
Parsing and verifying the specification "testSpec.toml"
Check specification keys
        contains "name" - check
        contains "longName" - check
        contains "numberOfVersions" - check

and quite a few more lines of checks. Most of this is simply checking that Plom has everything it needs, that total-marks are positive integers, each page used exactly once, and so on. You don’t really need to pay much attention to this unless something goes wrong. Since everything checks out okay, this tells us that the hard work of specifying the test is done. We can almost move onto building PDFs.

But first take a look the last few lines.

Assigning a privateSeed to the spec - check
Assigning a publicCode to the spec - check
Saving the verified spec to "verifiedSpec.toml"
Your spec indicates that you wish to print named papers.
Please process your class list using "plom-build class ".

This tells that Plom is assigning your test a random number seed that will be used for any random selection of versions. It is also fixing a code that will be stored (along with other information) in the qr-codes stamped on pages. This code helps plom to make sure that when you upload page images that they really do belong to this test. Then, rather than altering your spec file, Plom copies your spec file with these new codes to a new verified spec file, which is what the system will actually use.

Finally, since the specification tells Plom to print some tests with names printed on them, we have to supply Plom with a class list.

Loading in a classlist

Plom needs to know who is in your class so that it can construct your spreadsheets and return papers to (the correct) students. Additionally, if you asked Plom to print some papers with names on the front (ie numberToName is non-zero) then Plom gets those names from your classlist. To be more precise – the system grabs those names from the start of your list.

Run plom-build class:

$ plom-build class
Please provide a classlist file.
Class list format Class list must be a CSV with column headers
(*) "id" - student ID number
(*) student name in a single field = "studentName"  *or*
(*) student name split in two fields:
--->["surname" or "familyName" or "lastName"] *and*
--->["name" or "firstName" or "givenName" or "nickName" or "preferredName"].

Alternatively, give csv exported from Canvas.

Plom wants your classlist as a CSV file. Since the developers’ institution runs Canvas, Plom handles canvas’ csv spreadsheets. More generally Plom only needs 2 pieces of data per student:

  • student ID number – Plom expects an 8 digit ID number. If your institution uses a different format then please let us know and we’ll work out how to accomodate it.

  • student name – for various good reasons Plom will compress multiple name-fields (eg “family name” and “given name”) into a single name field. That being said, Plom will handle a few different as indicated by the output above.

The Plom demo-system includes a fake classlist (which we generated using this python module). Loading this into Plom gives:

$ plom-build class ./fakeList.csv
Class list headers = ['id', 'studentName']
Loading from non-Canvas csv file: "./fakeList.csv"
"id" column present
"studentName" column present
WARNING: The following ID/name pairs contain non-Latin characters:
row 14, number 11593837, name: "三, 一"
>>> WARNING <<<
Potential classlist problems The classlist you supplied contains non-Latin characters - see console output above. You can proceed, but it might cause problems later. Apologies for the eurocentricity.
Saving to specAndDatabase/classlist.csv

This tells us that the file is in the correct format, and warns us about non-Latin characters in one of the names. A copy of the file is saved for future use by the system.

Producing PDFs to print

Plom now has everything that it needs to build PDFs for you to print. Run plom-build make.

$ plom-build make
Reading specification
Creating plom database
Test 1 created
        ID-group created
        DoNotMark-group created
        Question 1 created
        Question 2 created
        Question 3 created
Test 2 created
        ID-group created
        DoNotMark-group created
        Question 1 created
        Question 2 created
        Question 3 created


Database created successfully
Making directory for papers.
Building named papers
100%|████████████████████████████████████████████████████████| 40/40 [00:04<00:00,  8.22it/s]
Checking papers produced and updating databases

The system constructs your PDFs in two steps. First it builds the database and populates it with information about each of the papers — such as the student name and ID (if you printed some named tests), and which question-versions have been chosen. Once that is done, the PDFs are constructed (in parallel) and any updates to the database are made. Your papers are now ready to print and you can find them in the papersToPrint subdirectory:

$ ls papersToPrint/
exam_0001.pdf  exam_0008.pdf  exam_0015.pdf  exam_0022.pdf  exam_0029.pdf  exam_0036.pdf
exam_0002.pdf  exam_0009.pdf  exam_0016.pdf  exam_0023.pdf  exam_0030.pdf  exam_0037.pdf
exam_0003.pdf  exam_0010.pdf  exam_0017.pdf  exam_0024.pdf  exam_0031.pdf  exam_0038.pdf
exam_0004.pdf  exam_0011.pdf  exam_0018.pdf  exam_0025.pdf  exam_0032.pdf  exam_0039.pdf
exam_0005.pdf  exam_0012.pdf  exam_0019.pdf  exam_0026.pdf  exam_0033.pdf  exam_0040.pdf
exam_0006.pdf  exam_0013.pdf  exam_0020.pdf  exam_0027.pdf  exam_0034.pdf
exam_0007.pdf  exam_0014.pdf  exam_0021.pdf  exam_0028.pdf  exam_0035.pdf

Here is the resulting exam_0001.pdf (which is named) and exam_0027.pdf (which is unnamed):

We strongly recommend that you check some of these files before you print. Each page should have 3 qr-codes and 1 triangle stamped in the corners. Additionally the centre-top of each page has the test- and page-number stamped on it. Please also double-check the specification. If something has gone wrong, before you print is the best time to catch it. After printing, it is much harder to fix things and some dangerous manual hackery may be required.

Something went wrong

Here are some common errors that are easy to fix before printing.

  • “I found an error in the specification”
  • “I used the wrong classlist”, or “My classlist is out of order”
  • “I didn’t generate enough papers”
  • “I found typos in my source tests”
  • “My source tests are different lengths”
  • “My source tests have different numbers of questions”
  • “The qr-codes in the final PDFs overlap my text.”

Before doing anything, take a quick look in the specAndDatabase subdirectory.

$ ls specAndDatabase/
classlist.csv  plom.db  verifiedSpec.toml

Depending on how far you got, the subdirectory contains your classlist (massaged into 2 columns by Plom), the database with all the tests, and the verified test specification. Next look in the papersToPrint subdirectory; it contains all your PDFs. Finally, the sourceVersions subdirectory should contain your source PDFs.

So you should

  • Delete the files inside the specAndDatabase subdirectory
  • Delete the PDFs inside the papersToPrint subdirectory
  • Check the source tests in the sourceVersions subdirectory and replace if needed
  • Check your test specification file and fix any errors.

Note – if you have problems with qr-codes overlapping the contents of your test then we recommend that you use the provided LaTeX template, or use the provided mockplom.sty to help you see where the final qr-codes will appear. See instructions here

You can now resume starting from plom-build parse.