105 lines
3.6 KiB
Python
105 lines
3.6 KiB
Python
import uuid
|
|
from repositories.pocketbase_client import pb_client
|
|
from repositories.repo_config import USE_MOCK_REPOSITORIES
|
|
|
|
COLLECTION = 'alerts'
|
|
|
|
|
|
class AlertRepository:
|
|
"""Data access for the `alerts` collection."""
|
|
|
|
def create(self, data):
|
|
return pb_client.create(COLLECTION, data)
|
|
|
|
def get_by_id(self, alert_id):
|
|
return pb_client.get_one(COLLECTION, alert_id)
|
|
|
|
def list_unread(self, tenant_id):
|
|
return pb_client.list(
|
|
COLLECTION, filter_str=f'tenant_id = "{tenant_id}" && read = false', sort='-created', per_page=200
|
|
)
|
|
|
|
def list_undelivered_email(self, tenant_id):
|
|
"""
|
|
Alerts not yet included in a digest email — independent of the
|
|
dashboard `read` flag, since a PM reading something on the
|
|
dashboard first shouldn't make it disappear from that day's
|
|
digest (CLAUDE.md §6 alerts schema: delivered_email tracks
|
|
digest inclusion, read tracks dashboard dismissal — separate
|
|
concerns).
|
|
"""
|
|
return pb_client.list(
|
|
COLLECTION, filter_str=f'tenant_id = "{tenant_id}" && delivered_email = false',
|
|
sort='-created', per_page=200
|
|
)
|
|
|
|
def list_recent(self, tenant_id, limit=50):
|
|
return pb_client.list(COLLECTION, filter_str=f'tenant_id = "{tenant_id}"', sort='-created', per_page=limit)
|
|
|
|
def mark_read(self, alert_id):
|
|
return pb_client.update(COLLECTION, alert_id, {'read': True})
|
|
|
|
def mark_email_delivered(self, alert_id):
|
|
return pb_client.update(COLLECTION, alert_id, {'delivered_email': True})
|
|
|
|
def list_read_older_than(self, cutoff_iso):
|
|
"""Used by the weekly data-cleanup job — CLAUDE.md §22 DATA_RETENTION (90 days, read alerts only)."""
|
|
return pb_client.list(COLLECTION, filter_str=f'read = true && created < "{cutoff_iso}"', per_page=500)
|
|
|
|
def delete(self, alert_id):
|
|
return pb_client.delete(COLLECTION, alert_id)
|
|
|
|
|
|
class MockAlertRepository:
|
|
"""In-memory stand-in for AlertRepository."""
|
|
|
|
def __init__(self):
|
|
self._records = {}
|
|
|
|
def create(self, data):
|
|
record_id = data.get('id') or str(uuid.uuid4())
|
|
record = {'id': record_id, 'read': False, **data}
|
|
self._records[record_id] = record
|
|
return record
|
|
|
|
def get_by_id(self, alert_id):
|
|
return self._records.get(alert_id)
|
|
|
|
def list_unread(self, tenant_id):
|
|
return [r for r in self._records.values() if r.get('tenant_id') == tenant_id and not r.get('read')]
|
|
|
|
def list_undelivered_email(self, tenant_id):
|
|
return [
|
|
r for r in self._records.values()
|
|
if r.get('tenant_id') == tenant_id and not r.get('delivered_email')
|
|
]
|
|
|
|
def list_recent(self, tenant_id, limit=50):
|
|
items = [r for r in self._records.values() if r.get('tenant_id') == tenant_id]
|
|
items.sort(key=lambda r: r.get('created') or '', reverse=True)
|
|
return items[:limit]
|
|
|
|
def mark_read(self, alert_id):
|
|
if alert_id not in self._records:
|
|
return None
|
|
self._records[alert_id]['read'] = True
|
|
return self._records[alert_id]
|
|
|
|
def mark_email_delivered(self, alert_id):
|
|
if alert_id not in self._records:
|
|
return None
|
|
self._records[alert_id]['delivered_email'] = True
|
|
return self._records[alert_id]
|
|
|
|
def list_read_older_than(self, cutoff_iso):
|
|
return [
|
|
r for r in self._records.values()
|
|
if r.get('read') and (r.get('created') or '') < cutoff_iso
|
|
]
|
|
|
|
def delete(self, alert_id):
|
|
return self._records.pop(alert_id, None) is not None
|
|
|
|
|
|
alert_repository = MockAlertRepository() if USE_MOCK_REPOSITORIES else AlertRepository()
|