"""Calendar plugin contract — public interface for cross-plugin access.""" from __future__ import annotations from typing import Any from sqlalchemy import select from sqlalchemy.ext.asyncio import AsyncSession from app.plugins.builtins.calendar.models import Calendar, CalendarEntry, CalendarEntryLink from app.plugins.builtins.contracts import get_contract_registry class CalendarContract: """Public contract for the calendar plugin.""" contract_name = "calendar" Calendar = Calendar CalendarEntry = CalendarEntry CalendarEntryLink = CalendarEntryLink @staticmethod async def dsar_collect( db: AsyncSession, tenant_id: Any, user_id: Any ) -> dict[str, Any]: """GDPR Art. 15: collect calendar entries owned by the user.""" cal_entries = ( await db.execute( select(CalendarEntry).where( CalendarEntry.tenant_id == tenant_id, CalendarEntry.owner_id == user_id, CalendarEntry.deleted_at.is_(None), ).limit(1000) ) ).scalars().all() return { "calendar_entries": [ { "id": str(e.id), "title": e.title, "entry_type": e.entry_type, "start_at": e.start_at.isoformat() if e.start_at else None, "end_at": e.end_at.isoformat() if e.end_at else None, } for e in cal_entries ] } @classmethod def get_function(cls, name: str): """Return a callable exposed by this contract, or None if absent.""" return getattr(cls, name, None) # ─── self-registration ─── _contract = CalendarContract() get_contract_registry().register("calendar", _contract) # Backward-compatible local accessor _contract_instance: CalendarContract | None = None def get_contract() -> CalendarContract: global _contract_instance if _contract_instance is None: _contract_instance = CalendarContract() return _contract_instance __all__ = ["CalendarContract", "Calendar", "CalendarEntry", "CalendarEntryLink"]