import logging from datetime import datetime, timedelta, timezone from repositories.scrape_repository import scrape_repository from repositories.price_history_repository import price_history_repository from repositories.alert_repository import alert_repository logger = logging.getLogger(__name__) # CLAUDE.md §22 DATA_RETENTION — scrape_runs and alerts (read only) at # 90 days, price_history at 365. tenant_data (0 = retained until an # explicit deletion request) has no cron counterpart — that's # billing_service.delete_tenant_data(). audit_logs (730 days) has no # implementation here — see billing_service's note on why no # audit_logs collection exists in this schema. DATA_RETENTION_DAYS = { 'scrape_runs': 90, 'price_history': 365, 'alerts': 90, } def run_weekly_cleanup(): """ Deletes data past its retention window — the Sunday 3am n8n workflow's only job is to trigger this. All real logic lives here, not in n8n, matching every other internal route in this codebase. Data flow: now → per-collection cutoff = now - retention_days → scrape_runs older than cutoff: deleted → alerts older than cutoff AND read=true: deleted (unread alerts are never auto-deleted, regardless of age) → price_history older than cutoff: deleted → counts logged and returned """ now = datetime.now(timezone.utc) scrape_cutoff = (now - timedelta(days=DATA_RETENTION_DAYS['scrape_runs'])).isoformat() scrape_runs_deleted = 0 for run in scrape_repository.list_older_than(scrape_cutoff): scrape_repository.delete(run['id']) scrape_runs_deleted += 1 alert_cutoff = (now - timedelta(days=DATA_RETENTION_DAYS['alerts'])).isoformat() alerts_deleted = 0 for alert in alert_repository.list_read_older_than(alert_cutoff): alert_repository.delete(alert['id']) alerts_deleted += 1 price_history_cutoff = (now - timedelta(days=DATA_RETENTION_DAYS['price_history'])).isoformat() price_history_deleted = 0 for record in price_history_repository.list_older_than(price_history_cutoff): price_history_repository.delete(record['id']) price_history_deleted += 1 result = { 'scrape_runs_deleted': scrape_runs_deleted, 'alerts_deleted': alerts_deleted, 'price_history_deleted': price_history_deleted, } logger.info(f'Weekly data cleanup: {result}') return result