feat(hutches): split hutches by compartments #11
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/hutch-compartments"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Model hutch floors as compartments
Hutches split by floor could only be described as fully partitioned or fully open. Hutch N is neither: Smokey, Burt, Coco, Treacle, Toffee and Maple share the bottom and middle floors while Gareth has the top to himself. There was no way to record that.
The root cause is that a
Hutchrow was already a living space, not a floor — occupants, cleaning date and playtime all hang off it, and all three describe a space rather than a shelf.floorbeing a single string was the only thing stopping a space from covering more than one floor.Changes
Hutch.floor: str→Hutch.floors: list[str](JSON). A row is now explicitly a compartment: the set of floors rabbits can move freely between.floor_label,floor_rankandis_whole_hutchderive from it. A compartment spanning a complete structure is still named by its letter alone, so a fully open hutch reads as "N", not "N - Top + Middle + Bottom".Floors editor. A Floors button on each hutch opens a dialog with a structure picker (single / double / triple) and a ramp or divider toggle for each boundary — covering split, join, add floor and remove floor in one place. Previously there was no way to change a hutch's floors at all short of hand-written SQL.
Saving reconciles compartments against the new layout: each one claims the existing row it shares the most floors with, so cleaning history and occupants stay with the space they belong to. Merged or deleted compartments hand their rabbits to the surviving compartment they overlap most, so nothing is orphaned. Splitting N leaves the six rabbits and the existing history on the Middle+Bottom row and creates an empty Top; the odd rabbit out is then moved with a normal rabbit edit.
Rendering. The list shows one row per physical floor with each compartment's cells
rowspan-ed across its floors, so the table looks like the hutch. The same structure picker is reused on the New Hutch page.Fresh-install fixes (found while testing).
create_all()now runs before the startup migrations — three unguardedALTER TABLEstatements assumed tablesthat don't exist yet, so the app could not boot against an empty database. That surfaced a second gap: a fresh install had no way to create its first account.
Added
rabbitdb --create-user(podman exec -it <container> rabbitdb --create-user), which prompts for username, role and password, and a login-pagehint pointing at it when the users table is empty. The password is prompt-only, never a flag, so it stays out of shell history and
ps.Migration
Runs automatically at startup, following the existing in-place pattern in
main.py. Partitioned rows backfill to["Top"]etc.; the open hutches become one compartment spanning their structure — J/N/O all three floors, P/Q["Top","Bottom"], House and Shed staying floorless. Verified against a Postgres 18 cluster seeded to production's exact shape, and idempotent across repeated boots.It drops the
floorcolumn and is not reversible — take a snapshot before deploying.Breaking
/api/v1/hutchesnow returns"floors": ["Top","Middle","Bottom"]instead of"floor": null. Any external consumer needs updating.Testing
~60 assertions over the real routes covering split, merge, floor removal, floor addition, hutch creation, the rabbit picker, API and delete; 24 pty-driven checks of
--create-user; migration verified on production-shaped Postgres data in both the empty-database and legacy-database directions; and the N split driven through the browser end to end.