Files
AI_portal/offer-to-order/store.py
2026-07-07 13:16:26 +02:00

182 lines
5.2 KiB
Python

"""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)