"""Persistent state for the order form (one JSON file in WORK_DIR volume). Per project (keyed by Kód zakázky) we keep: - nazev: Název zakázky - last: {field: last-used value} → used to prefill - hist: {field: {value: count}} → all values ever used, with counts (so the UI can offer them, most-used on top) Plus global meta: last order number (for the +1 suggestion). """ import json import os import re import threading from pathlib import Path _STORE = Path(os.getenv("WORK_DIR", "/tmp/offer-to-order")) / "order_state.json" _lock = threading.Lock() # Fields remembered per project (Název is stored separately as "nazev"). STICKY = [ "misto_dodani", "kontaktni_osoba", "vystavil", "splatnost", "zaruka", "zpusob_platby", "zpusob_odberu", "zpusob_dopravy", "forma_objednavky", "fakturujte", "popis", ] def _load() -> dict: try: d = json.loads(_STORE.read_text(encoding="utf-8")) except Exception: d = {} d.setdefault("projects", {}) d.setdefault("meta", {}) return d def _save(d: dict) -> None: _STORE.parent.mkdir(parents=True, exist_ok=True) _STORE.write_text(json.dumps(d, ensure_ascii=False, indent=2), encoding="utf-8") def _normalize(rec: dict) -> dict: """Upgrade an old flat-style record to the {nazev,last,hist} shape.""" if "last" in rec or "hist" in rec: rec.setdefault("last", {}) rec.setdefault("hist", {}) return rec nazev = rec.get("nazev", "") last, hist = {}, {} for k in STICKY: if rec.get(k) not in (None, ""): last[k] = rec[k] hist[k] = {rec[k]: 1} return {"nazev": nazev, "last": last, "hist": hist} # ── Projects ── def list_projects() -> list[dict]: projects = _load()["projects"] return sorted( [{"kod": k, "nazev": _normalize(v).get("nazev", "")} for k, v in projects.items()], key=lambda p: p["kod"].lower(), ) def get_defaults(code: str) -> dict: """Prefill values (last used) + a _history map ranked most-used first.""" code = (code or "").strip() if not code: return {} raw = _load()["projects"].get(code) if not raw: return {} rec = _normalize(raw) out = dict(rec["last"]) if rec.get("nazev"): out["nazev_zakazky"] = rec["nazev"] history = {} for field, counts in rec.get("hist", {}).items(): ranked = sorted(counts.items(), key=lambda kv: -kv[1]) history[field] = [{"value": v, "count": c} for v, c in ranked] out["_history"] = history return out def upsert_project(code: str, nazev: str) -> None: code = (code or "").strip() if not code: return with _lock: d = _load() rec = _normalize(d["projects"].get(code, {})) rec["nazev"] = (nazev or "").strip() d["projects"][code] = rec _save(d) def delete_project(code: str) -> None: code = (code or "").strip() with _lock: d = _load() if code in d["projects"]: del d["projects"][code] _save(d) def delete_history_value(code: str, field: str, value: str) -> None: """Remove one remembered value from a project field's history.""" code = (code or "").strip() if not code or not field: return with _lock: d = _load() raw = d["projects"].get(code) if not raw: return rec = _normalize(raw) counts = rec["hist"].get(field) if counts and value in counts: del counts[value] if not counts: rec["hist"].pop(field, None) if rec["last"].get(field) == value: remaining = rec["hist"].get(field) if remaining: rec["last"][field] = max(remaining, key=remaining.get) else: rec["last"].pop(field, None) d["projects"][code] = rec _save(d) def save_defaults(code: str, order: dict) -> None: """Record sticky-field values for a project: update 'last' and bump the frequency count in 'hist' for each non-empty value.""" code = (code or "").strip() if not code: return with _lock: d = _load() rec = _normalize(d["projects"].get(code, {})) if order.get("nazev_zakazky"): rec["nazev"] = order["nazev_zakazky"] for k in STICKY: v = order.get(k) if v in (None, ""): continue v = str(v) rec["last"][k] = v rec["hist"].setdefault(k, {}) rec["hist"][k][v] = rec["hist"][k].get(v, 0) + 1 d["projects"][code] = rec _save(d) # ── Order-number counter ── def next_order_number() -> str: return _increment(_load()["meta"].get("last_order_number") or "") def _increment(s: str) -> str: if not s: return "" m = re.search(r"(\d+)(\D*)$", s) if not m: return s num = m.group(1) return s[: m.start(1)] + str(int(num) + 1).zfill(len(num)) + m.group(2) def remember_order_number(num: str) -> None: num = (num or "").strip() if not num: return with _lock: d = _load() d["meta"]["last_order_number"] = num _save(d)