Intermediate Project 2 — Student Gradebook¶
Difficulty: Intermediate · Estimated effort: 12–16 hours · Prerequisites: Module 4 — Encapsulation, Module 5 — Operators and Static Members, Module 14 — The STL with OOP · Combines: validated value types, ordered containers, grading rules that vary, file input
This is one of the designated integration projects. It must use, explicitly and in writing: multiple classes · relationships between classes · encapsulation · inheritance or composition · polymorphism · the STL · exception handling.
No complete solution is provided. The marking scheme is described as behaviour, not as a class list — working out the classes is part of the exercise.
1. Scenario¶
A teacher keeps marks in a spreadsheet whose formulas differ per assessment: quizzes are averaged, assignments are weighted, the final exam has a minimum pass mark of its own, and a student who fails the exam fails the course regardless of the rest. Every semester the formulas are copied into a new sheet and every semester one of them is wrong.
You are asked for a program where the grading rules live in code, are testable, and are chosen by configuration rather than copied.
2. Problem statement¶
Build a gradebook: students, assessments with weights and categories, marks entered against both, and a grading policy that turns the marks into a final percentage and a letter. The policy must be replaceable — the teacher wants to try a different scheme without recompiling — and loading a malformed marks file must not half-populate the gradebook.
3. User requirements¶
- [ ] Register students and assessments, with weights that must add up.
- [ ] Record a mark for one student on one assessment.
- [ ] Ask for a student's weighted percentage, letter and pass/fail status.
- [ ] Get a class report: every student, ordered by percentage, with the mean and the median.
- [ ] Load students and marks from a file, and be told which line is wrong if any is.
- [ ] Change the grading scheme without rebuilding the program.
4. Functional requirements¶
- [ ]
Studentwith a unique id and a name, validated at construction. - [ ]
Assessmentwith a name, weight, maximum mark and category. - [ ]
Marks— a mapping from (student, assessment) to a mark — that enforces the maximum. - [ ] A
GradingPolicyinterface, with at least two implementations (weighted average; weighted average with a minimum exam mark). - [ ] Percentages rounded to a documented precision, with the rounding rule stated.
- [ ] A letter-grade table loaded from a file, so boundaries are data, not code.
- [ ] Weights must total exactly 100, checked when the assessment set is completed.
5. Non-functional requirements¶
- [ ] A mark above the assessment's maximum, or negative, is refused.
- [ ] Weights are validated as integers, not summed as
doubles. - [ ] The class report is deterministic: ties in percentage are broken by student id.
- [ ] The report uses STL algorithms for sorting and for the mean/median.
- [ ] A failed load leaves an empty gradebook, not a partial one.
- [ ] Builds warning-free with
g++ -std=c++17 -Wall -Wextra -Werror.
6. Suggested features¶
- Per-assessment statistics: mean, highest, lowest, and how many students missed it.
- A "what if" query: the mark needed on the final to reach a given letter.
- A per-category breakdown in the report, as well as the total.
- Export the report as a second format behind the same interface.
7. OOP concepts involved¶
| Concept | Required | Where it appears |
|---|---|---|
| Multiple classes | yes | Student, Assessment, Marks, GradingPolicy and its two implementations, Gradebook, report |
| Relationships | yes | Gradebook ◆ students, ◆ assessments, ◆ marks; policy injected, not owned |
| Encapsulation | yes | Marks cannot exceed the maximum; weights cannot drift from 100 |
| Inheritance or composition | yes | Policy behind an interface (composition + polymorphism); letter table as a value type |
| Polymorphism | yes | The grading rule dispatches through GradingPolicy, with no if on scheme |
| The STL | yes | std::map for marks, std::vector + std::sort for the report, std::accumulate for the mean |
| Exception handling | yes | Invalid marks and weights refused; file errors carry line numbers |
8. Suggested design considerations¶
Make the key for a mark explicit. A composite key — student id plus assessment name — is the honest model of "one mark per student per assessment". Deciding how to key the map is most of the design; write down the two alternatives and why you chose yours.
Keep the policy out of the data. A GradingPolicy that receives the marks and returns a
percentage is testable without constructing a gradebook. A policy implemented as a member of
Gradebook is not.
Integer weights, not double sums. 33.3 + 33.3 + 33.4 is not 100.0 in floating point.
Weights stored as integers out of 1000, or validated with a documented tolerance, is the fix.
Round once. Rounding each assessment then summing gives different results from summing then rounding. Pick one, document it, and test the boundary where the two disagree.
9. Minimum viable version¶
Student, Assessment, a marks container with validation, one grading policy producing a
weighted percentage and a letter, and a main printing an ordered report for a few hand-built
students.
10. Intermediate extension¶
Add the second grading policy with a minimum exam mark, switch between them by configuration, and prove with a table of marks that the two schemes differ only where the rule says they should — especially around the exam boundary.
11. Advanced extension¶
Load students, assessments, marks and the letter table from files, with every malformed case rejected with a line number. Then add the per-assessment statistics and export the report in a second format behind an interface, and record which files each change touched.
12. Testing checklist¶
- [ ] A student with full marks gets 100, and a student with none gets 0
- [ ] A missing mark counts as zero, or is refused — as documented, and tested
- [ ] A mark one above the assessment maximum is refused; exactly at it is accepted
- [ ] Weights totalling 99 or 101 are rejected with a message naming the total
- [ ] The two policies agree everywhere except within the minimum-exam rule
- [ ] The report is ordered by percentage descending, with ties by student id
- [ ] The mean and the median are correct for an even and an odd number of students
- [ ] A malformed marks file leaves the gradebook empty, not partly filled
- [ ] Rounding is applied once: a case that would differ under per-assessment rounding is tested
13. Documentation checklist¶
- [ ] The grading rules, written out as a specification a marker could follow
- [ ] The class responsibility list and the ownership diagram
- [ ] The key structure for marks and why it was chosen
- [ ] The rounding rule and where it is applied
- [ ] The input file formats, with one valid example and three invalid ones
14. Self-evaluation rubric¶
| Criterion | Excellent looks like | Weight |
|---|---|---|
| Rule design | Grading policies are separate, testable and swappable by configuration | 25% |
| Correctness | Percentages, letters and boundaries verified against hand-computed cases | 25% |
| Invariants | Marks and weights cannot be invalid at any moment | 15% |
| STL use | Containers and algorithms chosen with reasons; report deterministic | 10% |
| Failure handling | Malformed input rejected with line numbers; no partial state | 15% |
| Documentation | Rules, rounding and formats written precisely | 10% |
Related: Intermediate projects · Project roadmap · All projects · Module 14 — The STL with OOP