The Attendance Script That Cries Wolf
A Class XI teacher at a Guwahati school has 64 students. Attendance is exported from a register into a CSV with columns Roll, Name, DaysPresent, TotalDays and FeeStatus. Some TotalDays cells are blank because three students joined late; three other rows have TotalDays = 0. FeeStatus contains 'Paid', 'paid', 'PENDING', blank and 'Partial'. The teacher's existing script computes percentage = DaysPresent / TotalDays * 100 and prints students where percentage < 75.0 or FeeStatus == 'Pending'. It crashes on some rows and prints students who should not be flagged. The teacher wants a clean list, sorted by Roll, of students below 75% attendance or with fees not fully paid, and wants to know how many were wrongly flagged. The list will be used to call parents, so a wrong flag is a serious problem.
1. Identify the failure modes in the existing script and the order in which you would fix them. Explain why checking `percentage < 75.0` after floating-point division is risky at the boundary.
Read the worked answer
Failure modes: division by zero or blank TotalDays; blank DaysPresent treated as zero; case-sensitive FeeStatus so 'paid' and 'PENDING' behave inconsistently; 'Partial' not handled; the output is unsorted; and invalid rows either crash the run or disappear silently. I would fix in this order: (1) read and validate input, preserving the original row number; (2) define rules for blank TotalDays, zero TotalDays and late joiners; (3) normalise FeeStatus to a small set of values; (4) compute eligibility using integer arithmetic; (5) sort by Roll and write both a flagged list and a review list. Floating-point division can produce 74.999999 for a true 75%, so a student at exactly 75% may be wrongly flagged. Compare DaysPresent * 100 < 75 * TotalDays instead. This is exact for integers and makes the boundary rule explicit: exactly 75% is not below 75%.
2. Write pseudocode for the corrected logic, including how you would handle late joiners and blank FeeStatus.
Read the worked answer
Pseudocode:
flagged = []; review = []
for row in csv:
roll = trim(row.Roll); name = trim(row.Name)
present = parseInt(row.DaysPresent)
total = parseInt(row.TotalDays)
status = lower(trim(row.FeeStatus))
if total is blank or total <= 0 or present is blank or present < 0:
review.append(row, 'invalid attendance data'); continue
if total < 30: # late joiner threshold
review.append(row, 'insufficient attendance period'); continue
fee_bad = status in ('pending','partial','') # blank = unknown
if status == '': review.append(row, 'missing fee status')
attendance_bad = present * 100 < 75 * total
if attendance_bad or (fee_bad and status != ''):
flagged.append(row)
sort flagged by roll; print flagged and review
The 30-day threshold is a policy decision, not a fact; the teacher should confirm it. Blank FeeStatus is sent to review rather than treated as paid or pending, because either assumption could wrongly call a parent. Late joiners are not automatically defaulters; their attendance is judged over the period they were enrolled.
3. Design test cases that would catch the original bugs, including boundary and case-sensitivity cases, and state the expected output for each.
Read the worked answer
Test table:
| Roll | DaysPresent | TotalDays | FeeStatus | Expected |
|---|---|---|---|---|
| 1 | 45 | 60 | Paid | not flagged (exactly 75%) |
| 2 | 44 | 60 | Paid | flagged (73.33%) |
| 3 | 0 | 0 | Paid | review, no crash |
| 4 | 50 | blank | Paid | review, no crash |
| 5 | 50 | 60 | pending | flagged |
| 6 | 50 | 60 | PAID | not flagged |
| 7 | 50 | 60 | Partial | flagged |
| 8 | 60 | 60 | blank | review, not flagged |
| 9 | blank | 60 | Paid | review |
Roll 1 checks the boundary; Roll 2 checks just below it; Roll 3 and 4 check division and blank handling; Rolls 5–7 check case and partial status; Roll 8 checks unknown fee status. Also add a duplicate Roll to test sorting stability, and a non-numeric DaysPresent to test validation. The expected total should be stated explicitly, for example 3 flagged and 4 review rows, so the teacher can reconcile the call list.