from typing import cast import strawberry from strawberry.types import Info from strawberry.scalars import JSON from asgiref.sync import sync_to_async from core.graphql.inputs.project_scope_template import ( ProjectScopeTemplateInput, ProjectScopeTemplateUpdateInput, ProjectAreaTemplateInput, ProjectAreaTemplateUpdateInput, ProjectTaskTemplateInput, ProjectTaskTemplateUpdateInput, ) from core.graphql.pubsub import pubsub from core.graphql.types.project_scope_template import ( ProjectScopeTemplateType, ProjectAreaTemplateType, ProjectTaskTemplateType, ) from core.graphql.utils import create_object, update_object, delete_object from core.models.project_scope_template import ( ProjectScopeTemplate, ProjectAreaTemplate, ProjectTaskTemplate, ) from core.services.scope_builder import build_project_scope_template @strawberry.type class Mutation: @strawberry.mutation(description="Create a new Project Scope Template") async def create_project_scope_template(self, input: ProjectScopeTemplateInput, info: Info) -> ProjectScopeTemplateType: instance = await create_object(input, ProjectScopeTemplate) await pubsub.publish("project_scope_template_created", instance.id) # Note: No event publisher exists for project scope template CRUD operations yet return cast(ProjectScopeTemplateType, instance) @strawberry.mutation(description="Update an existing Project Scope Template") async def update_project_scope_template(self, input: ProjectScopeTemplateUpdateInput, info: Info) -> ProjectScopeTemplateType: instance = await update_object(input, ProjectScopeTemplate) await pubsub.publish("project_scope_template_updated", instance.id) # Note: No event publisher exists for project scope template CRUD operations yet return cast(ProjectScopeTemplateType, instance) @strawberry.mutation(description="Delete a Project Scope Template") async def delete_project_scope_template(self, id: strawberry.ID, info: Info) -> strawberry.ID: instance = await delete_object(id, ProjectScopeTemplate) if not instance: raise ValueError(f"ProjectScopeTemplate with ID {id} does not exist") await pubsub.publish("project_scope_template_deleted", id) # Note: No event publisher exists for project scope template CRUD operations yet return id @strawberry.mutation(description="Create a Project Area Template") async def create_project_area_template(self, input: ProjectAreaTemplateInput, info: Info) -> ProjectAreaTemplateType: instance = await create_object(input, ProjectAreaTemplate) await pubsub.publish("project_area_template_created", instance.id) # Note: No event publisher exists for project area template CRUD operations yet return cast(ProjectAreaTemplateType, instance) @strawberry.mutation(description="Update a Project Area Template") async def update_project_area_template(self, input: ProjectAreaTemplateUpdateInput, info: Info) -> ProjectAreaTemplateType: instance = await update_object(input, ProjectAreaTemplate) await pubsub.publish("project_area_template_updated", instance.id) # Note: No event publisher exists for project area template CRUD operations yet return cast(ProjectAreaTemplateType, instance) @strawberry.mutation(description="Delete a Project Area Template") async def delete_project_area_template(self, id: strawberry.ID, info: Info) -> strawberry.ID: instance = await delete_object(id, ProjectAreaTemplate) if not instance: raise ValueError(f"ProjectAreaTemplate with ID {id} does not exist") await pubsub.publish("project_area_template_deleted", id) # Note: No event publisher exists for project area template CRUD operations yet return id @strawberry.mutation(description="Create a Project Task Template") async def create_project_task_template(self, input: ProjectTaskTemplateInput, info: Info) -> ProjectTaskTemplateType: instance = await create_object(input, ProjectTaskTemplate) await pubsub.publish("project_task_template_created", instance.id) # Note: No event publisher exists for project task template CRUD operations yet return cast(ProjectTaskTemplateType, instance) @strawberry.mutation(description="Update a Project Task Template") async def update_project_task_template(self, input: ProjectTaskTemplateUpdateInput, info: Info) -> ProjectTaskTemplateType: instance = await update_object(input, ProjectTaskTemplate) await pubsub.publish("project_task_template_updated", instance.id) # Note: No event publisher exists for project task template CRUD operations yet return cast(ProjectTaskTemplateType, instance) @strawberry.mutation(description="Delete a Project Task Template") async def delete_project_task_template(self, id: strawberry.ID, info: Info) -> strawberry.ID: instance = await delete_object(id, ProjectTaskTemplate) if not instance: raise ValueError(f"ProjectTaskTemplate with ID {id} does not exist") await pubsub.publish("project_task_template_deleted", id) # Note: No event publisher exists for project task template CRUD operations yet return id @strawberry.mutation(description="Create a ProjectScopeTemplate (and nested Categories/Tasks) from a JSON payload") async def create_project_scope_template_from_json( self, payload: JSON, replace: bool = False, info: Info | None = None, ) -> ProjectScopeTemplateType: """ Accepts a JSON object matching the builder payload shape: { "name": str, "description": str, "is_active": bool, "categories": [ {"name": str, "order": int, "tasks": [ {"description": str, "checklist_description": str, "order": int, "estimated_minutes": int} ]} ] } If replace=True and a template with the same name exists, it will be deleted first. """ def _do_create_sync(): if not isinstance(payload, dict): raise ValueError("payload must be a JSON object") name = payload.get("name") if not name or not isinstance(name, str): raise ValueError("payload.name is required and must be a string") if replace: ProjectScopeTemplate.objects.filter(name=name).delete() elif ProjectScopeTemplate.objects.filter(name=name).exists(): raise ValueError( f"A ProjectScopeTemplate named '{name}' already exists (use replace=true to overwrite)" ) tpl = build_project_scope_template(payload) return tpl instance = await sync_to_async(_do_create_sync)() await pubsub.publish("project_scope_template_created", instance.id) # Note: No event publisher exists for project scope template CRUD operations yet return cast(ProjectScopeTemplateType, instance)