Generating Math Tests with Python

Auto-Generate Unique Tests

This is a script for generating a bunch of unique math tests from a “Test Template” and a spreadsheet containing test inputs and problem solutions.

In our Test Template we set the layout of our test and define our test problems. Our test problems will have variable placeholders (TestID, Question ID, VarA, etc.) that we will replace with data from our “Test Data” spreadsheet.

In our excel file, we random generate values for the A, B, and C variables (using the =RANDBETWEEN() function) and clearly identify which Question, Equation, and Test ID they correspond to. In the Excel file, we’ll calculate solutions using the input data and equation listed for each entry.

Next, we can run our script. This is dependent on the docx (Note: pip install python-docx), docx2txt, re, pandas, and tkinter libraries.  Forms will pop-up prompting you for the Test Template and Test Data files.

Creates unique test documents with data
taken from a DataFrame (which is populated from an excel file).

Input: Test Template (Word Document).  Test Data (Excel File)
Output: 20 Unique Tests (Test Data)
#Import modules
import docx
import docx2txt
import pandas as pd
import re
from tkinter import Tk
from tkinter import filedialog


#Define "Test" template
template_file=filedialog.askopenfilename(title="Please select Word template")
testdata_file=filedialog.askopenfilename(title="Please select Test Data spreadsheet")

#Read file data

#Produce 20 unique tests
for i in range(20):
    #Add data for 10 unique questions
    for j in range(10):
        #Define replacement dictionary
        rep=dict((re.escape(k),v) for k, v in rep.items())
        if j==0:
            new_text=pattern.sub(lambda m: rep[re.escape(],template_text,count=4)
            new_text=new_text.replace('TestID','Test #' + str(i+1))
            new_text=pattern.sub(lambda m: rep[re.escape(],new_text,count=4)
    #Create and save new test document
    test_doc.add_paragraph(new_text)'C:\Users\Craig\Documents\Python Scripts\Test #'+str(i+1)+'.docx')

After the files have been selected, the script reads the Test Template text and loads the Test Data into a DataFrame. We then loop through the Test Data and produce 20 unique test documents by substituting the placeholder variables with values from the Test Data spreadsheet. Each test document is clearly labeled and we can use our original Test Data as our answer key.

Thanks to Andrew Clark for his code for replacing multiple text strings.

Finding Words with PyPDF2

Find all instances of words in a PDF with Python’s PyPDF2 library.

This is a script for finding all instances of a given search word (or multiple search words) in a PDF.

For our example, we’ll be using a PDF of Romeo and Juliet.  In this case, our search terms are “Romeo” and “Juliet” (search is not case-sensitive).

import PyPDF2
import re




with open('FoundWordsList.csv', 'w') as f:
    f.write('{0},{1}\n'.format("Sheet Number", "Search Word"))
    for word in searchwords:
        for page in range(number_of_pages):
            words_start_pos[page]=[dwg.start() for dwg in re.finditer(word, pages_text[page].lower())]
            words[page]=[pages_text[page][value:value+len(word)] for value in words_start_pos[page]]
        for page in words:
            for i in range(0,len(words[page])):
               if str(words[page][i]) != 'nan':
                    f.write('{0},{1}\n'.format(page+1, words[page][i]))
                    print(page, words[page][i])

We run the script and get an output that shows each instance of each search word and the associated PDF page number:

This script can be used for a variety of other applications by updating the file path (line 4) and the search terms (line 12).

A few ideas for modification include:

  • Frequency counts of words in books/lyrics (ATS has an awesome frequency count graph generator)
  • Finding reference drawing numbers in a document
  • Identify search terms by prefixes rather than whole words
  • Identifying sheets that need to be updated
  • Using glob to iterate through multiple files

How else would you modify this script?  Let me know!

Thanks for reading!

Special thanks to these sources:

Automate the Boring Stuff with Python
ritesh_shrv on Stack Overflow