Airline Seat Map: Check-in, Paid Seats and Aircraft Swaps
MediumAI-assisted coding · AI-Assisted Feature Work
Seat Map: Check-in, Paid Seats and Aircraft Swaps
You've joined the airport-systems team that owns the seat-assignment service for a regional airline. It holds the seat map of one flight, the bookings on it, and decides who sits where, both when passengers pick seats themselves and when the system auto-assigns them at check-in. Operations has three tickets queued: check-in auto-assignment (Part 1), paid seats and seat changes (Part 2) and re-seating a flight when the aircraft is swapped (Part 3). The seat rules in the existing code were written in a hurry, so expect to read them against the rules below.
Codebase
| File | What it does |
|---|---|
seatmap/api.py | run(operations) entry point and the Dispatcher that routes each operation |
seatmap/service.py | SeatService: booking, seat selection, check-in, read-only views |
seatmap/assigner.py | plan_checkin: where check-in auto-assignment lives (stub) |
seatmap/rules.py | Who is an adult or child, exit-row eligibility, the "child beside an adult" test |
seatmap/cabin.py | Cabin: seat labels, categories, window/aisle/middle, adjacency, scan order |
seatmap/flight.py | Flight: passengers, bookings and the seat-occupancy index |
seatmap/models.py | Passenger, Booking and the passenger-list parser for book |
seatmap/catalog.py | The aircraft types and their layouts |
seatmap/errors.py, seatmap/config.py | Error codes and constants |
seatmap/loadgen.py | Deterministic booking generator behind bulk_book (known-good) |
tests/ | Example tests (some currently fail) |
How your code is run
run(operations: list[list]) -> list executes the operations in order against a fresh service and returns one result per operation. Every operation is [name, *args]. Errors are returned as "error: <code>" strings, never raised. An unknown operation name returns "error: unknown operation"; a wrong number of arguments returns "error: invalid arguments". Every operation except open_flight returns "error: no flight" until a flight has been opened.
Seats
- A seat label is
<row><letter>, e.g."12C". The layout of each aircraft is incatalog.py: seats are grouped in blocks separated by an aisle (A320:ABC | DEF, E175:AB | CD, B77W:ABC | DEFG | HJK). - Two seats are adjacent when they are in the same row and the same block, with consecutive letters.
5B/5Con an A320 are adjacent;5C/5Dare not (the aisle is between them). - Each seat is
blocked(can never be assigned) or belongs to one of three categories:exit(a row inexit_rows),legroom(a row inlegroom_rows; sold for 4 500 cents, see Part 2), orstandard. - Seat kind:
window= the outermost seat of the cabin on either side,aisle= a block's end seat that touches an aisle,middle= anything else (A320: A and F are windows, C and D aisles, B and E middles). - The scan order is how the airline walks the cabin when it has a free choice: rows from the back to the front, letters in layout order within a row (A320:
24A, 24B, 24C, 24D, ..., 23A, ...).
Passengers
- A passenger is
[pid, age, flags].flagsis a string of the lettersI(an adult travelling with a lap infant),T(accepted the exit-row terms),W(prefers a window) andA(prefers an aisle); each at most once, neverWandAtogether,Ionly for age 18 or more. - Adult: age 18 or more. Child: age under 12. 12 to 17 is neither (a "teen" has no seating rules).
- Exit-row eligible: an adult who accepted the terms (
T) and is not travelling with an infant (I). Only eligible passengers may sit inexitseats. - Family rule: a seated child must sit in a seat adjacent to a seat occupied by an adult of the same booking. An unseated child breaks no rule.
| Operation | Arguments | Returns | Errors (checked in this order) |
|---|---|---|---|
open_flight | aircraft | true | flight already open, unknown aircraft |
book | booking_id, passengers | true | invalid arguments (id not a non-empty string), booking exists, invalid passengers |
assign | pid, seat | true | passenger not found, seat not found, seat blocked, seat taken, payment required, not exit eligible, child not with adult |
checkin | booking_id | [[pid, seat], ...] | booking not found, cannot seat booking |
checkin_all | none | {"seated": n, "failed": [booking_id, ...]} | |
seat_of | pid | seat or null | passenger not found |
booking_seats | booking_id | [[pid, seat or null], ...] in booking order | booking not found |
occupancy | none | {"seated", "unseated", "free"} (free = open non-blocked seats of every category) | |
manifest_hash, bulk_book | known-good helpers for large flights; ignore them |
book accepts 1 to 6 passengers. It fails with invalid passengers if the list or any entry is malformed (age must be an integer 0-110; flags as above), a passenger id repeats within the booking or is already used anywhere on the flight, or the booking has a child but no adult.
Part 1: Auto-assignment at check-in
Goal: implement checkin and checkin_all so that a booking's unseated passengers are seated together when possible. While you are in there, the existing assign must follow the rules in the table above (the example tests show the symptoms).
assign rules
- Assigning a passenger to the seat they already hold succeeds and changes nothing. Otherwise the seat is taken by someone else →
seat taken. legroomseats cannot be assigned yet (payment required; Part 2 sells them).exitseats need an eligible passenger. A seat label that is not on this aircraft isseat not found.- Assigning a child whose new seat would not be adjacent to a seated adult of the booking fails with
child not with adult. Nothing else is checked for adults in this Part. - A passenger who moves to another seat frees the old one.
checkin rules. Only the booking's *unseated* passengers are placed; seated ones stay. An open seat is free, not blocked, and standard (auto-assignment never uses exit or legroom seats). Let U be the unseated passengers in booking order. Work out a plan; if it fails return error: cannot seat booking and change nothing. Otherwise seat everyone and return [pid, seat] for each newly seated passenger in booking order ([] if everyone was already seated).
- Step 1, together (only when
|U| >= 2). A *run* is|U|consecutive open seats in one block (same row, consecutive letters). Find the arrangement ofUfor a run: out of all orderings ofU, take the first one when orderings are compared position by position using booking positions (smallest first), such that every child inUhas an adult fromUimmediately left or right of them in the run. If no ordering works, skip Step 1. Otherwise take the first run in scan order (rows back to front, blocks left to right, runs left to right) whose seats are all open and seatUin it, left to right, in the arrangement. - Step 2, closest possible (when Step 1 does not apply or finds no run). Seat the members of
Uone at a time: first the adults ofUin booking order, then everyone else ofUin booking order. For one member, take every open seat not already used in this plan; if the member is a child, keep only seats adjacent to a seated adult of the booking (already seated, or placed earlier in this plan). Pick the seat with the smallest key(a, b, c, d), compared left to right:a= 0 if the seat is adjacent to a seat of any seated member of the booking (including earlier placements in this plan), else 1;b= |row of seat - anchor row|;c= 0 if the seat's kind matches the member'sW/Apreference, else 1;d= position in scan order. The anchor row is the row of the first member in *booking order* who is already seated before this check-in; if there is none it is the row of the first seat this plan assigns (andbis 0 until then). If some member has no candidate seat the plan fails. This greedy is the specification; it is not an optimal search.
checkin_all runs checkin on every booking in creation order; a booking that cannot be seated is left untouched and listed in failed; seated counts passengers newly seated.
Worked example
[["open_flight", "A320"],
["book", "fam", [["mum", 34, "T"], ["kid", 8, ""], ["tot", 5, ""]]],
["book", "solo", [["bo", 29, "W"]]],
["assign", "mum", "5C"],
["assign", "kid", "5D"],
["assign", "mum", "5B"],
["assign", "kid", "5A"],
["checkin", "fam"],
["checkin", "solo"],
["booking_seats", "fam"]]returns
[true, true, true, true, "error: child not with adult", true, true,
[["tot", "5C"]], [["bo", "24A"]],
[["mum", "5B"], ["kid", "5A"], ["tot", "5C"]]](5C and 5D are on opposite sides of the aisle; tot can only sit beside mum, and 5C is the last seat beside 5B; bo wants a window and 24A is the first one in scan order.)
Part 2: Paid seats and seat changes
Goal: sell the extra-legroom seats, allow refunds, and let a booking change seats as a group without ever leaving a child alone. This replaces the weaker family check of Part 1.
| Operation | Arguments | Returns | Errors (checked in this order) |
|---|---|---|---|
change | booking_id, moves[, "pay"] | true | booking not found, invalid moves, passenger not found, then per move in order: seat not found, seat blocked, seat taken, payment required, not exit eligible; finally child not with adult |
refund_seat | pid | 4500 | passenger not found, no paid seat, child not with adult |
invoice | booking_id | {"charged", "refunded", "net", "paid_seats"} | booking not found |
Rules
movesis a non-empty list of[pid, seat]pairs for members of one booking, applied all or nothing. The optional third argument may only be"pay".invalid movescovers a malformed list or pair, a repeated pid, or any other third argument. A pid that is not a member of that booking (even if it exists elsewhere) ispassenger not found; all pids are checked before any seat.- A seat may be the seat of a passenger who is moving away in the same change (so two members can swap seats). A move to the seat the passenger already holds is a no-op: it frees nothing, so its seat counts as taken for everyone else. Two moves to the same seat →
seat taken. legroomseats are rejected withpayment requiredunless the third argument is"pay". With"pay"every passenger who moves into alegroomseat is charged 4 500 cents.- Once every move passes the per-seat checks, the family rule is checked on the booking as it would look after the change: if any child of the booking (moved or not, in booking order) would be seated without an adult of the booking beside them, the change fails with
child not with adult. - A passenger who moves out of a seat they paid for gets the 4 500 refunded (their next seat is only paid if it is a
legroomseat in"pay"mode). assignnow means exactlychangefor that passenger's booking with that one move (the new family check therefore applies to adults too; all Part 1assignerrors keep their order).refund_seatworks on a passenger currently in a seat they paid for. It frees the seat, leaves the passenger unseated, refunds 4 500 and returns4500; if that would leave a child of the booking without an adult beside them it fails withchild not with adultand changes nothing.invoicereturns the booking's cumulativechargedandrefundedcents,net = charged - refunded, andpaid_seats:[pid, seat]for each member currently in a paid seat, in booking order.
Worked example
[["open_flight", "A320"],
["book", "fam", [["mum", 34, ""], ["kid", 8, ""]]],
["assign", "mum", "2A"],
["change", "fam", [["mum", "2A"], ["kid", "2B"]], "pay"],
["invoice", "fam"],
["change", "fam", [["mum", "7A"]]],
["change", "fam", [["mum", "7A"], ["kid", "7B"]]],
["invoice", "fam"]]returns
[true, true, "error: payment required", true,
{"charged": 9000, "refunded": 0, "net": 9000, "paid_seats": [["mum", "2A"], ["kid", "2B"]]},
"error: child not with adult", true,
{"charged": 9000, "refunded": 9000, "net": 0, "paid_seats": []}]Part 3: Aircraft swap
Goal: operations swaps the aircraft on the flight. Add ["swap_aircraft", aircraft], which installs the new seat map and re-seats everyone who was seated. This also ends an assumption of the earlier Parts: the cabin of a flight can now change.
Rules
unknown aircraftif the type does not exist (nothing changes). Swapping to the type already in service is allowed. Passengers who are not seated stay unseated and are not touched.- Keep. A seated passenger keeps their seat label if that label exists on the new aircraft, is not blocked there, and is *compatible*: the new seat is
exitand the passenger is exit-row eligible and not holding a paid seat; or the new seat islegroomand the passenger holds a paid seat; or the new seat isstandardand the passenger does not hold a paid seat. - Stranded children. Using the new layout, a kept child who is not adjacent to a *kept* adult of the same booking is not kept either.
- Refund. Every passenger who is not kept and holds a paid seat gets 4 500 refunded (booking
refundedgoes up) and no longer holds a paid seat. - Re-seat. For each booking in creation order that has passengers who were seated but not kept, run the check-in plan of Part 1 on exactly those passengers (kept passengers act as the already seated members; members who were never seated are left alone). The plan uses the new cabin. If the plan fails, those passengers end up unseated.
- Result:
{"kept": n, "moved": [[pid, old_seat, new_seat], ...], "unseated": [pid, ...], "refunded": [[pid, old_seat, 4500], ...]}.movedandunseatedfollow booking creation order, then booking order.refundedis ordered by old seat, front row first, letters in the old layout's order.movedlists every passenger re-seated in step 5, even if the new label equals the old one. - Everything else keeps working on the new cabin:
assign,change,refund_seat,checkinandinvoice(refunds made by the swap count ininvoice).
Worked example
[["open_flight", "A320"],
["book", "fam", [["mum", 34, ""], ["kid", 8, ""]]],
["book", "duo", [["x", 30, "T"], ["y", 31, ""]]],
["change", "fam", [["mum", "5B"], ["kid", "5C"]]],
["assign", "x", "12A"],
["swap_aircraft", "E175"],
["booking_seats", "duo"]]returns
[true, true, true, true, true,
{"kept": 1, "moved": [["kid", "5C", "5A"], ["x", "12A", "12B"]], "unseated": [], "refunded": []},
[["x", "12B"], ["y", null]]](On the E175 5B and 5C are on opposite sides of the aisle, so kid is stranded; 12A is blocked there.)
Notes
models.py,catalog.py,config.py,errors.pyandloadgen.pyare known-good.- You may use the AI assistant freely; you are expected to verify what it produces.
- Later Parts must not break earlier ones; hidden tests re-check earlier behaviour, including on large generated flights (
bulk_book), so keep an eye on how often you rescan the cabin. - Parts 1-2 is a solid result; Part 3 is the hardest.
Example 1
- Input:
- [["open_flight","A320"],["book","fam",[["mum",34,"T"],["kid",8,""],["tot",5,""]]],["book","solo",[["bo",29,"W"]]],["assign","mum","5C"],["assign","kid","5D"],["assign","mum","5B"],["assign","kid","5A"],["checkin","fam"],["checkin","solo"],["booking_seats","fam"]]
- Output:
- [true,true,true,true,"error: child not with adult",true,true,[["tot","5C"]],[["bo","24A"]],[["mum","5B"],["kid","5A"],["tot","5C"]]]
- Explanation
- 5C and 5D are on opposite sides of the aisle, so the child cannot sit at 5D. Check-in seats tot beside mum in the only free seat beside 5B, and the window-seeking solo passenger gets the first window in scan order.
Example 2
- Input:
- [["open_flight","A320"],["book","fam",[["mum",34,""],["kid",8,""]]],["assign","mum","2A"],["change","fam",[["mum","2A"],["kid","2B"]],"pay"],["invoice","fam"],["change","fam",[["mum","7A"]]],["change","fam",[["mum","7A"],["kid","7B"]]],["invoice","fam"]]
- Output:
- [true,true,"error: payment required",true,{"charged":9000,"refunded":0,"net":9000,"paid_seats":[["mum","2A"],["kid","2B"]]},"error: child not with adult",true,{"charged":9000,"refunded":9000,"net":0,"paid_seats":[]}]
- Explanation
- Legroom seats are bought with a group change in pay mode. Moving only the adult would strand the child, but moving both works and refunds both paid seats.
Example 3
- Input:
- [["open_flight","A320"],["book","fam",[["mum",34,""],["kid",8,""]]],["book","duo",[["x",30,"T"],["y",31,""]]],["change","fam",[["mum","5B"],["kid","5C"]]],["assign","x","12A"],["swap_aircraft","E175"],["booking_seats","duo"]]
- Output:
- [true,true,true,true,true,{"kept":1,"moved":[["kid","5C","5A"],["x","12A","12B"]],"unseated":[],"refunded":[]},[["x","12B"],["y",null]]]
- Explanation
- On the E175 the aisle separates 5B and 5C, so the child is displaced and re-seated beside the kept adult; 12A is blocked there so x moves to 12B.
Guidelines
- Python 3 standard library only
- Every operation returns exactly one value; errors are returned as "error: <code>" strings, never raised
- Seat plans are all-or-nothing: a failed check-in, change or refund leaves the flight and the invoices untouched
- Hidden tests check in flights of several hundred passengers generated with bulk_book and compare whole seat maps by hash