{
  "id": "P005",
  "slug": "gradebook",
  "key": "P005-gradebook",
  "title": "Gradebook",
  "summary": "Students and their scores in three subjects: report cards, a ranking where equal averages share a place, pass or fail, and the class average and median, all worked out exactly - from a text menu.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P005 - Gradebook\n\nKeeps students and their scores in three subjects - math, english and\nscience - and shows a report card for any student, a ranking, and a class\nsummary: the class average, the median, how many pass, and each subject's\naverage. A text menu; the scores live while the program runs.\n\n- `main.eml` - the menu loop and the questions asked, with their checks\n- `grades.eml` - the arithmetic: averages, the pass mark, the ranking and\n  shared places, the class average, the median and the subject averages\n- `report.eml` - what the screen shows: the menu, the report card, the\n  ranking and the summary\n- `text.eml` - trimming, lower case and reading a whole number\n\nEverything is worked out exactly. An average of up to three scores is a whole\nnumber of sixths, so averages are compared, ranked and held against the pass\nmark (60) as whole numbers; they become tenths, rounded half up, only to be\nshown. Equal averages share a place and the next place is skipped (1, 1, 3),\nwith equal averages listed in name order. The median of an even number of\nstudents is the mean of the two in the middle.\n\nWhat is checked: a name is not empty, has at most 20 characters and is not\nalready there in any case; a subject is one of the numbers offered; a score is\na whole number from 0 to 100. Entering a score again replaces it and says\nwhat it was.\n\nSessions: `sessions/basic.in` adds four students, enters their scores (one\nstudent has no science score, one score is replaced), shows two report cards,\nthe ranking - two students share first place - and the class summary;\n`sessions/bad-input.in` asks for the ranking and the summary with no scores,\ntypes an empty, a too-long and a repeated name, ids and subjects that are not\nthere, and scores that are words, too high, negative, not whole or empty,\nthen ranks a class where one student has no scores yet.\n\nBuilt on the verified corpus cases `exam-score-records` (students' scores\nkept and averaged) and `simple-quiz-grader` (scores held against a pass mark).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P005 gradebook: students, their scores in three subjects, report cards, a\n# ranking and a class summary. The scores live while the program runs; the\n# first twenty projects do not save to files.\nimport grades\nimport report\nimport text\n\ndef ask_student(students):\n    # The student whose id is typed, or None once the problem has been shown.\n    text.number(text.trim(input(\"student id> \"))) => n\n    for s in students:\n        if s[0] == n:\n            return s\n    \"No student with that id.\" ^0\n    return None\n\ndef ask_subject():\n    # The index of the subject picked, or -1 once the problem has been shown.\n    \"\" => choices\n    for k in [0:len(grades.subjects) - 1]:\n        if choices != \"\":\n            choices + \"  \" => choices\n        choices + str(k + 1) + \") \" + grades.subjects[k] => choices\n    choices ^0\n    text.number(text.trim(input(\"subject> \"))) => k\n    if k < 1 or k > len(grades.subjects):\n        (\"Pick a subject from 1 to \" + str(len(grades.subjects)) + \".\") ^0\n        return -1\n    return k - 1\n\ndef ask_score():\n    # A whole score from 0 to 100, or -1 once the problem has been shown.\n    text.number(text.trim(input(\"score> \"))) => score\n    if score < 0 or score > 100:\n        \"A score is a whole number from 0 to 100.\" ^0\n        return -1\n    return score\n\ndef name_problem(students, name):\n    # What is wrong with name for a new student, or \"\" if nothing is.\n    if name == \"\":\n        return \"A student needs a name.\"\n    if len(name) > 20:\n        return \"A name has at most 20 characters.\"\n    for s in students:\n        if text.lower(s[1]) == text.lower(name):\n            return \"There is already a student named \" + s[1] + \".\"\n    return \"\"\n\n[] => students\n1 => next_id\nTrue => running\nwhile running:\n    report.menu(students)\n    text.trim(input(\"choice> \")) => choice\n    if choice == \"1\":\n        text.trim(input(\"name> \")) => name\n        name_problem(students, name) => problem\n        if problem != \"\":\n            problem ^0\n        else:\n            students + [[next_id, name, [None for x in grades.subjects]]] => students\n            (\"Added #\" + str(next_id) + \": \" + name + \".\") ^0\n            next_id + 1 => next_id\n    elif choice == \"2\":\n        ask_student(students) => s\n        if s != None:\n            ask_subject() => k\n            if k >= 0:\n                ask_score() => score\n                if score >= 0:\n                    s[2][k] => old\n                    score => s[2][k]\n                    (grades.subjects[k] + \" for \" + s[1] + \": \" + str(score)) => said\n                    if old != None:\n                        said + \" (was \" + str(old) + \")\" => said\n                    (said + \".\") ^0\n    elif choice == \"3\":\n        ask_student(students) => s\n        if s != None:\n            report.card(s)\n    elif choice == \"4\":\n        report.ranking(students)\n    elif choice == \"5\":\n        report.summary(students)\n    elif choice == \"6\":\n        False => running\n    else:\n        \"Pick a number from 1 to 6.\" ^0\n\"Bye.\" ^0\n",
      "python": "import grades\nimport report\nimport text\n\ndef ask_student(students):\n    n = text.number(text.trim(input(\"student id> \")))\n    for s in students:\n        if s[0] == n:\n            return s\n    print(\"No student with that id.\")\n    return None\n\ndef ask_subject():\n    choices = \"\"\n    for k in range(0, len(grades.subjects)):\n        if choices != \"\":\n            choices = choices + \"  \"\n        choices = choices + str(k + 1) + \") \" + grades.subjects[k]\n    print(choices)\n    k = text.number(text.trim(input(\"subject> \")))\n    if k < 1 or k > len(grades.subjects):\n        print(\"Pick a subject from 1 to \" + str(len(grades.subjects)) + \".\")\n        return -1\n    return k - 1\n\ndef ask_score():\n    score = text.number(text.trim(input(\"score> \")))\n    if score < 0 or score > 100:\n        print(\"A score is a whole number from 0 to 100.\")\n        return -1\n    return score\n\ndef name_problem(students, name):\n    if name == \"\":\n        return \"A student needs a name.\"\n    if len(name) > 20:\n        return \"A name has at most 20 characters.\"\n    for s in students:\n        if text.lower(s[1]) == text.lower(name):\n            return \"There is already a student named \" + s[1] + \".\"\n    return \"\"\n\nstudents = []\nnext_id = 1\nrunning = True\nwhile running:\n    report.menu(students)\n    choice = text.trim(input(\"choice> \"))\n    if choice == \"1\":\n        name = text.trim(input(\"name> \"))\n        problem = name_problem(students, name)\n        if problem != \"\":\n            print(problem)\n        else:\n            students = students + [[next_id, name, [None for x in grades.subjects]]]\n            print(\"Added #\" + str(next_id) + \": \" + name + \".\")\n            next_id = next_id + 1\n    elif choice == \"2\":\n        s = ask_student(students)\n        if s != None:\n            k = ask_subject()\n            if k >= 0:\n                score = ask_score()\n                if score >= 0:\n                    old = s[2][k]\n                    s[2][k] = score\n                    said = grades.subjects[k] + \" for \" + s[1] + \": \" + str(score)\n                    if old != None:\n                        said = said + \" (was \" + str(old) + \")\"\n                    print(said + \".\")\n    elif choice == \"3\":\n        s = ask_student(students)\n        if s != None:\n            report.card(s)\n    elif choice == \"4\":\n        report.ranking(students)\n    elif choice == \"5\":\n        report.summary(students)\n    elif choice == \"6\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 6.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "grades.eml",
      "eml": "# P005 gradebook - the arithmetic. A student is a list [id, name, scores]:\n# scores has one place per subject, a whole number from 0 to 100 or None.\n#\n# Everything here is exact. An average of up to three scores is a whole\n# number of SIXTHS (6 is the smallest number that 1, 2 and 3 all divide), so\n# averages are compared, ranked and tested against the pass mark as whole\n# numbers, and only turned into tenths, rounded half up, to be shown.\nimport text\n\n[\"math\", \"english\", \"science\"] => subjects\n6 => sixths\n60 => pass_mark\n\ndef div(a, b):\n    # a // b for whole numbers, b > 0.\n    return int((a - a % b) / b)\n\ndef entered(student):\n    return [s for s in student[2] if s != None]\n\ndef average6(student):\n    # The average score in sixths, or -1 when there is no score yet.\n    entered(student) => scores\n    if len(scores) == 0:\n        return -1\n    return div(sum(scores) * sixths, len(scores))\n\ndef passes(student):\n    return average6(student) >= pass_mark * sixths\n\ndef tenths(v, d):\n    # v / d in tenths, rounded half up (v >= 0, d > 0).\n    return div(20 * v + d, 2 * d)\n\ndef shown(t):\n    # Tenths as 85.0.\n    return str(div(t, 10)) + \".\" + str(t % 10)\n\ndef ranked(students):\n    # The students who have scores, highest average first; equal averages in\n    # name order. Each goes after every student already placed who is ahead\n    # of it or level with it.\n    [] => order\n    for s in students:\n        average6(s) => a\n        if a >= 0:\n            0 => i\n            while i < len(order) and (average6(order[i]) > a or (average6(order[i]) == a and text.lower(order[i][1]) <= text.lower(s[1]))):\n                i + 1 => i\n            order[0:i] + [s] + order[i:len(order)] => order\n    return order\n\ndef place(order, student):\n    # 1 + the number of students with a strictly higher average, so equal\n    # averages share a place and the next place is skipped: 1, 1, 3.\n    1 => p\n    for s in order:\n        if average6(s) > average6(student):\n            p + 1 => p\n    return p\n\ndef class_average(order):\n    # The mean of the students' averages, in tenths.\n    0 => total6\n    for s in order:\n        total6 + average6(s) => total6\n    return tenths(total6, sixths * len(order))\n\ndef median(order):\n    # The middle average in tenths; with an even number of students, the mean\n    # of the two in the middle. order is highest first, which gives the same\n    # middle as lowest first.\n    len(order) => n\n    div(n, 2) => m\n    if n % 2 == 1:\n        return tenths(average6(order[m]), sixths)\n    return tenths(average6(order[m - 1]) + average6(order[m]), 2 * sixths)\n\ndef subject_average(students, k):\n    # [average in tenths, number of scores] for subject k; [0, 0] if none.\n    [s[2][k] for s in students if s[2][k] != None] => scores\n    if len(scores) == 0:\n        return [0, 0]\n    return [tenths(sum(scores), len(scores)), len(scores)]\n",
      "python": "import text\nsubjects = [\"math\", \"english\", \"science\"]\nsixths = 6\npass_mark = 60\n\ndef div(a, b):\n    return int((a - a % b) / b)\n\ndef entered(student):\n    return [s for s in student[2] if s != None]\n\ndef average6(student):\n    scores = entered(student)\n    if len(scores) == 0:\n        return -1\n    return div(sum(scores) * sixths, len(scores))\n\ndef passes(student):\n    return average6(student) >= pass_mark * sixths\n\ndef tenths(v, d):\n    return div(20 * v + d, 2 * d)\n\ndef shown(t):\n    return str(div(t, 10)) + \".\" + str(t % 10)\n\ndef ranked(students):\n    order = []\n    for s in students:\n        a = average6(s)\n        if a >= 0:\n            i = 0\n            while i < len(order) and (average6(order[i]) > a or average6(order[i]) == a and text.lower(order[i][1]) <= text.lower(s[1])):\n                i = i + 1\n            order = order[0:i] + [s] + order[i:len(order)]\n    return order\n\ndef place(order, student):\n    p = 1\n    for s in order:\n        if average6(s) > average6(student):\n            p = p + 1\n    return p\n\ndef class_average(order):\n    total6 = 0\n    for s in order:\n        total6 = total6 + average6(s)\n    return tenths(total6, sixths * len(order))\n\ndef median(order):\n    n = len(order)\n    m = div(n, 2)\n    if n % 2 == 1:\n        return tenths(average6(order[m]), sixths)\n    return tenths(average6(order[m - 1]) + average6(order[m]), 2 * sixths)\n\ndef subject_average(students, k):\n    scores = [s[2][k] for s in students if s[2][k] != None]\n    if len(scores) == 0:\n        return [0, 0]\n    return [tenths(sum(scores), len(scores)), len(scores)]\n"
    },
    {
      "name": "report.eml",
      "eml": "# P005 gradebook - what the screen shows: the menu, a report card, the\n# ranking and the class summary.\nimport grades\n\ndef plural(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef menu(students):\n    \"\" ^0\n    (\"== Gradebook: \" + plural(len(students), \"student\") + \" ==\") ^0\n    \"1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\" ^0\n\ndef verdict(student):\n    if grades.passes(student):\n        return \"pass\"\n    return \"FAIL\"\n\ndef average_shown(student):\n    return grades.shown(grades.tenths(grades.average6(student), grades.sixths))\n\ndef card(student):\n    \"\" ^0\n    (\"-- report card: \" + student[1] + \" --\") ^0\n    for k in [0:len(grades.subjects) - 1]:\n        student[2][k] => score\n        (\"  \" + (\"%-9s\" % grades.subjects[k])) => line\n        if score == None:\n            line + \"    -\" => line\n        else:\n            line + (\"%5d\" % score) => line\n            if score < grades.pass_mark:\n                line + \"  below \" + str(grades.pass_mark) => line\n        line ^0\n    if grades.average6(student) < 0:\n        \"  (no scores yet)\" ^0\n    else:\n        (\"  \" + (\"%-9s\" % \"average\") + (\"%5s\" % average_shown(student)) + \"  \" + verdict(student)) ^0\n\ndef ranking(students):\n    grades.ranked(students) => order\n    \"\" ^0\n    \"-- ranking --\" ^0\n    if len(order) == 0:\n        \"  No scores yet.\" ^0\n        return None\n    for s in order:\n        (\"  \" + (\"%3d\" % grades.place(order, s)) + \"  \" + (\"%-20s\" % s[1]) + (\"%6s\" % average_shown(s)) + \"  \" + verdict(s)) ^0\n    for s in students:\n        if grades.average6(s) < 0:\n            (\"    -  \" + (\"%-20s\" % s[1]) + \"  no scores yet\") ^0\n\ndef summary(students):\n    grades.ranked(students) => order\n    \"\" ^0\n    \"-- class summary --\" ^0\n    if len(order) == 0:\n        \"  No scores yet.\" ^0\n        return None\n    (\"  students with scores  \" + str(len(order)) + \" of \" + str(len(students))) ^0\n    (\"  class average         \" + grades.shown(grades.class_average(order))) ^0\n    (\"  median                \" + grades.shown(grades.median(order))) ^0\n    (\"  passing               \" + str(len([s for s in order if grades.passes(s)])) + \" of \" + str(len(order))) ^0\n    for k in [0:len(grades.subjects) - 1]:\n        grades.subject_average(students, k) => sa\n        (\"  \" + (\"%-22s\" % (grades.subjects[k] + \" average\"))) => line\n        if sa[1] == 0:\n            line + \"-\" => line\n        else:\n            line + grades.shown(sa[0]) + \"  (\" + plural(sa[1], \"score\") + \")\" => line\n        line ^0\n",
      "python": "import grades\n\ndef plural(n, word):\n    if n == 1:\n        return \"1 \" + word\n    return str(n) + \" \" + word + \"s\"\n\ndef menu(students):\n    print(\"\")\n    print(\"== Gradebook: \" + plural(len(students), \"student\") + \" ==\")\n    print(\"1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\")\n\ndef verdict(student):\n    if grades.passes(student):\n        return \"pass\"\n    return \"FAIL\"\n\ndef average_shown(student):\n    return grades.shown(grades.tenths(grades.average6(student), grades.sixths))\n\ndef card(student):\n    print(\"\")\n    print(\"-- report card: \" + student[1] + \" --\")\n    for k in range(0, len(grades.subjects)):\n        score = student[2][k]\n        line = \"  \" + \"%-9s\" % grades.subjects[k]\n        if score == None:\n            line = line + \"    -\"\n        else:\n            line = line + \"%5d\" % score\n            if score < grades.pass_mark:\n                line = line + \"  below \" + str(grades.pass_mark)\n        print(line)\n    if grades.average6(student) < 0:\n        print(\"  (no scores yet)\")\n    else:\n        print(\"  \" + \"%-9s\" % \"average\" + \"%5s\" % average_shown(student) + \"  \" + verdict(student))\n\ndef ranking(students):\n    order = grades.ranked(students)\n    print(\"\")\n    print(\"-- ranking --\")\n    if len(order) == 0:\n        print(\"  No scores yet.\")\n        return None\n    for s in order:\n        print(\"  \" + \"%3d\" % grades.place(order, s) + \"  \" + \"%-20s\" % s[1] + \"%6s\" % average_shown(s) + \"  \" + verdict(s))\n    for s in students:\n        if grades.average6(s) < 0:\n            print(\"    -  \" + \"%-20s\" % s[1] + \"  no scores yet\")\n\ndef summary(students):\n    order = grades.ranked(students)\n    print(\"\")\n    print(\"-- class summary --\")\n    if len(order) == 0:\n        print(\"  No scores yet.\")\n        return None\n    print(\"  students with scores  \" + str(len(order)) + \" of \" + str(len(students)))\n    print(\"  class average         \" + grades.shown(grades.class_average(order)))\n    print(\"  median                \" + grades.shown(grades.median(order)))\n    print(\"  passing               \" + str(len([s for s in order if grades.passes(s)])) + \" of \" + str(len(order)))\n    for k in range(0, len(grades.subjects)):\n        sa = grades.subject_average(students, k)\n        line = \"  \" + \"%-22s\" % (grades.subjects[k] + \" average\")\n        if sa[1] == 0:\n            line = line + \"-\"\n        else:\n            line = line + grades.shown(sa[0]) + \"  (\" + plural(sa[1], \"score\") + \")\"\n        print(line)\n"
    },
    {
      "name": "text.eml",
      "eml": "# P005 gradebook - text helpers. The interpreter that checks every session\n# does not run string methods yet, so these are written out.\n\ndef trim(s):\n    # s without the spaces at either end.\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef lower(s):\n    # s with A-Z turned into a-z; everything else as it was.\n    \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\" => upper_letters\n    \"abcdefghijklmnopqrstuvwxyz\" => lower_letters\n    \"\" => out\n    for c in s:\n        c => d\n        for i in [0:25]:\n            if upper_letters[i] == c:\n                lower_letters[i] => d\n        out + d => out\n    return out\n\ndef number(s):\n    # The value of s if it is digits only (at least one), otherwise -1.\n    if s == \"\":\n        return -1\n    0 => n\n    for c in s:\n        if not (c in \"0123456789\"):\n            return -1\n        n * 10 + int(c) => n\n    return n\n",
      "python": "def trim(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef lower(s):\n    upper_letters = \"ABCDEFGHIJKLMNOPQRSTUVWXYZ\"\n    lower_letters = \"abcdefghijklmnopqrstuvwxyz\"\n    out = \"\"\n    for c in s:\n        d = c\n        for i in range(0, 26):\n            if upper_letters[i] == c:\n                d = lower_letters[i]\n        out = out + d\n    return out\n\ndef number(s):\n    if s == \"\":\n        return -1\n    n = 0\n    for c in s:\n        if not c in \"0123456789\":\n            return -1\n        n = n * 10 + int(c)\n    return n\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "9\n4\n5\n1\n\n1\nAlexandra Montgomery-Smith\n1\nAnn\n1\nANN\n2\n7\n2\nx\n2\n1\n4\n2\n1\nx\n2\n1\n1\nabc\n2\n1\n1\n101\n2\n1\n1\n-5\n2\n1\n1\n7.5\n2\n1\n1\n\n3\n1\n4\n2\n1\n1\n100\n1\nBo\n4\n6\n",
      "screen": "\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 9\nPick a number from 1 to 6.\n\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 4\n\n-- ranking --\n  No scores yet.\n\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 5\n\n-- class summary --\n  No scores yet.\n\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> \nA student needs a name.\n\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Alexandra Montgomery-Smith\nA name has at most 20 characters.\n\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Ann\nAdded #1: Ann.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> ANN\nThere is already a student named Ann.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 7\nNo student with that id.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> x\nNo student with that id.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 4\nPick a subject from 1 to 3.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> x\nPick a subject from 1 to 3.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> abc\nA score is a whole number from 0 to 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> 101\nA score is a whole number from 0 to 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> -5\nA score is a whole number from 0 to 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> 7.5\nA score is a whole number from 0 to 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> \nA score is a whole number from 0 to 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 3\nstudent id> 1\n\n-- report card: Ann --\n  math         -\n  english      -\n  science      -\n  (no scores yet)\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 4\n\n-- ranking --\n  No scores yet.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> 100\nmath for Ann: 100.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Bo\nAdded #2: Bo.\n\n== Gradebook: 2 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 4\n\n-- ranking --\n    1  Ann                  100.0  pass\n    -  Bo                    no scores yet\n\n== Gradebook: 2 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\nAnn\n1\nBen\n1\nCara\n1\nDev\n2\n1\n1\n90\n2\n1\n2\n80\n2\n1\n3\n85\n2\n2\n1\n70\n2\n2\n2\n95\n2\n2\n3\n90\n2\n3\n1\n55\n2\n3\n2\n58\n2\n3\n2\n60\n2\n4\n1\n72\n2\n4\n2\n73\n2\n4\n3\n72\n3\n1\n3\n3\n4\n5\n6\n",
      "screen": "\n== Gradebook: 0 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Ann\nAdded #1: Ann.\n\n== Gradebook: 1 student ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Ben\nAdded #2: Ben.\n\n== Gradebook: 2 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Cara\nAdded #3: Cara.\n\n== Gradebook: 3 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 1\nname> Dev\nAdded #4: Dev.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 1\nscore> 90\nmath for Ann: 90.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 2\nscore> 80\nenglish for Ann: 80.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 1\n1) math  2) english  3) science\nsubject> 3\nscore> 85\nscience for Ann: 85.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 2\n1) math  2) english  3) science\nsubject> 1\nscore> 70\nmath for Ben: 70.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 2\n1) math  2) english  3) science\nsubject> 2\nscore> 95\nenglish for Ben: 95.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 2\n1) math  2) english  3) science\nsubject> 3\nscore> 90\nscience for Ben: 90.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 3\n1) math  2) english  3) science\nsubject> 1\nscore> 55\nmath for Cara: 55.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 3\n1) math  2) english  3) science\nsubject> 2\nscore> 58\nenglish for Cara: 58.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 3\n1) math  2) english  3) science\nsubject> 2\nscore> 60\nenglish for Cara: 60 (was 58).\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 4\n1) math  2) english  3) science\nsubject> 1\nscore> 72\nmath for Dev: 72.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 4\n1) math  2) english  3) science\nsubject> 2\nscore> 73\nenglish for Dev: 73.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 2\nstudent id> 4\n1) math  2) english  3) science\nsubject> 3\nscore> 72\nscience for Dev: 72.\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 3\nstudent id> 1\n\n-- report card: Ann --\n  math        90\n  english     80\n  science     85\n  average   85.0  pass\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 3\nstudent id> 3\n\n-- report card: Cara --\n  math        55  below 60\n  english     60\n  science      -\n  average   57.5  FAIL\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 4\n\n-- ranking --\n    1  Ann                   85.0  pass\n    1  Ben                   85.0  pass\n    3  Dev                   72.3  pass\n    4  Cara                  57.5  FAIL\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 5\n\n-- class summary --\n  students with scores  4 of 4\n  class average         75.0\n  median                78.7\n  passing               3 of 4\n  math average          71.8  (4 scores)\n  english average       77.0  (4 scores)\n  science average       82.3  (3 scores)\n\n== Gradebook: 4 students ==\n1) add student  2) enter score  3) report card  4) ranking  5) class summary  6) quit\nchoice> 6\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "exam-score-records",
      "caseId": "160-exam-score-records",
      "title": "A list of tuples is a table"
    },
    {
      "slug": "simple-quiz-grader",
      "caseId": "041-simple-quiz-grader",
      "title": "Simple quiz grader"
    }
  ],
  "updated": "2026-09-28"
}
