Python Forum

Full Version: Doctesting a function which prints a students name along with the maximum mark scored
You're currently viewing a stripped down version of our content. View the full version with proper formatting.
How would I go about doctesting this? Output is following for e.g Maths Sam - 98

def Maximum():
    while True:
        print("Maximum marks: Please select a subject, (1) Maths (2) Physics (3) Biology (4) Geo (5) Chemistry (6) Total Marks (7) Menu")
        choice = input(" >> ")
        if choice == '1':
            # dictionary created which maps the students name to their score
            # Math
            mathtodict = dict(zip(Student, Maths_marks))
            maxmath = max(zip(mathtodict.values(), mathtodict.keys()))[1]
            print('Highest maths score: ' + maxmath + ' ' + '-' + ' ' + str(max(Maths_marks)))
        elif choice == '2':
            # Physics
            phystodict = dict(zip(Student, Physics_marks))
            maxphys = max(zip(phystodict.values(), phystodict.keys()))[1]
            print('Highest Physics score: ' + maxphys + ' ' + '-' + ' ' + str(max(Physics_marks)))
        elif choice == '3':
            # Biology
            biotodict = dict(zip(Student, Biology_marks))
            maxbio = max(zip(biotodict.values(), biotodict.keys()))[1]
            print('Highest Biology score: ' + maxbio + ' ' + '-' + ' ' + str(max(Biology_marks)))
        elif choice == '4':
            # Geography
            geogtodict = dict(zip(Student, Geog_marks))
            maxgeog = max(zip(geogtodict.values(), geogtodict.keys()))[1]
            print('Highest Geography score: ' + maxgeog + ' ' + '-' + ' ' + str(max(Geog_marks)))
        elif choice == '5':
            # Chemistry
            chemtodict = dict(zip(Student, Chemistry_marks))
            maxchem = max(zip(chemtodict.values(), chemtodict.keys()))[1]
            print('Highest Chemistry score: ' + maxchem + ' ' + '-' + ' ' + str(max(Chemistry_marks)))
        elif choice == '6':
            # calculates highest total marks scored across all subjects
            max_marks = 0
            for y in Total_marks:
              if y > max_marks:
                max_marks = y
                totaltodict = dict(zip(Student, Total_marks))
                totmax = max(zip(totaltodict.values(), totaltodict.keys()))[1]
            print('Highest total score: ' + totmax + ' ' + '-' + ' ' + str(max_marks))
        elif choice == '7':
            # exits the program and asks user if they want to return to menu or quit the program
            break

        else:
            print("Choice not found. Please choose again.")
            continue
Convert it to a more useful function that takes input as arguments instead of using the input function and have it return a value instead of printing. Most functions should not use stdio.
Yes, so called pure functions are easier to test.
Do you know how I could refactor this code to do that?

(Jan-26-2022, 11:58 AM)sean1 Wrote: [ -> ]How would I go about doctesting this? Output is following for e.g Maths Sam - 98

def Maximum():
    while True:
        print("Maximum marks: Please select a subject, (1) Maths (2) Physics (3) Biology (4) Geo (5) Chemistry (6) Total Marks (7) Menu")
        choice = input(" >> ")
        if choice == '1':
            # dictionary created which maps the students name to their score
            # Math
            mathtodict = dict(zip(Student, Maths_marks))
            maxmath = max(zip(mathtodict.values(), mathtodict.keys()))[1]
            print('Highest maths score: ' + maxmath + ' ' + '-' + ' ' + str(max(Maths_marks)))
        elif choice == '2':
            # Physics
            phystodict = dict(zip(Student, Physics_marks))
            maxphys = max(zip(phystodict.values(), phystodict.keys()))[1]
            print('Highest Physics score: ' + maxphys + ' ' + '-' + ' ' + str(max(Physics_marks)))
        elif choice == '3':
            # Biology
            biotodict = dict(zip(Student, Biology_marks))
            maxbio = max(zip(biotodict.values(), biotodict.keys()))[1]
            print('Highest Biology score: ' + maxbio + ' ' + '-' + ' ' + str(max(Biology_marks)))
        elif choice == '4':
            # Geography
            geogtodict = dict(zip(Student, Geog_marks))
            maxgeog = max(zip(geogtodict.values(), geogtodict.keys()))[1]
            print('Highest Geography score: ' + maxgeog + ' ' + '-' + ' ' + str(max(Geog_marks)))
        elif choice == '5':
            # Chemistry
            chemtodict = dict(zip(Student, Chemistry_marks))
            maxchem = max(zip(chemtodict.values(), chemtodict.keys()))[1]
            print('Highest Chemistry score: ' + maxchem + ' ' + '-' + ' ' + str(max(Chemistry_marks)))
        elif choice == '6':
            # calculates highest total marks scored across all subjects
            max_marks = 0
            for y in Total_marks:
              if y > max_marks:
                max_marks = y
                totaltodict = dict(zip(Student, Total_marks))
                totmax = max(zip(totaltodict.values(), totaltodict.keys()))[1]
            print('Highest total score: ' + totmax + ' ' + '-' + ' ' + str(max_marks))
        elif choice == '7':
            # exits the program and asks user if they want to return to menu or quit the program
            break

        else:
            print("Choice not found. Please choose again.")
            continue
I think I would write 3 functions: max_mark(marks, students), total_marks(marks) and get_input(). max_marks does what your current function does, except it does not loop, it does not ask for input, and it returns a tuple (student, score). Since choice 6 is completely different than choices 1-5 I would write that as a separate function instead of messing up max_mark() trying to make it do two different things.

get_input() displays a prompt, gets user input, VERIFIES USER INPUT, and returns user input. I would write it to return something more friendly than 1, 2, 3... Perhaps "Math", "Physics"..."Total", "Exit". I would probably make an enum for this, but that is overkill for a homework assignment.

The loop gets moved to the body of the script.
while True:
    choice = user_input()
    if choice == "Exit":
        break
    elif choice == "Total":
        totmax, max_marks = total_marks(Total_marks)
        print(f'Highest total score: {totmax} - {max_marks}')
    else:
        high_mark, student = max_marks(total_marks[choice], Students)
        print(f'Highest {choice} score: {student} - {high_mark}')
This makes the assumption that "total_marks" is a dictionary of all of the marks and that the keys are "Math", "Physics"...

Now that max_marks does not take input and returns output you can write a doctest. You also have a function that can be used by other scripts/programs.
Where are you getting your data from? Excel? MySQL?

Assume Excel, then I would have an Excel file with a sheet for each subject and one sheet for totals. Each sheet would have the columns:

student_number, name, score1, score2, .... , scoreX

No two student numbers can be the same, so they are safer than names.

Hope you don't have millions of data!

import openpyxl

path2file = '/home/pedro/temp/'
scoresXL = 'getmax.xlsx'
sourceFile = openpyxl.load_workbook(path2file + scoresXL)
sourceSheetNames = sourceFile.sheetnames

for sheet in sourceSheetNames:
    print('Which maximum do you want?', sheet)

mymax = input('Copy and paste one of the above sheet names here ... ')

# maybe you want to change this for equally high scores or add the name
def getData(sheet):
    high_score = 0
    maxRow = sourceFile[sheet].max_row + 1 # + 1 makes sure you get the last row
    maxCol = sourceFile[sheet].max_column + 1 # ditto
    for rowNum in range(2, maxRow):
        for col in range(3, maxCol):           
            score = sourceFile[sheet].cell(row=rowNum, column=col).value
            if score > high_score:
                high_score = score
                studinr = sourceFile[sheet].cell(row=rowNum, column=1).value
    return (studinr, high_score)

tup = getData(mymax)
print('The student with student number', tup[0], 'achieved', tup[1], 'in', mymax)