"""Feedback module — feature requests / feedback / bug reports from every user. Sits OUTSIDE the Dashboards/Workflows nav groups (its own sidebar entry). Submissions accumulate in the platform's writable HF Dataset store (core/store.py, key 'feedback') so they survive restarts and are shared across users/instances. The record shape is deliberately structured so an AI staging pass can later triage items into the build workflow (type/module/importance/status + free-text details). No Odoo involvement at all — this module never touches ERP data. Statuses: New (just submitted) -> Staged (AI/owner queued it for a build cycle) -> Planned -> Done | Declined. Triage is admin-only; submitting is open to every signed-in user. """ import time import datetime as dt import core.store as store KEY = 'feedback' TYPES = ['Feature request', 'Feedback', 'Bug'] IMPORTANCE = ['Must have', 'Nice to have', 'Idea'] STATUSES = ['New', 'Staged', 'Planned', 'Done', 'Declined'] def available(): return store.available() def items(fresh=True): """All submissions, newest first. Lenient read (display only).""" data = store.get(KEY, fresh=fresh) or {} out = list(data.get('items', [])) out.sort(key=lambda r: r.get('ts', ''), reverse=True) return out def add(user, name, ftype, module, title, details, importance): """Append one submission (strict read-modify-write — a transient read error aborts, never clobbers). Returns the new id.""" rec = { 'id': f'fb-{int(time.time() * 1000)}-{user}', 'ts': dt.datetime.now().strftime('%Y-%m-%d %H:%M'), 'user': user, 'name': name, 'type': ftype, 'module': module, 'title': (title or '').strip(), 'details': (details or '').strip(), 'importance': importance, 'status': 'New', 'note': '', } store.update(KEY, lambda d: {**d, 'items': d.get('items', []) + [rec]}) return rec['id'] def set_fields(fid, **fields): """Update triage fields (status / note) on one submission by id.""" def _fn(d): for it in d.get('items', []): if it.get('id') == fid: it.update(fields) return d store.update(KEY, _fn) def counts(rows=None): """Status -> count, for the header KPIs.""" rows = items() if rows is None else rows out = {s: 0 for s in STATUSES} for r in rows: out[r.get('status', 'New')] = out.get(r.get('status', 'New'), 0) + 1 return out