Capstone: Grade Report

Capstone: Grade Report

Build a program that reads student scores from a file and produces a report.

The input file has one student per line, name and score separated by a comma:

Ada,92
Grace,85
Alan,58

Exercise

Define four functions.

  1. read_scores(path) — reads the file and returns a dictionary mapping each name to their score as an int. Skip blank lines.
  2. grade_letter(score) — returns "A" for 90 and above, "B" for 80–89, "C" for 70–79, "D" for 60–69, "F" below 60. This is the function from the conditionals section — write it again from memory.
  3. class_average(scores) — takes the dictionary and returns the average score. Assume at least one student.
  4. report_lines(scores) — returns a list of strings, one per student, sorted by name, each in the form:

<name>: <score> (<letter>)

For the file above, report_lines(read_scores(path)) returns:

['Ada: 92 (A)', 'Alan: 58 (F)', 'Grace: 85 (B)']

and class_average returns 78.33....

Run your code to see the output, then press Submit.

Hints

  • In read_scores, split each line on ",", strip the pieces, and convert the score with int.
  • class_average sums scores.values() and divides by len(scores).
  • report_lines calls grade_letter for each student — build it after the others work.

Tests

import unittest


class TestGradeReport(unittest.TestCase):
    def setUp(self):
        with open("test_scores.txt", "w") as file:
            file.write("Ada,92\nGrace,85\n\nAlan,58\n")

    def test_read_scores_parses_names_and_ints(self):
        self.assertEqual(
            read_scores("test_scores.txt"),
            {"Ada": 92, "Grace": 85, "Alan": 58},
        )

    def test_grade_letter_boundaries(self):
        self.assertEqual(grade_letter(90), "A")
        self.assertEqual(grade_letter(89), "B")
        self.assertEqual(grade_letter(70), "C")
        self.assertEqual(grade_letter(65), "D")
        self.assertEqual(grade_letter(59), "F")

    def test_class_average(self):
        self.assertAlmostEqual(class_average({"Ada": 92, "Grace": 85, "Alan": 58}), 78.33333333333333)

    def test_report_lines_sorted_and_formatted(self):
        scores = read_scores("test_scores.txt")
        self.assertEqual(
            report_lines(scores),
            ["Ada: 92 (A)", "Alan: 58 (F)", "Grace: 85 (B)"],
        )

    def test_single_student_average(self):
        self.assertAlmostEqual(class_average({"Ada": 80}), 80.0)
def read_scores(path):
    scores = {}
    with open(path) as file:
        for line in file:
            if line.strip() == "":
                continue
            name, score = line.strip().split(",")
            scores[name.strip()] = int(score.strip())
    return scores


def grade_letter(score):
    if score >= 90:
        return "A"
    elif score >= 80:
        return "B"
    elif score >= 70:
        return "C"
    elif score >= 60:
        return "D"
    else:
        return "F"


def class_average(scores):
    return sum(scores.values()) / len(scores)


def report_lines(scores):
    lines = []
    for name in sorted(scores):
        score = scores[name]
        lines.append(f"{name}: {score} ({grade_letter(score)})")
    return lines


with open("scores.txt", "w") as file:
    file.write("Ada,92\nGrace,85\nAlan,58\n")

scores = read_scores("scores.txt")
for line in report_lines(scores):
    print(line)
print(class_average(scores))
Solution hidden. Give it a real try first.

Sign in to join the discussion.

No comments yet. Be the first to ask a question.

main.py
Console
Press Run to execute your code, or Submit to test and complete this problem.