CS303E Homework 10

Instructor: Dr. Bill Young
Due Date: Friday, March 29, 2024 at 11:59pm

Grading a List of Tests

In this assignment you'll write a program to grade a collection of tests. You'll be given several constants that you should copy into your code file. This data is in a file here: HW10 Data. You should download this file and copy the data from there. These include a list of student names, a list containing the correct answers to questions on an exam where the possible answers are 'A', 'B', 'C', 'D', and a list of lists containing the student answers. This list should have the same number of items as the list of student names, and each item should be a list of answers given by the corresponding student.

Note that in these data there are 20 students, 20 student responses, and each test has 20 answers (sort of). However, you should not write your program to depend on this fact; i.e., your program should work no matter how many students took the exam or how many questions were on it. (Hint: you can get that information by looking at the lengths of the appropriate lists.) You can assume that there are the same number of students as student exams; you can't assume that there are the right number of responses on each student's list of responses (see below).

Your task is the write a program that will grade all of the exams and print out a grade report for each of the students. Finally, print the class average at the bottom. See the sample output below. Your output should match this exactly for these data.

There are several situations you should watch out for:

  1. If the list of answers for a student is not of the correct length (either too long or too short), treat that as an error and print an error line in the report. See the examples below.
  2. If a student answer is None, that means that the student didn't answer the question; treat that as an incorrect answer.
  3. If the student answer isn't one of the allowed answers 'A', 'B', 'C', or 'D', just treat that as an incorrect answer.

Here is the list of student names, the correct answers and the list of lists of student answers. Note that the previous list of student names didn't match the sample output. I have now updated the list below.

STUDENT = ['Kevin', 'Joe', 'Nick', 'Michael', 'Randy', 'Jermaine', 'Tito', 'Marlon', 'Jackie', 
           'Jimmy', 'Merrill', 'Alan', 'Jay', 'Wayne', 'Virl', 'Donnie', 'Phil', 'Don', 'Dick', 'Tom' ]

CORRECT =  ['A', 'C', 'C', 'B', 'A', 'A', 'D', 'A', 'C', 'C', 'C', 'B', 'A', 'A', 'C', 'D', 'C', 'A', 'A', 'D']

ANSWERS = [['C',  'C',  'B',  'A',  'B',  'C',  'A',  'D',  'C',  'A',  'D',  'A',  'C',  None,  'C',  'B',  'A',  'B',  'A',  'B'],
           ['B',  'D',  'B',  'B',  'B',  'D',  'B',  'C',  'C',  'A',  'C',  'C',  'D',  'B',  'A',  'B',  'A',  'A' ],
           ['A',  'B',  'C',  'B',  'C',  'D',  'B',  'C',  'C',  'A',  'D',  'C',  'D',  'B',  'A',  'A',  'C',  'A',  'D',  'D'],
           [None, 'A',  None, 'B',  'D',  None, 'C',  'B',  'B',  None, 'A',  'C',  None, 'A',  'A',  'B',  'B',  'D',  'C',  'A'],
           ['D',  'G',  'C',  'D',  'C',  'D',  'A',  'C',  'D',  'D',  'C',  'D',  'A',  'D',  'A',  'D',  'A',  'D',  'C',  'A'],
           ['B',  'C',  'C',  'B',  'C',  'B',  'C',  'C',  'C',  'C',  'D',  'A',  'D',  'A',  'A',  'D',  'D',  'D',  'D',  'A'],
           ['B',  'C',  'C',  'B',  'C',  'D',  'B',  'C',  'C',  'A',  'D',  'A',  'D',  'B',  'A',  'A',  'A',  'D',  'D',  'D'],
           ['D',  'C',  'A',  'B',  'C',  'D',  'B',  'B',  'A',  'A',  'D',  'A',  'D',  'A',  'B',  'A',  'A',  'D',  'D',  'B'],
           ['B',  'C',  'C',  'A',  'C',  'C',  'B',  'C',  'C',  'A',  'D',  'A',  'A',  'B',  'C',  'A',  'A',  'D',  'D',  'D'],
           ['B',  'B',  'B',  'B',  'C',  'D',  'C',  'B',  'C',  'C',  'D',  'A',  'B',  'D',  'A',  'A',  'B',  'D',  'C',  'C'],
           ['B',  'C',  'D',  'D',  'A',  'D',  'D',  'C',  'C',  'C',  'D',  'A',  'A',  'A',  'C',  'A',  'A',  'D',  'A',  'D'],
           ['B',  'C',  'C',  'B',  'C',  'D',  'B',  'C',  'C',  'A',  'D',  'A',  'D',  'B',  'A',  'A',  'A',  'D',  'D',  'D'],
           ['C',  'C',  'C',  'B',  'C',  'D',  'B',  'D',  'B',  'A',  'D',  'D',  'C',  'C',  'A',  'A',  'B',  'C',  'B',  'C'],
           ['A',  'A',  'A',  'D',  'C',  'B',  'B',  'A',  'B',  'D',  'A',  'D',  'C',  'C',  'D',  'D',  'B',  'A',  'D',  'D'],
           ['B',  'C',  'D',  'A',  'C',  'D',  'C',  'C',  'B',  'A',  'D',  'B',  'D',  'B',  'A',  'A',  'A',  'D',  'D',  'D'],
           ['B',  'D',  'C',  'B',  'C',  'C',  'B',  'D',  'C',  'A',  'D',  'A',  'D',  'B',  'A',  'A',  'A',  'D',  'D',  'D'],
           ['D',  'D',  'A',  'B',  'D',  'D',  'B',  'D',  'A',  'A',  'D',  'A',  'C',  'B',  'A',  'D',  'C',  'D',  'A',  'D'],
           ['B',  'C',  'C',  'B',  'C',  'A',  'B',  'A',  'C',  'D',  'C',  'A',  'A',  'A',  'A',  'B',  'A',  'B',  'B',  'B'],
           ['A',  'A',  'C',  'B',  'D',  'D',  'C',  'A',  'C',  'A',  'A',  'D',  'D',  'C',  'D',  'D',  'A',  'A',  'C',  'A'],
           ['D',  'C',  'C',  'C',  'D',  'D',  'B',  'C',  'C',  'A',  'D',  'A',  'D',  'B',  'A',  'C',  'D',  'A',  'D',  'B']]

Expected output:

> python ScoreTests.py 

Student        Grade
--------------------
Kevin      :   20.00
Joe        : Bad format in answer list.
Nick       :   35.00
Michael    :   10.00
Randy      :   20.00
Jermaine   :   35.00
Tito       :   25.00
Marlon     :   15.00
Jackie     :   30.00
Jimmy      :   15.00
Merrill    :   50.00
Alan       :   25.00
Jay        :   15.00
Wayne      :   25.00
Virl       :   15.00
Donnie     :   20.00
Phil       :   25.00
Don        :   45.00
Dick       :   35.00
Tom        :   20.00
--------------------
Average    :   25.26

>
There are blank lines above and below the table. The name is printed in a field of 10; the grade is printed in a field of 7 with two digits after the decimal point. Notice that when computing the average, you should not count lines with a bad format. So even though there were 20 students here, only 19 were counted in the average.

Turning in the Assignment:

The program should be in a file named ScoreTests.py. Submit the file via Canvas before the deadline shown at the top of this page. Submit it to the assignment 10 the assignments sections by uploading your python file.

Your file must compile and run before submission. It must also contain a header with the following format:

# File: ScoreTests.py
# Student: 
# UT EID:
# Course Name: CS303E
# 
# Date:
# Description of Program: 

Programming Tips:

Testing: On a typical large software development project, over 50% of the total effort is testing. About 10 years ago, I recruited and led a team of around 12 UT students, both grad and undergrad, who were the testing team on a project for the U.S. Army porting a number of software applications from a proprietary platform to open source.

When testing a small program, you can usually come up with test data by hand. But when testing a larger program, particularly one that will be used by many people, it's important to be thorough in your testing. For some problems, it is even possible to test exhaustively, meaning to exercise all possible test cases. But that's rare; for even simple problems there are often just too many possibilities. At very least, try to test many different categories of possible inputs, including edge cases, inputs that are near the boundary of legal inputs. For example, if your program expects an input that is non-negative, make sure to test what happens if the input is 0 and what happens if the program is provided an illegal negative input.

It's often useful to write code to generate your test data. This often provides better coverage and may introduce a degree of randomness that will expose errors you might not have thought to test for. Below is some code that I wrote to generate some of the test data for this assignment. I suggest that you study this code and see why it works.

import random
POSSIBLE_ANSWERS = ['A', 'B', 'C', 'D']

def generateCorrectAnswers( count ):
    """ Given a count, generate a random list of length count of answers
        from the possible answers.
    """
    return [ random.choice( POSSIBLE_ANSWERS ) for i in range( count ) ]

def selectTestAnswer( correctAnswer, perc ):
    """ Given a correct answer, generate a possible test answer that is
        the correct answer perc percent of the time;  otherwise, select
        randomly one of the other possible answers. 
    """
    n = random.randint(0, 100)
    if n <= perc:
        ans = correctAnswer
    else:
        ans = random.choice( [ x for x in POSSIBLE_ANSWERS + [None] if x != correctAnswer ] )
    return ans

def generateTestAnswers( correctAnswers, perc ):
    """ This generate a potential set of test answers, where the percent
        correct is perc, given the correct answers.
    """
    return [ selectTestAnswer( correctAnswers[i], perc ) for i in range( len(correctAnswers) ) ]

def generateTestAnswersMultiple( correctAnswers, n ):
    """Generate n rows of test answers, where the percentage correct
    is chosen randomly.
    """
    rows = []
    # I show the percent correct for each student as a sanity check to
    # see if the values being produced are reasonable.
    print("Percents correct: ", end="")
    for i in range( n ):
        # Choose a percent
        perc = random.choice( [20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100] )
        print(perc, end=" ")
        # Generate and add the row to the list of student answer.
        row = generateTestAnswers( correctAnswers, perc )
        rows.append( row )
    print()
    return rows
By the way, I didn't use any code to generate the list of student names. Those are members of the Jonas Brothers, the Jackson brothers, the Osmond brothers, the Everley Brothers, and the Smothers Brothers.