No new syntax here — just everything from Modules 1–4, applied to the real fixture data instead of small made-up examples.
Everything so far used tiny examples you typed by hand. Now you're working with USERS — 12 real-shaped user records — and LOGIN_COUNTS, a separate lookup dict. Same list-of-dicts shape you already know, just bigger and messier, like real API data actually is.
from fixture import USERS, LOGIN_COUNTS
print(USERS[0])
Before doing anything else: create fixture.py in your module-5/problems/ folder by typing in the data from DAY1_01_FIXTURE.md yourself — don't paste it. Everything in this module imports from that file.
Print USERS[0] whole — see the full shape of one record.
id, status, lastLogin, profile, groupsPrint len(USERS).
12Print len(LOGIN_COUNTS).
9Print type(USERS), then print type(USERS[0]) — confirm it's a list of dicts.
<class 'list'> then <class 'dict'>Print USERS[0]["id"].
00u001Open problems/01_meet_the_fixture.py — but first, type fixture.py into that same folder from DAY1_01_FIXTURE.md.
Same indexing from Module 2 — USERS[0] is the first record, USERS[-1] is the last. Just applied to real, bigger data now.
print(USERS[0]["id"]) # 00u001
print(USERS[-1]["id"]) # 00u012
Print USERS[0]["id"].
00u001Print USERS[-1]["id"].
00u012Print USERS[5]["id"].
00u006Print USERS[11]["id"], then print USERS[-1]["id"] — same record, two ways.
00u012Print USERS[1]["status"].
ACTIVEOpen problems/02_indexing.py and do all 5 problems locally.
Same nested access from Module 3 — chain the brackets to reach a specific field inside a specific record.
print(USERS[0]["profile"]["email"]) # jrivera@example.com
Print USERS[0]["profile"]["email"].
jrivera@example.comPrint USERS[1]["profile"]["firstName"].
AmyPrint USERS[2]["status"].
SUSPENDEDPrint USERS[6]["profile"]["department"].
SecurityPrint USERS[9]["profile"]["lastName"].
FosterUSERS[7] (Layla Haddad) is missing something. What happens with print(USERS[7]["profile"]["department"])?
KeyError: 'department'This is the fixture's real planted bug — record 8 (USERS[7]) has no department key at all. Real API data does this constantly.
Open problems/03_chaining.py and do all 6 problems locally.
Use .get() at the level that might be missing, same as Module 3, to avoid the crash you just saw.
print(USERS[7]["profile"].get("department", "Unknown")) # Unknown -- no crash
Print USERS[7]["profile"].get("department", "Unknown").
UnknownPrint USERS[7]["profile"].get("email") — a key that does exist.
lhaddad@example.comPrint USERS[3]["lastLogin"] directly — record 4 (Dana) has this set to None. No crash, it just prints None.
NonePrint USERS[3].get("lastLogin") — same result, safer habit.
NoneWhat happens with print(USERS[3]["lastLogin"] + " ago")?
TypeError: unsupported operand type(s) for +: 'NoneType' and 'str'Same bug family from Module 1 — you can't glue text onto None. This is the fixture's second planted bug, now hit for real.
Print USERS[0]["profile"].get("department", "Unknown") — a record where the key IS present, default gets ignored.
ITOne script, three lines: safely get USERS[7]'s department with a default, safely get USERS[3]'s lastLogin, and safely get USERS[0]'s department (present this time).
Unknown, None, ITOpen problems/04_safe_access.py — 6 problems, #6 is a checkpoint combining sub-skills 1–4.
Each record's groups is a list — but not a list of plain names. It's a list of dicts, each with its own id and name. One more chain step than you might expect.
print(USERS[0]["groups"][0]) # {'id': 'g01', 'name': 'Engineering'} -- a dict, not a string
print(USERS[0]["groups"][0]["name"]) # Engineering -- now you have the string
Print USERS[0]["groups"][0]["name"].
EngineeringPrint USERS[0]["groups"][-1]["name"].
VPN-UsersPrint len(USERS[0]["groups"]) — how many groups.
2Print USERS[3]["groups"] — record 4 (Dana) has zero groups.
[]Print bool(USERS[3]["groups"]).
FalseMarcus (USERS[9]) has 3 groups. Print USERS[9]["groups"][1]["name"].
AdminsOpen problems/05_groups_list.py and do all 6 problems locally.
Pull a record's email, then use it to look something up in the other dict, LOGIN_COUNTS. This is exactly how you'd join two different API responses in a real script.
email = USERS[0]["profile"]["email"]
print(LOGIN_COUNTS.get(email)) # 142
email = USERS[0]["profile"]["email"]. Print LOGIN_COUNTS.get(email).
142email = USERS[4]["profile"]["email"] (Kelly O'Connor). Print LOGIN_COUNTS.get(email). Will it find her count?
None — not foundThis is a real bug, not a coincidence: LOGIN_COUNTS has a typo — "koonnor@example.com" instead of Kelly's real email "kokonnor@example.com". The lookup silently fails instead of crashing, which is worse — you'd never notice unless you checked.
Print "koonnor@example.com" in LOGIN_COUNTS — the misspelled key IS there.
TruePrint USERS[4]["profile"]["email"] in LOGIN_COUNTS — her real email is not.
Falseemail = USERS[5]["profile"]["email"] (Tri Nguyen). Print LOGIN_COUNTS.get(email, 0) — genuinely missing this time, not a typo.
0Print LOGIN_COUNTS.get(USERS[4]["profile"]["email"], 0) — same typo case, with a default this time.
0 (still wrong data, just doesn't crash)email = USERS[8]["profile"]["email"] (Rosa Gomez). Print LOGIN_COUNTS.get(email, 0).
0Open problems/06_join_by_key.py and do all 6 problems locally.
Pull a field from two different records and compare with == — same operator, applied to real records instead of small examples.
print(USERS[0]["status"] == USERS[1]["status"]) # True -- both ACTIVE
Print USERS[0]["status"] == USERS[1]["status"].
TruePrint USERS[2]["status"] == USERS[8]["status"] — Bina and Rosa.
True (both SUSPENDED)Print USERS[0]["profile"]["department"] == USERS[3]["profile"]["department"] — Julio and Dana.
True (both IT)Print USERS[0] == USERS[1] — the whole records, not just one field.
FalsePrint USERS[5]["status"] == USERS[11]["status"] — Tri and Pat.
True (both DEPROVISIONED)Open problems/07_compare_records.py and do all 5 problems locally.
The payoff: an f-string (Module 1) combined with everything else — chained access, safe .get(), list length — into one clean printed line. This is what a real report script actually produces.
user = USERS[0]
name = user["profile"]["firstName"] + " " + user["profile"]["lastName"]
dept = user["profile"].get("department", "Unknown")
group_count = len(user["groups"])
print(f"{name} ({dept}) -- {group_count} groups")
# Julio Rivera (IT) -- 2 groups
Build the same report line for USERS[1] (Amy Chen).
Amy Chen (Security) -- 3 groupsBuild it for USERS[7] (Layla) — her missing department should show "Unknown", not crash.
Layla Haddad (Unknown) -- 1 groupsBuild it for USERS[3] (Dana) — zero groups.
Dana Morris (IT) -- 0 groupsAdd the login count to the line for USERS[0], using LOGIN_COUNTS.get() with a default of 0.
142Build the full line (name, dept, groups, login count) for USERS[4] (Kelly) — remember her login count won't be found due to the typo.
0Print one report line each for USERS[0] and USERS[7], back to back — a mini two-user report combining every sub-skill in this module.
Open problems/08_report_line.py — 6 problems, #6 is the final checkpoint for all of Module 5.
Run python check.py in problems/ to grade everything.