from __future__ import annotations from io import BytesIO from openpyxl import Workbook from openpyxl.styles import Alignment, Font, PatternFill from openpyxl.utils import get_column_letter from sqlalchemy import select from sqlalchemy.orm import Session from app.modules.services.models import FirmServiceTaskTemplate, ServiceCatalogue, ServiceTaskCategory HEADERS = [ "service_code", "sequence_no", "task_name", "default_role_name", "eligible_role_names", "is_mandatory", "requires_review", "normal_review_role", "task_category_code", "task_category", "response_required", "response_type", "evidence_required", "remarks_required_if_no", "task_tool_code", "is_aqmm_task", "aqmm_mandatory", "aqmm_evidence_required", "aqmm_manager_review_required", "aqmm_partner_review_required", "aqmm_review_partner_required", "aqmm_blocks_final_release", "aqmm_reference", "is_active", "description", ] def _yn(value): return "TRUE" if bool(value) else "FALSE" def build_firm_tasks_export(db: Session, *, tenant_id: int, catalogue_id: int) -> bytes: service = db.execute(select(ServiceCatalogue).where(ServiceCatalogue.id == catalogue_id)).scalar_one() tasks = list(db.execute(select(FirmServiceTaskTemplate).where( FirmServiceTaskTemplate.tenant_id == tenant_id, FirmServiceTaskTemplate.service_catalogue_id == catalogue_id, ).order_by(FirmServiceTaskTemplate.sequence_no, FirmServiceTaskTemplate.id)).scalars().all()) category_code_by_id = {row.id: row.code for row in db.execute(select(ServiceTaskCategory).where( ServiceTaskCategory.tenant_id == tenant_id, ServiceTaskCategory.service_catalogue_id == catalogue_id, )).scalars().all()} wb = Workbook(); ws = wb.active; ws.title = "Firm Task Templates"; ws.append(HEADERS) for task in tasks: ws.append([service.service_code, task.sequence_no, task.task_name, task.default_role_name or "", task.eligible_role_names or "", _yn(task.is_mandatory), _yn(task.requires_review), task.normal_review_role or "", category_code_by_id.get(getattr(task, "task_category_id", None), ""), task.task_category or "", _yn(task.response_required), task.response_type or "NONE", _yn(task.evidence_required), _yn(task.remarks_required_if_no), getattr(task, "task_tool_code", "NONE") or "NONE", _yn(task.is_aqmm_task), _yn(task.aqmm_mandatory), _yn(task.aqmm_evidence_required), _yn(task.aqmm_manager_review_required), _yn(task.aqmm_partner_review_required), _yn(task.aqmm_review_partner_required), _yn(task.aqmm_blocks_final_release), task.aqmm_reference or "", _yn(task.is_active), task.description or ""]) fill=PatternFill("solid", fgColor="1F4E78"); font=Font(color="FFFFFF", bold=True) for c in ws[1]: c.fill=fill; c.font=font; c.alignment=Alignment(horizontal="center", wrap_text=True) ws.freeze_panes="A2"; ws.auto_filter.ref=ws.dimensions for i in range(1, len(HEADERS)+1): ws.column_dimensions[get_column_letter(i)].width = 20 ws.column_dimensions['C'].width=46; ws.column_dimensions['X'].width=70 out=BytesIO(); wb.save(out); return out.getvalue()