""" Compliance repositories — extracted from compliance/db/repository.py. Phase 1 Step 5: the monolithic repository module is decomposed per aggregate. Every repository class is re-exported from ``compliance.db.repository`` for backwards compatibility. """ import uuid from datetime import datetime, date, timezone from typing import List, Optional, Dict, Any, Tuple from sqlalchemy.orm import Session as DBSession, selectinload, joinedload from sqlalchemy import func, and_, or_ from compliance.db.models import ( RegulationDB, RequirementDB, ControlDB, ControlMappingDB, EvidenceDB, RiskDB, AuditExportDB, AuditSessionDB, AuditSignOffDB, AuditResultEnum, AuditSessionStatusEnum, RegulationTypeEnum, ControlDomainEnum, ControlStatusEnum, RiskLevelEnum, EvidenceStatusEnum, ExportStatusEnum, ServiceModuleDB, ModuleRegulationMappingDB, ) class AuditExportRepository: """Repository for audit exports.""" def __init__(self, db: DBSession): self.db = db def create( self, export_type: str, requested_by: str, export_name: Optional[str] = None, included_regulations: Optional[List[str]] = None, included_domains: Optional[List[str]] = None, date_range_start: Optional[date] = None, date_range_end: Optional[date] = None, ) -> AuditExportDB: """Create an export request.""" export = AuditExportDB( id=str(uuid.uuid4()), export_type=export_type, export_name=export_name or f"audit_export_{datetime.now().strftime('%Y%m%d_%H%M%S')}", requested_by=requested_by, included_regulations=included_regulations, included_domains=included_domains, date_range_start=date_range_start, date_range_end=date_range_end, ) self.db.add(export) self.db.commit() self.db.refresh(export) return export def get_by_id(self, export_id: str) -> Optional[AuditExportDB]: """Get export by ID.""" return self.db.query(AuditExportDB).filter(AuditExportDB.id == export_id).first() def get_all(self, limit: int = 50) -> List[AuditExportDB]: """Get all exports.""" return ( self.db.query(AuditExportDB) .order_by(AuditExportDB.requested_at.desc()) .limit(limit) .all() ) def update_status( self, export_id: str, status: ExportStatusEnum, file_path: Optional[str] = None, file_hash: Optional[str] = None, file_size_bytes: Optional[int] = None, error_message: Optional[str] = None, total_controls: Optional[int] = None, total_evidence: Optional[int] = None, compliance_score: Optional[float] = None, ) -> Optional[AuditExportDB]: """Update export status.""" export = self.get_by_id(export_id) if not export: return None export.status = status if file_path: export.file_path = file_path if file_hash: export.file_hash = file_hash if file_size_bytes: export.file_size_bytes = file_size_bytes if error_message: export.error_message = error_message if total_controls is not None: export.total_controls = total_controls if total_evidence is not None: export.total_evidence = total_evidence if compliance_score is not None: export.compliance_score = compliance_score if status == ExportStatusEnum.COMPLETED: export.completed_at = datetime.now(timezone.utc) export.updated_at = datetime.now(timezone.utc) self.db.commit() self.db.refresh(export) return export