"""Public contract for the contacts plugin. Exposes the symbols that other core modules and plugins need without importing from internal modules directly (Block C7: dashboard counts). """ from __future__ import annotations from typing import Any from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.visibility import apply_visibility_filter from app.models.contact import Contact from app.plugins.builtins.contracts import get_contract_registry class ContactsContract: """Public API surface for the contacts plugin.""" contract_name = "contacts" @staticmethod async def get_counts( db: AsyncSession, tenant_id: Any, user_id: Any, is_system_admin: bool = False, ) -> dict[str, int]: """Return visibility-filtered contact/company/person counts.""" queries = [] for type_filter in (None, "company", "person"): query = select(func.count(Contact.id)).where( Contact.tenant_id == tenant_id, Contact.deleted_at.is_(None), ) if type_filter is not None: query = query.where(Contact.type == type_filter) query = await apply_visibility_filter( db, query, "contact", Contact, user_id, tenant_id, is_system_admin ) queries.append(query) results = [((await db.execute(q)).scalar() or 0) for q in queries] return { "contacts": results[0], "companies": results[1], "persons": results[2], "total": results[0], } @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 = ContactsContract() get_contract_registry().register("contacts", _contract)