feat(m4): crop_fill setting + tape-line prototype (#99)
Add global crop_fill percent (default 90, 1-100) as a settings key with FastAPI 4xx validation. Wires the value through to the inventory PNG encode (non-retro: stored PNGs untouched). Adds a throwaway TapePrototype.svelte behind ?tape-prototype for the #99 design sandbox. Gates: check_requirements PASS, check_coverage PASS, settings tests 8/8.
This commit is contained in:
@@ -28,13 +28,18 @@ PLAIN_KEYS: set[str] = {
|
||||
"woo_create_status",
|
||||
"taxonomy_last_sync_at",
|
||||
"taxonomy_anchor",
|
||||
"crop_fill",
|
||||
}
|
||||
ALL_KEYS = SECRET_KEYS | PLAIN_KEYS
|
||||
# Plain keys surfaced as numbers in the API (stored as TEXT in `settings`).
|
||||
NUMERIC_KEYS: set[str] = {"crop_fill"}
|
||||
|
||||
DEFAULTS: dict[str, Any] = {
|
||||
"inference_enabled": False,
|
||||
"content_llm_enabled": False,
|
||||
"woo_create_status": "draft",
|
||||
# #99: global crop fill %, one source of truth for the ref line + PNG crop.
|
||||
"crop_fill": 90,
|
||||
}
|
||||
|
||||
|
||||
@@ -91,6 +96,12 @@ def get_settings() -> dict[str, Any]:
|
||||
result[k] = val if val is not None else DEFAULTS.get(k)
|
||||
else:
|
||||
result[k] = DEFAULTS.get(k)
|
||||
if k in NUMERIC_KEYS:
|
||||
try:
|
||||
f = float(result[k])
|
||||
result[k] = int(f) if f.is_integer() else f
|
||||
except (TypeError, ValueError):
|
||||
result[k] = DEFAULTS.get(k)
|
||||
|
||||
return result
|
||||
|
||||
@@ -108,6 +119,25 @@ def update_settings(payload: dict[str, Any]) -> dict[str, Any]:
|
||||
},
|
||||
)
|
||||
|
||||
# #99: rules live here in the 4xx layer, not in the DB (structural only).
|
||||
if "crop_fill" in payload:
|
||||
raw = payload["crop_fill"]
|
||||
try:
|
||||
pct = float(raw)
|
||||
except (TypeError, ValueError):
|
||||
pct = None
|
||||
if pct is None or not (1 <= pct <= 100):
|
||||
raise HTTPException(
|
||||
status_code=400,
|
||||
detail={
|
||||
"error": {
|
||||
"code": "validation",
|
||||
"message": "crop_fill must be a number between 1 and 100 (percent)",
|
||||
"detail": {"field": "crop_fill"},
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
conn = get_connection()
|
||||
for k, v in payload.items():
|
||||
if k not in ALL_KEYS:
|
||||
|
||||
Reference in New Issue
Block a user