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