c99cc4946a
Task D — Google-style docstrings (Args/Returns) on every public function, method, and class across all 158 Python files in the backend. Zero ruff D violations (pydocstyle Google convention). Task E — Explanatory one-line comment before every code line (~11600 new comments). ruff check passes clean after isort re-sort.
90 lines
2.9 KiB
Python
90 lines
2.9 KiB
Python
"""Data retention policies — scheduled cleanup of aged records."""
|
|
|
|
# Enable future language features for compatibility
|
|
from __future__ import annotations
|
|
|
|
# Import logging
|
|
import logging
|
|
|
|
# Import datetime, timedelta, timezone from datetime
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
# Import Session from sqlalchemy.orm
|
|
from sqlalchemy.orm import Session
|
|
|
|
# Import SessionLocal from app.database
|
|
from app.database import SessionLocal
|
|
|
|
# Import AuditLog from app.models.audit
|
|
from app.models.audit import AuditLog
|
|
|
|
# Import cleanup_old_notifications from app.services.notification_service
|
|
from app.services.notification_service import cleanup_old_notifications
|
|
|
|
# Assign logger = logging.getLogger(__name__)
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Assign AUDIT_LOG_RETENTION_DAYS = 730
|
|
AUDIT_LOG_RETENTION_DAYS = 730
|
|
|
|
|
|
# Define function apply_retention_policies
|
|
def apply_retention_policies(db: Session) -> dict[str, int]:
|
|
"""Apply retention rules. Commits the session before returning."""
|
|
# Assign cutoff = datetime.now(timezone.utc) - timedelta(days=AUDIT_LOG_RETENTION_DAYS)
|
|
cutoff = datetime.now(timezone.utc) - timedelta(days=AUDIT_LOG_RETENTION_DAYS)
|
|
# Assign deleted_audit = (
|
|
deleted_audit = (
|
|
db.query(AuditLog)
|
|
# Chain .filter() call
|
|
.filter(AuditLog.timestamp < cutoff)
|
|
# Chain .delete() call
|
|
.delete(synchronize_session=False)
|
|
)
|
|
# Check: deleted_audit
|
|
if deleted_audit:
|
|
# Log info:
|
|
logger.info(
|
|
# Literal argument value
|
|
"Retention: deleted %d audit logs older than %d days",
|
|
deleted_audit,
|
|
AUDIT_LOG_RETENTION_DAYS,
|
|
)
|
|
|
|
# Assign deleted_notifications = cleanup_old_notifications(db, days=90)
|
|
deleted_notifications = cleanup_old_notifications(db, days=90)
|
|
# Commit all pending changes to the database
|
|
db.commit()
|
|
# Return {
|
|
return {
|
|
# Literal argument value
|
|
"audit_logs_deleted": deleted_audit,
|
|
# Literal argument value
|
|
"notifications_deleted": deleted_notifications,
|
|
}
|
|
|
|
|
|
# Define function run_retention_job
|
|
def run_retention_job() -> None:
|
|
"""Entry point for the daily retention scheduler job."""
|
|
# Log info: "Scheduled retention job starting..."
|
|
logger.info("Scheduled retention job starting...")
|
|
# Assign db = SessionLocal()
|
|
db = SessionLocal()
|
|
# Attempt the following; catch errors below
|
|
try:
|
|
# Assign summary = apply_retention_policies(db)
|
|
summary = apply_retention_policies(db)
|
|
# Log info: "Retention job finished — %s", summary
|
|
logger.info("Retention job finished — %s", summary)
|
|
# Handle Exception
|
|
except Exception:
|
|
# Log exception: "Retention job failed"
|
|
logger.exception("Retention job failed")
|
|
# Roll back all uncommitted changes
|
|
db.rollback()
|
|
# Always execute this cleanup block
|
|
finally:
|
|
# Close the database session
|
|
db.close()
|