import uuid from datetime import datetime, timedelta, timezone from repositories.pocketbase_client import pb_client from repositories.repo_config import USE_MOCK_REPOSITORIES PRICE_HISTORY = 'price_history' PRODUCTS = 'products' BATTLECARDS = 'battlecards' TENANTS = 'tenants' DIGEST_LOGS = 'digest_logs' class BriefRepository: """ Read-only aggregation across price_history/products/battlecards/tenants for the weekly brief email, plus the digest_logs write. This is data retrieval (multi-collection reads), not decision-making, so it stays in the repository layer per brief_service's call pattern in CLAUDE.md §13. """ def get_weekly_data(self, tenant_id): """ Pulls everything brief_service needs to render brief.html for one tenant's past 7 days. Data flow: tenant_id → tenant record (admin_email) → price_history since 7 days ago → products where is_new since 7 days ago → battlecards updated since 7 days ago → { admin_email, week_of, price_changes, new_products, battlecard_updates } """ tenant = pb_client.get_one(TENANTS, tenant_id) since = (datetime.now(timezone.utc) - timedelta(days=7)).isoformat() price_changes = pb_client.list( PRICE_HISTORY, filter_str=f'tenant_id = "{tenant_id}" && scraped_at >= "{since}"', sort='-scraped_at', per_page=200 ) new_products = pb_client.list( PRODUCTS, filter_str=f'tenant_id = "{tenant_id}" && is_new = true && first_seen >= "{since}"', sort='-first_seen', per_page=200 ) battlecard_updates = pb_client.list( BATTLECARDS, filter_str=f'tenant_id = "{tenant_id}" && generated_at >= "{since}"', sort='-generated_at', per_page=200 ) return { 'admin_email': tenant.get('email') if tenant else None, 'tenant_name': tenant.get('name') if tenant else None, 'week_of': datetime.now(timezone.utc).strftime('%Y-%m-%d'), 'price_changes': price_changes, 'new_products': new_products, 'battlecard_updates': battlecard_updates, } def log_digest(self, tenant_id, digest_type, recipient_email=None, status='sent'): """Records that a brief/digest was sent (or failed to send).""" return pb_client.create(DIGEST_LOGS, { 'tenant_id': tenant_id, 'digest_type': digest_type, 'recipient_email': recipient_email, 'status': status, }) class MockBriefRepository: """In-memory stand-in for BriefRepository — returns empty/zeroed aggregates.""" def __init__(self): self._tenants = {} self._logs = {} def seed_tenant(self, tenant_id, tenant): self._tenants[tenant_id] = tenant def get_weekly_data(self, tenant_id): tenant = self._tenants.get(tenant_id, {}) return { 'admin_email': tenant.get('email'), 'tenant_name': tenant.get('name'), 'week_of': datetime.now(timezone.utc).strftime('%Y-%m-%d'), 'price_changes': [], 'new_products': [], 'battlecard_updates': [], } def log_digest(self, tenant_id, digest_type, recipient_email=None, status='sent'): record_id = str(uuid.uuid4()) record = { 'id': record_id, 'tenant_id': tenant_id, 'digest_type': digest_type, 'recipient_email': recipient_email, 'status': status, } self._logs[record_id] = record return record brief_repository = MockBriefRepository() if USE_MOCK_REPOSITORIES else BriefRepository()