63 lines
2.4 KiB
Python
63 lines
2.4 KiB
Python
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
|