"""Persistent selection preferences scoped to a library and its stable item ID.""" from pigway_frame_flow.errors import ErrorCode as E from pigway_frame_flow.errors import FlowError def ensure_compatible(starred, excluded): if starred and excluded: raise FlowError( E.CONFLICT, "Starred photos cannot be excluded; clear the existing mark first", 409, ) def flags(db, source_id, item_id): row = db.one( "SELECT starred,excluded FROM photo_preferences WHERE source_id=? AND item_id=?", (source_id, item_id), ) return {key: bool(row and row[key]) for key in ("starred", "excluded")} def set_flags(db, source_id, item_id, *, starred=None, excluded=None): with db.transaction() as connection: current = flags(db, source_id, item_id) if source_id == "@assets": meta = db.one( "SELECT starred FROM asset_metadata WHERE asset_id=?", (item_id,) ) current["starred"] = bool(meta and meta["starred"]) ensure_compatible( current["starred"] if starred is None else starred, current["excluded"] if excluded is None else excluded, ) connection.execute( "INSERT INTO photo_preferences(source_id,item_id,starred,excluded) VALUES(?,?,?,?) ON CONFLICT(source_id,item_id) DO UPDATE SET starred=coalesce(?,starred),excluded=coalesce(?,excluded)", ( source_id, item_id, int(starred or False), int(excluded or False), None if starred is None else int(starred), None if excluded is None else int(excluded), ), ) return flags(db, source_id, item_id) def annotate(db, source_id, items): preferences = { row["item_id"]: row for row in db.all( "SELECT item_id,starred,excluded FROM photo_preferences WHERE source_id=?", (source_id,), ) } return [ { **item, **{ key: bool(preferences.get(item["id"], {}).get(key)) for key in ("starred", "excluded") }, } for item in items ] def eligible(db, source_id, items): excluded = { row["item_id"] for row in db.all( "SELECT item_id FROM photo_preferences WHERE source_id=? AND excluded=1", (source_id,), ) } return [item for item in items if item["id"] not in excluded]