Museum Timed Entry: Tickets, Special Exhibition and Weekend Crowds
EasyAI-assisted coding · AI-Assisted Debugging
Museum Timed Entry: Tickets, Special Exhibition and Weekend Crowds
You've joined the visitor-experience team at an art museum. Entry is by timed slot: every day has fourteen entry slots, one every 30 minutes from 10:00 to 16:30, and each slot can only take so many people. The booking service is live but front-desk staff have reported wrong totals and a slot that "fills up" with babies. A special exhibition opens next month, and the facilities team has just told us weekends need a different crowd limit. Work through the three Parts in order. All money is integer cents; dates are YYYY-MM-DD, slots are HH:MM.
Codebase
| File | What it does |
|---|---|
museum/api.py | run(operations) entry point and the Dispatcher that routes each operation |
museum/service.py | MuseumService: opening days, booking, refunds, availability, day report |
museum/tickets.py | Parses ticket strings such as "child:9" into Ticket records |
museum/pricing.py | Order totals, and how many places an order takes |
museum/capacity.py | CapacityBook: places left per day and slot |
museum/orders.py | OrderBook: order ids and lookup |
museum/timegrid.py | Date/slot/clock helpers and the slot grid |
museum/models.py | Ticket and Order records and ticket-kind constants |
museum/config.py | Prices, slot grid and limits |
museum/errors.py | MuseumError and every error code |
tests/ | Existing unit tests (one of them currently fails) |
How your code is run
run(operations: list[list]) -> list executes the operations in order against a fresh museum and returns one result per operation. Every operation is [name, now, ...args], where now is the caller's clock as "YYYY-MM-DD HH:MM" (it is only compared with slot start times). Errors are returned as strings, never raised: "error: <code>", e.g. "error: slot full".
| Operation | Arguments after now | Returns | Errors (checked in this order) |
|---|---|---|---|
open_day | date, capacity | true | invalid date, invalid capacity, day exists |
availability | date | [[slot, remaining], ...] for all 14 slots, earliest first | day not open |
day_report | date | {"orders", "headcount", "revenue"} | day not open |
book | date, slot, tickets | {"order": "MT-1", "total": 3600, "slot": "10:00"} | invalid ticket, empty order, invalid slot, day not open, slot started, slot full |
refund | order_id | the refunded amount in cents | order not found, already refunded, slot started |
- An unknown operation name returns
"error: unknown operation". A wrong number of arguments, anowthat is not a realYYYY-MM-DD HH:MM, or an operation that is not a list returns"error: invalid arguments"(unknown name is checked first). capacitymust be an integer>= 1(true/falseare not integers).- Order ids are
"MT-1","MT-2", ... in the order orders succeed; a rejected order uses no id. availabilityandday_reportignorenow. A day can be opened for any date, past or future.day_reportcovers the orders on that date that have not been refunded:ordersis how many,headcountis every person including children under 5,revenueis the sum of their totals.
---
Part 1 - Fix the ticket counting and pricing
Front desk reports three problems. Find and fix them; do not change any other behaviour. tests/test_service.py has a failing test to start from.
Rules
- A ticket is
"adult","senior","member","guest"or"child:<age>"where age is a whole number 0-17 written without leading zeros. Anything else (wrong case,"child","child:18","child:07", a non-string) isinvalid ticket, and one bad ticket rejects the whole order. - Prices: adult 2400, senior 1800, child aged 5-17 1200, child under 5 free, member free, guest 1500.
- Each member in the order may bring up to 2 guests at the guest price. Guests beyond
2 x memberspay the adult price. With no member in the order, every guest pays adult. - Capacity is per slot. Every ticket takes one place except children under 5, who take none (members and guests do take places). An order is all-or-nothing: if its places do not fit in the slot's remaining places, nothing is booked and the result is
slot full. An order that fits exactly is accepted; an order that takes zero places is always accepted, even in a full slot. refundreturns the whole order total and puts its places back. It works only whilenowis strictly before the slot's start (at exactly the start time it isslot started), and an order can be refunded once.slot startedforbookuses the same test: a slot can no longer be booked at or after its start time.
Example (capacity 6, a Friday)
["open_day", "2026-03-13 08:00", "2026-03-13", 6] -> true
["book", "2026-03-13 08:00", "2026-03-13", "10:00", ["adult","child:3","child:9"]] -> {"order":"MT-1","total":3600,"slot":"10:00"}
["book", "2026-03-13 08:00", "2026-03-13", "10:00", ["member","guest","guest","guest"]] -> {"order":"MT-2","total":5400,"slot":"10:00"}
["book", "2026-03-13 08:00", "2026-03-13", "10:00", ["adult"]] -> "error: slot full"
["book", "2026-03-13 08:00", "2026-03-13", "10:00", ["child:2"]] -> {"order":"MT-3","total":0,"slot":"10:00"}
["refund", "2026-03-13 09:59", "MT-1"] -> 3600
["book", "2026-03-13 09:59", "2026-03-13", "10:00", ["adult"]] -> {"order":"MT-4","total":2400,"slot":"10:00"}
["day_report", "2026-03-13 09:59", "2026-03-13"] -> {"orders":3,"headcount":6,"revenue":7800}MT-1 takes 2 places (the 3-year-old is free), MT-2 takes 4 (the third guest pays 2400, so 2 x 1500 + 2400), which fills the slot exactly.
---
Part 2 - Special exhibition
A special exhibition opens in a small gallery. Visitors can add it to any ticket, it costs extra, and the gallery has its own, smaller capacity per slot. A visitor who wants the exhibition needs room in both general admission and the exhibition for that slot.
Rules
open_daytakes an optional fourth argument,exhibit_capacity(integer>= 0; default0, meaning no exhibition that day). An invalid value isinvalid capacity, checked after thecapacitycheck. Exhibition capacity is per slot.- Any ticket may end in
+x("adult+x","child:9+x","guest+x") to add the exhibition. Only that exact suffix, once, is valid; anything else isinvalid ticket. - The exhibition adds 700 per person on top of the ticket price, for every kind including members and guests. Children under 5 are still free:
"child:4+x"costs nothing, takes no general place and no exhibition place. bookerror order becomes:invalid ticket,empty order,invalid slot,day not open,slot started, thenexhibit closed(the order needs exhibition places and the day'sexhibit_capacityis 0), thenslot full(general), thenexhibit full. The order is still all-or-nothing: if the exhibition is short, no general places are kept either.refundputs back both kinds of places and returns the full total including surcharges.availabilitytakes an optional second argument"exhibit"and then reports remaining exhibition places per slot. Any other second argument isinvalid arguments(checked beforeday not open).day_reportrevenue includes surcharges.
Example (capacity 10, exhibit capacity 2)
["open_day", "2026-03-13 08:00", "2026-03-13", 10, 2] -> true
["book", "2026-03-13 08:00", "2026-03-13", "11:00", ["adult+x","child:4+x","senior+x"]] -> {"order":"MT-1","total":5600,"slot":"11:00"}
["book", "2026-03-13 08:00", "2026-03-13", "11:00", ["adult+x"]] -> "error: exhibit full"
["book", "2026-03-13 08:00", "2026-03-13", "11:00", ["adult"]] -> {"order":"MT-2","total":2400,"slot":"11:00"}
["availability", "2026-03-13 08:00", "2026-03-13", "exhibit"] -> [["10:00",2],["10:30",2],["11:00",0],["11:30",2], ...]
["refund", "2026-03-13 10:00", "MT-1"] -> 5600MT-1 is 2400 + 1800 + 2 x 700 + 0 = 5600 and uses 2 exhibition places. The refund frees them again.
---
Part 3 - Weekend crowds and walk-up overflow
Facilities has changed the crowd limit for weekends, and the front desk wants to offer walk-ups the next free slot instead of turning them away.
Rules
- On Saturdays and Sundays (by the calendar date), the day's
capacityis no longer per slot: it is the limit for the whole clock hour. The two slotsHH:00andHH:30share one pool ofcapacityplaces. Weekdays are unchanged. Exhibition capacity stays per slot on every day. availability(general) on a weekend reports, for each slot, the places left in that slot's hour, so theHH:00andHH:30entries are equal. A refund returns places to that hour's pool.- New operation
walk_upwith the arguments ofbook(date, slot, tickets). It validates exactly likebookup to and includingexhibit closed. Then it tries the requested slot, and if the order does not fit (in general admission, or in the exhibition if the order needs it) it tries each later slot of the same day, in order, and books the first one where the whole order fits both pools. It returns{"order", "total", "slot"}whereslotis the slot actually booked. If no slot from the requested one to 16:30 fits, it returns"error: sold out". bookitself never moves an order: it still returnsslot full/exhibit full. Refunds, theslot startedrule andday_reportuse the slot that was actually booked.- Parts 1 and 2 behave as before on weekdays; their examples use a Friday.
Example (Saturday, capacity 4)
["open_day", "2026-03-14 08:00", "2026-03-14", 4] -> true
["book", "2026-03-14 08:00", "2026-03-14", "10:00", ["adult","adult","adult"]] -> {"order":"MT-1","total":7200,"slot":"10:00"}
["book", "2026-03-14 08:00", "2026-03-14", "10:30", ["adult","adult"]] -> "error: slot full"
["walk_up", "2026-03-14 09:50", "2026-03-14", "10:30", ["adult","adult"]] -> {"order":"MT-2","total":4800,"slot":"11:00"}
["refund", "2026-03-14 09:55", "MT-1"] -> 7200
["book", "2026-03-14 09:55", "2026-03-14", "10:30", ["adult","adult"]] -> {"order":"MT-3","total":4800,"slot":"10:30"}The 10:00 and 10:30 slots share the four places of the 10:xx hour, so the 10:30 order does not fit and the walk-up rolls over to 11:00, the first slot of the next hour.
Example 1
- Input:
- [["open_day","2026-03-13 08:00","2026-03-13",6],["book","2026-03-13 08:00","2026-03-13","10:00",["adult","child:3","child:9"]],["book","2026-03-13 08:00","2026-03-13","10:00",["member","guest","guest","guest"]],["book","2026-03-13 08:00","2026-03-13","10:00",["adult"]]]
- Output:
- [true,{"order":"MT-1","total":3600,"slot":"10:00"},{"order":"MT-2","total":5400,"slot":"10:00"},"error: slot full"]
- Explanation
- The 3-year-old is free and takes no place, so MT-1 uses 2 of 6 places. MT-2 uses 4: the member is free and the third guest pays the adult price (2 x 1500 + 2400). The slot is now exactly full.
Example 2
- Input:
- [["open_day","2026-03-13 08:00","2026-03-13",10,2],["book","2026-03-13 08:00","2026-03-13","11:00",["adult+x","child:4+x","senior+x"]],["book","2026-03-13 08:00","2026-03-13","11:00",["adult+x"]]]
- Output:
- [true,{"order":"MT-1","total":5600,"slot":"11:00"},"error: exhibit full"]
- Explanation
- The exhibition adds 700 per paying person (the 4-year-old is exempt), so 2400 + 1800 + 1400 = 5600 and both exhibition places are used.
Example 3
- Input:
- [["open_day","2026-03-14 08:00","2026-03-14",4],["book","2026-03-14 08:00","2026-03-14","10:00",["adult","adult","adult"]],["walk_up","2026-03-14 09:50","2026-03-14","10:30",["adult","adult"]]]
- Output:
- [true,{"order":"MT-1","total":7200,"slot":"10:00"},{"order":"MT-2","total":4800,"slot":"11:00"}]
- Explanation
- On a Saturday the 10:00 and 10:30 slots share one pool of 4, which has 1 place left, so the walk-up is offered 11:00 instead.
Guidelines
- Python 3 standard library only
- Amounts are integer cents; there is no floating-point money anywhere
- Dates are YYYY-MM-DD, slots are HH:MM, the clock is YYYY-MM-DD HH:MM
- Every operation returns exactly one value; errors are returned as "error: <code>" strings, never raised
- Orders are all-or-nothing: a rejected order changes nothing