ocado-grocy/ocado_grocy/order_state.py

56 lines
2.9 KiB
Python

"""Order-level completion, with conservative migration of the line journal."""
import json
import sqlite3
from types import SimpleNamespace
from .grocy import align_recorded_lines, item_fingerprint
from .pantry import classify
from .receipt import parse_document
def completed_orders(state, server, archive, config):
"""Read only: a downloaded receipt alone is never evidence of an import."""
if not state.exists():
return set()
with sqlite3.connect(f'{state.resolve().as_uri()}?mode=ro', uri=True) as db:
tables = {r[0] for r in db.execute("SELECT name FROM sqlite_master WHERE type='table'")}
tracked = dict(db.execute('SELECT order_id,status FROM loaded_orders WHERE server=?', (server,))) if 'loaded_orders' in tables else {}
completed = {order for order, status in tracked.items() if status == 'complete'}
if 'imports' not in tables:
return completed
legacy = {r[0] for r in db.execute('SELECT DISTINCT order_id FROM imports WHERE server=?', (server,))} - tracked.keys()
for order in legacy:
path = archive / f'{order}.json'
if not path.exists():
continue
try:
receipt = parse_document(json.loads(path.read_text()))
if receipt.order_id != order:
continue
receipt = align_recorded_lines(receipt, server, SimpleNamespace(db=db))
rows = {line:(fingerprint,status) for line,fingerprint,status in db.execute(
'SELECT line,fingerprint,status FROM imports WHERE server=? AND order_id=?', (server,order))}
if any(status != 'done' for _,status in rows.values()):
continue
selected = [(line,item) for line,item in enumerate(receipt.items,1)
if classify(item,config.get('pantry',{}).get('overrides',{})).include]
if all(rows.get(line) == (item_fingerprint(receipt,item),'done') for line,item in selected):
completed.add(order)
except (ValueError, KeyError, TypeError):
# A changed or incomplete receipt needs the normal reconciliation path.
continue
return completed
def mark_orders(state, server, orders, status):
if not orders:
return
state.parent.mkdir(parents=True, exist_ok=True, mode=0o700)
with sqlite3.connect(state) as db:
db.execute('''CREATE TABLE IF NOT EXISTS loaded_orders (
server TEXT NOT NULL, order_id TEXT NOT NULL, status TEXT NOT NULL,
updated_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY(server,order_id))''')
db.executemany('''INSERT INTO loaded_orders(server,order_id,status) VALUES (?,?,?)
ON CONFLICT(server,order_id) DO UPDATE SET status=excluded.status, updated_at=CURRENT_TIMESTAMP''',
[(server,order,status) for order in orders])