Skip to main content

Tech Engineering Exam in Python

Project description

TEEPy stands for Tech Engineering Exam in Python. TEEPy’s aim is to make an easy-to-use framework to create clean and professional-looking paper-based assessments (e.g., quiz, exam, etc.) with a focus on STEM (Science, Technology, Engineering, and Math) related topics. There are two intended end-users of TEEPy, question creators (QCs), and assessment creators (ACs). QCs create individual questions or problems that will be used in a particular assessment that an AC will use to create the overall assessment. TEEPy accomplishes these goals by generating all question and assessment content as HTML and then rendering the HTML to create PDFs (using a combination of cefpython and pyppeteer). There is also some Beautiful Soup thrown in there as well. TEEPy supports the use of LaTex (via MathJax) as well as the use of units (using the package pint). TEEPy is capable of randomizing question placement and answer choices to questions.

Installation

Installation is a simple matter of

$ pip install teepy

and then enjoy!

Roles

There are two roles that an end-user of TEEPy can assume, question creator (QC) or assessment creator (AC). QCs deal solely with individual questions or problem creation. ACs deal with the assessment as a whole. ACs take one or more questions created by QCs and make an assessment out of the selected questions.

Question Creator (QC)

The goal of a QC is to create individual questions that are self-contained. The content and calculations in the individual question file will not interact with content and calculations performed in another question file. Each question file must contain two function definitions, a PROBLEM() function and a CHOICES() function. Each of these functions takes one argument, an index value variable. The index value is used to create different versions of a particular question. The PROBLEM() function uses the function’s docstring to define the problem statement and should return a dictionary containing at least a key of answer. If the question has no answer (e.g., in the case of an open-ended question), a None may be returned from the PROBLEM() function. An example of an open-ended question is shown below.

# An open-ended question
def PROBLEM(ind):
    '''What is the meaning of life?'''

    return None

Even if there are no different versions of the question, the PROBLEM() should be a function of the index variable ind.

In the event a question does have a correct answer (or answers), the value of the key answer should be a single value or a list of values (in the case of a multiple answer problem). An example of a single answer question is shown below.

# A single answer question
def PROBLEM(ind):
    '''What color is the sky?'''

    answer = 'Blue'

    return {'answer': answer}

An example of a multiple-answer question is shown below.

# A multiple-answer question
def PROBLEM(ind):
    '''How many licks does it take to get to the center of a Tootsie Pop?'''

    answer = [3, 'The world may never know.']

    return {'answer': answer}

The return value of PROBLEM() may also contain a key of given. The value of the key given should be a dictionary that includes any variables used in the question statement. An example of using a given variable is shown below.

# A question with a given variable
def PROBLEM(ind):
    '''A {object} is an example of what?'''

    obj = ['dog', 'carrot', 'diamond']

    answers = ['Animal', 'Vegatable', 'Mineral']

    given = {'object': obj[ind]}

    return {'answer': answers[ind], 'given': given}

It should be noted in the example above that three different versions of the questions may be created by simply changing the ind variable to a value of zero, one, or two. Units may also be used in the PROBLEM() function. An example of utilizing units is shown below.

import teepy

def PROBLEM(ind):
    '''If points A, B, and C lie along a straight line in that order,
and the distance between point A and B is $ {L1} $, and the distance
between point B and C is $ {L2} $, what is the distance between point
A and C?'''

    L1s = [1, 2, 3]
    L2s = [4, 5, 6]

    L1 = teepy.define_unit(L1s[ind], 'ft')
    L2 = teepy.define_unit(L2s[ind], 'cm')

    L = L1 + L2

    answer = L.to('m')
    given = {'L1': L1,
             'L2': L2}

    return {'answer': answer, 'given': given}

There are a few things to note about the example above. If a given variable has units, the rendered version of the variable (i.e., what is in the problem statement) needs to be enclosed in dollar signs. The units of a given variable get converted into LaTeX. LaTeX code that is not enclosed in dollar signs will not be rendered as LaTeX. The TEEPy function define_unit may be used to assign units to a variable. This function is pint’s Q_ function (please refer to pint’s documentation on how to use it). Once units have been assigned to a variable, calculations performed with those variables will automatically perform the necessary conversions when dealing with different types of units.

The CHOICES() function must return a None value, or a dictionary containing the key choices. No multiple-choice choices will be displayed if CHOICES() returns a None value. An open-ended question is typically when this is needed. Below is an example of a CHOICES() function that returns a None value.

def CHOICES(ind):

    return None

If multiple-choice answers are provided, the CHOICES() function should return a dictionary containing the key choices. The value of this key should be a list containing the correct answer and wrong answers. In other words, it should include everything that is to be listed as answer choices in the question. An example of using the choices key-value pair is shown below.

import teepy

def CHOICES(ind):
    choices = teepy.get_answers(PROBLEM(ind))

    choices.extend(['Red',
                    'Green',
                    'Yellow',
                    'Orange'])

    random.shuffle(choices)

    return {'choices': choices}

The example above also illustrates the use of a TEEPy function called get_answers(). The function takes one argument of a PROBLEMS() function with the particular index value that is to used. The function always returns a list even if the answer to the problem is a single value answer. The example above also demonstrates the use of Python’s built-in module random. random has many useful methods but the one here shuffles a list. The list of choices does not have to be rearranged. An example of not mixing the list of options is shown below.

import teepy

def CHOICES(ind):
    choices = [1, 2]
    choices.extend(teepy.get_answers(PROBLEM(ind)))

    return {'choices': choices}

There are a couple of things worth mentioning about the CHOICES() function when an answer has units. When an answer has units, TEEPy has the function generate_choices() available to generate randomized choices. The function takes three arguments; the number of choices, the correct answer, and the step size between choices. The CHOICES() function must also have a key choice_format in the dictionary it returns. The value of this key is the desired format type of the answer choices. An example of using the generate_choices() function and the choice_format key is shown below.

import teepy

def CHOICES(ind):
    N = 10
    choice_format = '{:0.3f}'
    step = random.uniform(0.01, 0.05)
    ans = teepy.get_answers(PROBLEM(ind))

    choices = teepy.generate_choices(N, ans, step)

    return {'choices': choices, 'choice_format': choice_format}

All of the examples seen above may be found in the examples directory.

Assessment Creator (AC)

An assessment creator (AC) is an individual that takes questions created by QCs and arranges them to form an assessment (e.g., quiz, exam, etc.). The AC will design the assessment layout and set the question point values. To do so, an AC will initialize a teepy.begin() class. This class will be the handle for structuring the assessment content. When initializing the class, two arguments are typically used; n_forms and n_inds. n_forms sets the number of randomized forms to generate. This value is typically the number of students taking the assessment. The n_inds argument sets the number of versions each question has. Currently, TEEPy requires the number of versions a question has to be the same between different questions (i.e., if question A has three different versions, question B must also have three different versions). The exception to this rule is if the question’s PROBLEM() and CHOICES() function do not utilize the ind variable (as in the case of the open-ended question shown previously). An example of initializing the TEEPy begin class is shown below.

import teepy

exam = teepy.begin(n_forms = 5, n_inds = 3)

In the example above, the TEEPy begin class will generate five randomized assessments where each question in the assessment has three different versions. The begin class has a method called HTML that allows the addition of arbitrary HTML content to be added to the assessment. An example of using the HTML is shown below.

exam.HTML('''<div style="text-align: center;">
<h1>COURSE NAME</h1><br>
<h2>ASSESMENT NAME</h2><br>
<h3>DATE</h3><br><br>
<h3>Form Number: ''' + exam.form_number() + '''</h3><br><br>
<h4>Printed Name: ________________________________________ </h4>
</div>''')

In the example above, the method form_number is implemented. This method inserts the randomly generated assessment form number. It is always important to include this on an assessment. Otherwise, assessment forms may not be distinguished from each other. Other methods are exposed in the begin class. An example of the new_page and problem methods are shown below.

exam.new_page()

exam.problem('path/to/question/file', 5)

The new_page method inserts a page break at its placement. The problem method is how question files are added to an assessment. The problem method requires two arguments; the path to the question file and the question’s point value if a correct answer is given. The problem method also can accept the keyword arguments of display_worth, min_height, and pts_incorrect. display_worth is a Boolean that sets whether the question’s point value should be displayed or not. min_height sets the minimum height of a question. The value supplied to min_height gets translated into the CSS property of min-height using units of inches (e.g., min_height = 1.5 means the minimum height of the problem will be 1.5 inches). This keyword argument is helpful in questions that require a certain amount of paper space for a student’s written computation. The pts_incorrect keyword argument indicates that an incorrect answer should result in a deduction of points, not just the failure to earn points. An example of utilizing the various keyword arguments of the method problem is shown below.

exam.problem('path/to/question/file', 0,
             display_worth = False,
             min_height = 2.5,
             pts_incorrect = -2)

The final method exposed in the TEEPy begin class is the section method. This method allows for the grouping of content in the assessment. An example of its use is shown below.

concept_section = exam.section(shuffle = True)

concept_section.problem('path/to/concept/question1', 3)
concept_section.problem('path/to/concept/quesiton2', 3)

exam.section(concept_section)

The order of content in a section may be randomized by setting the keyword argument shuffle to True. The final step in creating an assessment is to generate the forms. The assessment forms are generated by invoking the generate method of the TEEPy begin class. Utilizing the generate method is shown below.

exam.generate()

Whenever the generate method is invoked, the number of assessment forms are generated, also well as n_inds reference forms (which are assessment forms with all shuffling disabled and with the correct answers marked), and an Excel sheet containing assessment key information (i.e., correct answers for a particular form, etc.). More examples of different functionality of TEEPy can be found in the examples directory.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

teepy-0.3.8.tar.gz (56.5 kB view details)

Uploaded Source

File details

Details for the file teepy-0.3.8.tar.gz.

File metadata

  • Download URL: teepy-0.3.8.tar.gz
  • Upload date:
  • Size: 56.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/3.7.0 importlib_metadata/4.8.2 pkginfo/1.8.2 requests/2.26.0 requests-toolbelt/0.9.1 tqdm/4.62.3 CPython/3.9.6

File hashes

Hashes for teepy-0.3.8.tar.gz
Algorithm Hash digest
SHA256 7652b9f4069e2778e9dca3a46c1b120dab75d628a1521bf8ea332f9bb61d395b
MD5 c50df58fe5d1aa330b40b469c95e95f9
BLAKE2b-256 6e176e4230f1dd17b6f9e31d96602471ab0b21cf407eff879fe0eed25a602c86

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page