""" Project domain metadata enrichment. Handles Project, ProjectSession, and ProjectPunchlist entities. """ from typing import Dict, Any, Set from asgiref.sync import sync_to_async from core.models.enums import EventTypeChoices PROJECT_EVENTS: Set[str] = { EventTypeChoices.PROJECT_CREATED, EventTypeChoices.PROJECT_UPDATED, EventTypeChoices.PROJECT_STATUS_CHANGED, EventTypeChoices.PROJECT_COMPLETED, EventTypeChoices.PROJECT_CANCELLED, EventTypeChoices.PROJECT_DISPATCHED, EventTypeChoices.PROJECT_SESSION_OPENED, EventTypeChoices.PROJECT_SESSION_CLOSED, EventTypeChoices.PROJECT_SESSION_REVERTED, EventTypeChoices.PROJECT_TASK_COMPLETED, EventTypeChoices.PROJECT_TASK_UNCOMPLETED, EventTypeChoices.PROJECT_PUNCHLIST_CREATED, EventTypeChoices.PROJECT_PUNCHLIST_UPDATED, EventTypeChoices.PROJECT_PUNCHLIST_DELETED, } async def enrich_project_domain(entity_type: str, entity_id: str) -> Dict[str, Any]: """Enrich metadata for project-domain events.""" if entity_type == 'Project': return await _load_project_metadata(entity_id) elif entity_type == 'ProjectSession': return await _load_project_session_metadata(entity_id) elif entity_type == 'ProjectPunchlist': return await _load_project_punchlist_metadata(entity_id) return {} async def _load_project_metadata(entity_id: str) -> Dict[str, Any]: """Load metadata from a Project entity.""" from core.models.project import Project def _load(): try: project = Project.objects.select_related( 'customer', 'account_address', 'account_address__account', 'account_address__account__customer', ).get(pk=entity_id) return _extract_project_metadata(project) except Project.DoesNotExist: return {} return await sync_to_async(_load)() async def _load_project_session_metadata(entity_id: str) -> Dict[str, Any]: """Load metadata from a ProjectSession entity.""" from core.models.session import ProjectSession def _load(): try: session = ProjectSession.objects.select_related( 'project', 'customer', 'account_address', 'account_address__account', 'account_address__account__customer', ).get(pk=entity_id) return _extract_project_session_metadata(session) except ProjectSession.DoesNotExist: return {} return await sync_to_async(_load)() async def _load_project_punchlist_metadata(entity_id: str) -> Dict[str, Any]: """Load metadata from a ProjectPunchlist entity.""" from core.models.project_punchlist import ProjectPunchlist def _load(): try: punchlist = ProjectPunchlist.objects.select_related( 'project', 'project__customer', 'project__account_address', 'project__account_address__account', ).get(pk=entity_id) metadata = {'project_id': str(punchlist.project_id)} if punchlist.project: metadata.update(_extract_project_metadata(punchlist.project)) return metadata except ProjectPunchlist.DoesNotExist: return {} return await sync_to_async(_load)() def _extract_project_metadata(project) -> Dict[str, Any]: """Extract metadata dict from a pre-loaded Project object.""" metadata = {} # Project name if project.name: metadata['project_name'] = project.name # Date if project.date: metadata['date'] = str(project.date) # Projects can be affiliated with account (via account_address) OR direct customer if project.account_address_id and project.account_address: # Account-affiliated project if project.account_address.account: metadata['account_name'] = project.account_address.account.name or '' if project.account_address.account.customer: metadata['customer_name'] = project.account_address.account.customer.name or '' # Address from account_address address_parts = [] if project.account_address.street_address: address_parts.append(project.account_address.street_address) if project.account_address.city: address_parts.append(project.account_address.city) if address_parts: metadata['address'] = ', '.join(address_parts) elif project.customer: # Direct customer project (no account affiliation) metadata['customer_name'] = project.customer.name or '' # Use customer name as account_name fallback for template compatibility metadata['account_name'] = project.customer.name or '' # Address from project fields address_parts = [] if project.street_address: address_parts.append(project.street_address) if project.city: address_parts.append(project.city) if address_parts: metadata['address'] = ', '.join(address_parts) return metadata def _extract_project_session_metadata(session) -> Dict[str, Any]: """Extract metadata dict from a preloaded ProjectSession object.""" metadata = {} # Project ID for reference if session.project_id: metadata['project_id'] = str(session.project_id) # Project name and date if session.project: if session.project.name: metadata['project_name'] = session.project.name if session.project.date: metadata['date'] = str(session.project.date) # Account/customer via account_address path if session.account_address_id and session.account_address: if session.account_address.account: metadata['account_name'] = session.account_address.account.name or '' if session.account_address.account.customer: metadata['customer_name'] = session.account_address.account.customer.name or '' # Address address_parts = [] if session.account_address.street_address: address_parts.append(session.account_address.street_address) if session.account_address.city: address_parts.append(session.account_address.city) if address_parts: metadata['address'] = ', '.join(address_parts) elif session.customer: # Direct customer project metadata['customer_name'] = session.customer.name or '' metadata['account_name'] = session.customer.name or '' return metadata