2026-01-26 11:09:40 -05:00

189 lines
6.5 KiB
Python

"""
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