Paper 2 Ethics Issues Answers
These answers correspond to Paper 2 Ethics Issues Drills.
Verification note: every Python code block in this answer file has been executed locally.
Answer 1: Privacy Checklist
Model answer:
def privacy_issues(form):
required = ["purpose", "consent", "retention"]
missing = []
for item in required:
if item not in form:
missing.append(item)
return missing
form = {"name": "Amy", "phone": "91234567", "purpose": "event registration", "consent": True}
print(privacy_issues(form))Expected output:
['retention']Mark points:
- defines the required privacy items;
- checks purpose;
- checks consent;
- checks retention notice;
- returns the exact missing item list.
Common weak answer:
- checking only whether the form has a name and phone number. The question asks for privacy-notice items.
Answer 2: Access Log Analysis
Model answer:
def flag_after_hours(logs):
flagged = []
for record in logs:
if record["hour"] < 8 or record["hour"] >= 18:
flagged.append(record["user"])
return flagged
logs = [
{"user": "u1", "hour": 7},
{"user": "u2", "hour": 9},
{"user": "u3", "hour": 18},
{"user": "u4", "hour": 17}
]
print(flag_after_hours(logs))Expected output:
['u1', 'u3']Mark points:
- loops through all log records;
- checks before opening time;
- checks at or after closing time;
- treats
18as outside office hours; - appends user IDs, not whole records;
- preserves record order.
Common weak answer:
- using
hour > 18, which fails to flag exactly18.
Answer 3: Consent Flag
Model answer:
def has_consent(record, purpose):
return purpose in record["consent"]
record = {"user": "u1", "consent": ["billing", "support"]}
print(has_consent(record, "billing"))
print(has_consent(record, "marketing"))Expected output:
True
FalseMark points:
- reads the consent-purpose list;
- checks the requested purpose;
- returns
Trueforbilling; - returns
Falseformarketing; - does not treat any consent as consent for every purpose;
- returns Boolean values.
Common weak answer:
- returning
Truejust because the consent list is not empty.
Answer 4: Anonymisation
Model answer:
def anonymise(records):
anonymised = []
for record in records:
anonymised.append({
"age_group": record["age_group"],
"region": record["region"],
"score": record["score"]
})
return anonymised
records = [
{"name": "Amy", "email": "amy@example.com", "age_group": "16-18", "region": "East", "score": 82}
]
print(anonymise(records))Expected output:
[{'age_group': '16-18', 'region': 'East', 'score': 82}]Mark points:
- loops through records;
- removes
name; - removes
email; - keeps
age_group; - keeps
region; - keeps
score.
Common weak answer:
- replacing only the name while leaving the email address.
Answer 5: Risk Register
Model answer:
def risk_register():
return [
("unauthorised access", "access control"),
("excessive retention", "delete after 12 months"),
("biased output", "compare approval rates")
]
print(risk_register())Expected output:
[('unauthorised access', 'access control'), ('excessive retention', 'delete after 12 months'), ('biased output', 'compare approval rates')]Mark points:
- includes unauthorised access risk;
- matches it with access control;
- includes excessive retention risk;
- matches it with deletion after 12 months;
- includes biased output risk;
- matches it with comparing approval rates.
Common weak answer:
- listing risks without mitigations.
Answer 6: Audit Function
Model answer:
def count_missing_consent(records):
count = 0
for record in records:
if "consent" not in record or record["consent"] == False:
count = count + 1
return count
records = [
{"user": "u1", "consent": True},
{"user": "u2", "consent": False},
{"user": "u3"}
]
print(count_missing_consent(records))Expected output:
2Mark points:
- initializes a counter;
- loops through all records;
- counts explicit
Falseconsent; - counts missing consent field;
- returns the correct count.
Common weak answer:
- counting only records where
consentexists and isFalse, missing records with no consent field.
Answer 7: Retention Rule
Model answer:
def should_archive(record_date, cutoff_date):
return record_date < cutoff_date
print(should_archive("2024-01-15", "2025-01-01"))
print(should_archive("2025-02-10", "2025-01-01"))Expected output:
True
FalseMark points:
- compares record date with cutoff date;
- uses ISO date strings where lexical order matches date order;
- returns
Truefor a record before cutoff; - returns
Falsefor a record after cutoff; - produces both expected outputs;
- keeps the rule clear.
Common weak answer:
- archiving records newer than the cutoff date.
Answer 8: Stakeholder Report
Model answer:
def impact_counts(records):
counts = {}
for record in records:
stakeholder = record["stakeholder"]
if stakeholder not in counts:
counts[stakeholder] = 0
counts[stakeholder] = counts[stakeholder] + 1
return counts
records = [
{"stakeholder": "students", "impact": "privacy concern"},
{"stakeholder": "staff", "impact": "less manual work"},
{"stakeholder": "students", "impact": "faster service"}
]
print(impact_counts(records))Expected output:
{'students': 2, 'staff': 1}Mark points:
- initializes a dictionary;
- loops through impact records;
- groups by stakeholder;
- initializes unseen stakeholders;
- increments counts correctly.
Common weak answer:
- counting impact descriptions rather than stakeholder groups.
Answer 9: Bias Check
Model answer:
def approval_rates(records):
approved = {}
totals = {}
for record in records:
group = record["group"]
if group not in approved:
approved[group] = 0
totals[group] = 0
totals[group] = totals[group] + 1
if record["approved"]:
approved[group] = approved[group] + 1
rates = {}
for group in totals:
rates[group] = round(approved[group] / totals[group], 2)
return rates
records = [
{"group": "A", "approved": True},
{"group": "A", "approved": True},
{"group": "A", "approved": False},
{"group": "B", "approved": True},
{"group": "B", "approved": False},
{"group": "B", "approved": False}
]
print(approval_rates(records))Expected output:
{'A': 0.67, 'B': 0.33}Mark points:
- counts total records per group;
- counts approved records per group;
- calculates approval rate for Group A;
- calculates approval rate for Group B;
- rounds rates to two decimal places;
- returns a dictionary by group;
- matches the expected output.
Common weak answer:
- comparing only approved counts without considering different group totals.
Answer 10: Incident Response
Model answer:
def ordered_incident_steps():
return [
"contain leak",
"preserve evidence",
"assess affected data",
"notify responsible parties",
"fix cause",
"review controls"
]
print(ordered_incident_steps())Expected output:
['contain leak', 'preserve evidence', 'assess affected data', 'notify responsible parties', 'fix cause', 'review controls']Mark points:
- contains the leak first;
- preserves evidence;
- assesses affected data;
- includes notification/reporting;
- fixes the cause and reviews controls.
Common weak answer:
- deleting evidence immediately before assessing what happened.