275 lines
8.4 KiB
Python
275 lines
8.4 KiB
Python
from datetime import date
|
|
from typing import Optional
|
|
import strawberry
|
|
from strawberry import ID
|
|
from django.db.models import Prefetch
|
|
from asgiref.sync import sync_to_async
|
|
from core.graphql.types.dashboard import (
|
|
AdminDashboardData,
|
|
TeamDashboardData,
|
|
CustomerDashboardData,
|
|
)
|
|
from core.models.service import Service
|
|
from core.models.project import Project
|
|
from core.models.invoice import Invoice
|
|
from core.models.report import Report
|
|
from core.models.scope_template import ScopeTemplate, AreaTemplate, TaskTemplate
|
|
from core.models.project_scope_template import (
|
|
ProjectScopeTemplate,
|
|
ProjectAreaTemplate,
|
|
ProjectTaskTemplate,
|
|
)
|
|
|
|
|
|
def parse_month_range(month: str) -> tuple[date, date]:
|
|
"""Parse a month string like '2024-01' into start and end dates."""
|
|
year, month_num = map(int, month.split('-'))
|
|
start = date(year, month_num, 1)
|
|
|
|
# Calculate end of month
|
|
if month_num == 12:
|
|
end = date(year + 1, 1, 1)
|
|
else:
|
|
end = date(year, month_num + 1, 1)
|
|
|
|
# End is exclusive, so subtract one day for inclusive range
|
|
from datetime import timedelta
|
|
end = end - timedelta(days=1)
|
|
|
|
return start, end
|
|
|
|
|
|
def _fetch_admin_dashboard_sync(
|
|
start: date,
|
|
end: date,
|
|
invoice_status: Optional[str],
|
|
) -> AdminDashboardData:
|
|
"""Synchronous database fetching for admin dashboard."""
|
|
# Services - optimized with prefetch for team_members
|
|
services = list(
|
|
Service.objects
|
|
.filter(date__gte=start, date__lte=end)
|
|
.select_related('account_address', 'account_address__account')
|
|
.prefetch_related('team_members')
|
|
.order_by('date', 'id')
|
|
)
|
|
|
|
# Projects - optimized with prefetch for team_members
|
|
projects = list(
|
|
Project.objects
|
|
.filter(date__gte=start, date__lte=end)
|
|
.select_related('account_address', 'account_address__account', 'customer')
|
|
.prefetch_related('team_members')
|
|
.order_by('date', 'id')
|
|
)
|
|
|
|
# Invoices - show all (pages need full list, not month-filtered)
|
|
invoices_qs = Invoice.objects.select_related('customer')
|
|
if invoice_status:
|
|
invoices_qs = invoices_qs.filter(status=invoice_status)
|
|
invoices = list(invoices_qs.order_by('-date', '-id'))
|
|
|
|
# Reports - show all (pages need full list, not month-filtered)
|
|
reports = list(
|
|
Report.objects
|
|
.select_related('team_member')
|
|
.order_by('-date', '-id')
|
|
)
|
|
|
|
# Service Scope Templates - with nested areas and tasks prefetched
|
|
task_prefetch = Prefetch(
|
|
'task_templates',
|
|
queryset=TaskTemplate.objects.order_by('order', 'id')
|
|
)
|
|
area_prefetch = Prefetch(
|
|
'area_templates',
|
|
queryset=AreaTemplate.objects.prefetch_related(task_prefetch).order_by('order', 'name')
|
|
)
|
|
service_scope_templates = list(
|
|
ScopeTemplate.objects
|
|
.prefetch_related(area_prefetch)
|
|
.order_by('name')
|
|
)
|
|
|
|
# Project Scope Templates - with nested categories and tasks prefetched
|
|
project_task_prefetch = Prefetch(
|
|
'task_templates',
|
|
queryset=ProjectTaskTemplate.objects.order_by('order', 'id')
|
|
)
|
|
category_prefetch = Prefetch(
|
|
'category_templates',
|
|
queryset=ProjectAreaTemplate.objects.prefetch_related(project_task_prefetch).order_by('order', 'name')
|
|
)
|
|
project_scope_templates = list(
|
|
ProjectScopeTemplate.objects
|
|
.prefetch_related(category_prefetch)
|
|
.order_by('name')
|
|
)
|
|
|
|
return AdminDashboardData(
|
|
services=services,
|
|
projects=projects,
|
|
invoices=invoices,
|
|
reports=reports,
|
|
service_scope_templates=service_scope_templates,
|
|
project_scope_templates=project_scope_templates,
|
|
)
|
|
|
|
|
|
def _fetch_team_dashboard_sync(
|
|
team_profile_id: str,
|
|
start: date,
|
|
end: date,
|
|
) -> TeamDashboardData:
|
|
"""Synchronous database fetching for team dashboard."""
|
|
# Services assigned to this team member
|
|
services = list(
|
|
Service.objects
|
|
.filter(
|
|
team_members__id=team_profile_id,
|
|
date__gte=start,
|
|
date__lte=end
|
|
)
|
|
.select_related('account_address', 'account_address__account')
|
|
.prefetch_related('team_members')
|
|
.order_by('date', 'id')
|
|
)
|
|
|
|
# Projects assigned to this team member
|
|
projects = list(
|
|
Project.objects
|
|
.filter(
|
|
team_members__id=team_profile_id,
|
|
date__gte=start,
|
|
date__lte=end
|
|
)
|
|
.select_related('account_address', 'account_address__account', 'customer')
|
|
.prefetch_related('team_members')
|
|
.order_by('date', 'id')
|
|
)
|
|
|
|
# Reports for this team member
|
|
reports = list(
|
|
Report.objects
|
|
.filter(
|
|
team_member_id=team_profile_id,
|
|
date__gte=start,
|
|
date__lte=end
|
|
)
|
|
.select_related('team_member')
|
|
.order_by('-date', '-id')
|
|
)
|
|
|
|
return TeamDashboardData(
|
|
services=services,
|
|
projects=projects,
|
|
reports=reports,
|
|
)
|
|
|
|
|
|
def _fetch_customer_dashboard_sync(
|
|
customer_id: str,
|
|
) -> CustomerDashboardData:
|
|
"""Synchronous database fetching for customer dashboard."""
|
|
# Services for customer's accounts
|
|
services = list(
|
|
Service.objects
|
|
.filter(account_address__account__customer_id=customer_id)
|
|
.select_related('account_address', 'account_address__account')
|
|
.prefetch_related('team_members')
|
|
.order_by('-date', '-id')[:100] # Limit for performance
|
|
)
|
|
|
|
# Projects for customer
|
|
projects = list(
|
|
Project.objects
|
|
.filter(customer_id=customer_id)
|
|
.select_related('account_address', 'account_address__account', 'customer')
|
|
.prefetch_related('team_members')
|
|
.order_by('-date', '-id')[:100] # Limit for performance
|
|
)
|
|
|
|
# Invoices for customer
|
|
invoices = list(
|
|
Invoice.objects
|
|
.filter(customer_id=customer_id)
|
|
.select_related('customer')
|
|
.order_by('-date', '-id')[:100] # Limit for performance
|
|
)
|
|
|
|
return CustomerDashboardData(
|
|
services=services,
|
|
projects=projects,
|
|
invoices=invoices,
|
|
)
|
|
|
|
|
|
@strawberry.type
|
|
class Query:
|
|
@strawberry.field(
|
|
name="adminDashboard",
|
|
description="Consolidated dashboard data for admin/team leader users. "
|
|
"Returns all services, projects, invoices, reports, and scope templates "
|
|
"for the given month in a single optimized query."
|
|
)
|
|
async def admin_dashboard(
|
|
self,
|
|
info,
|
|
month: str,
|
|
invoice_status: Optional[str] = None,
|
|
) -> AdminDashboardData:
|
|
"""Fetch all admin dashboard data in a single optimized query.
|
|
|
|
Args:
|
|
month: Month string in format 'YYYY-MM' (e.g., '2024-01')
|
|
invoice_status: Optional invoice status filter (e.g., 'SENT', 'PAID')
|
|
|
|
Returns:
|
|
AdminDashboardData with all dashboard entities
|
|
"""
|
|
start, end = parse_month_range(month)
|
|
return await sync_to_async(_fetch_admin_dashboard_sync)(start, end, invoice_status)
|
|
|
|
@strawberry.field(
|
|
name="teamDashboard",
|
|
description="Consolidated dashboard data for team member users. "
|
|
"Returns services and projects assigned to the requesting user."
|
|
)
|
|
async def team_dashboard(
|
|
self,
|
|
info,
|
|
team_profile_id: ID,
|
|
month: str,
|
|
) -> TeamDashboardData:
|
|
"""Fetch all team dashboard data in a single optimized query.
|
|
|
|
Args:
|
|
team_profile_id: The team member's profile ID
|
|
month: Month string in format 'YYYY-MM' (e.g., '2024-01')
|
|
|
|
Returns:
|
|
TeamDashboardData with services and projects for the team member
|
|
"""
|
|
start, end = parse_month_range(month)
|
|
return await sync_to_async(_fetch_team_dashboard_sync)(team_profile_id, start, end)
|
|
|
|
@strawberry.field(
|
|
name="customerDashboard",
|
|
description="Consolidated dashboard data for customer users. "
|
|
"Returns services, projects, and invoices for the customer."
|
|
)
|
|
async def customer_dashboard(
|
|
self,
|
|
info,
|
|
customer_id: ID,
|
|
) -> CustomerDashboardData:
|
|
"""Fetch all customer dashboard data in a single optimized query.
|
|
|
|
Args:
|
|
customer_id: The customer's profile ID
|
|
|
|
Returns:
|
|
CustomerDashboardData with services, projects, and invoices
|
|
"""
|
|
return await sync_to_async(_fetch_customer_dashboard_sync)(customer_id)
|