Python · Module 5
Answers and explanations · Files, CSV and errors
Compare these references after your own attempt. You can read them at any time; they do not record a live model or vendor run.
Module 5
The conversion rejects malformed and negative values. The original text and record position remain in the reject list. DictWriter quotes a topic containing a comma, so the reader reconstructs it correctly.
# Reference answer. Attempt the exercise first.
import csv
from io import StringIO
def parse_minutes(raw):
value = int(raw)
if value < 0:
raise ValueError("minutes must be non-negative")
return value
def read_sessions(csv_text):
accepted, rejected = [], []
with StringIO(csv_text) as source:
for line_number, row in enumerate(csv.DictReader(source), start=2):
try:
value = parse_minutes(row["minutes"])
except ValueError:
rejected.append({"line": line_number, "raw": row["minutes"]})
else:
accepted.append({"topic": row["topic"], "minutes": value})
return accepted, rejected
def encode_summary(summary):
output = StringIO()
writer = csv.DictWriter(output, fieldnames=["topic", "minutes"])
writer.writeheader()
writer.writerows(summary)
return output.getvalue()
text = "topic,minutes\npython,20\ndata,unknown\ncharts,-1\npython,0\n"
accepted, rejected = read_sessions(text)
assert len(accepted) == 2 and len(rejected) == 2
assert sum(row["minutes"] for row in accepted) == 20
print(encode_summary(accepted))
quoted = encode_summary([{"topic": "data, charts", "minutes": 10}])
assert list(csv.DictReader(StringIO(quoted)))[0]["topic"] == "data, charts"
print("CSV round-trip check passed")
Exit-check guide: A value can parse successfully and still violate the task rule. Missing minutes are unknown, not zero. A missing required header should remain a visible error. The with block closes the source on normal or exceptional exit.