Validation_Suite/validation-suite/backend/mercury/app/modules/orion/service.py

128 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

from __future__ import annotations
from pathlib import Path
import logging
from sqlalchemy.orm import Session
from app.modules.orion.assets import ORION_ASSET_DIR
from app.modules.orion.components import (
AttachmentComponent,
ChecklistComponent,
CoverComponent,
CustomerComponent,
DeviceComponent,
DryingComponent,
EnvironmentComponent,
EquipmentComponent,
LoadingComponent,
MeasurementComponent,
ProgramComponent,
RecommendationComponent,
ReportComponent,
StaticTextComponent,
SummaryComponent,
TocComponent,
)
from app.modules.orion.context import OrionContextBuilder, ReportContext
from app.modules.orion.templates.report import render_document
logger = logging.getLogger(__name__)
class OrionReportService:
def __init__(self, session: Session, generated_dir: Path | None = None) -> None:
self.session = session
self.generated_dir = generated_dir or Path("/app/reports")
def render_html(self, validation_id: str) -> str:
context = OrionContextBuilder(self.session, self.generated_dir).build(validation_id)
components = self._components()
chapters = [component.render(context) for component in components]
return render_document(context, chapters)
def render_pdf(self, validation_id: str) -> Path:
from weasyprint import HTML
context = OrionContextBuilder(self.session, self.generated_dir).build(validation_id)
output_path = context.generated_dir / f"{context.validation.report_number}.pdf"
html = self.render_html(validation_id)
HTML(string=html, base_url=str(ORION_ASSET_DIR)).write_pdf(output_path)
self._append_pdf_attachments(context, output_path)
return output_path
def _components(self) -> list[ReportComponent]:
chapters: list[ReportComponent] = [
StaticTextComponent("bq", "1 Funktionsqualifikation (BQ)", "nicht erfasst"),
StaticTextComponent("bq-goal", "1.1 Anlass und Ziel der Prüfung", block_key="bq_goal"),
StaticTextComponent("legal", "1.2 Gesetzliche Grundlagen", block_key="legal"),
DeviceComponent(),
StaticTextComponent("performance", "1.4 Leistungsüberprüfung", block_key="performance"),
ChecklistComponent(),
StaticTextComponent("documentation", "1.6 Dokumentation / Kontrolle"),
StaticTextComponent("work-instructions", "1.7 Arbeitsanweisungen"),
EnvironmentComponent(),
StaticTextComponent("batch-control", "1.9 Chargenkontrolle"),
ProgramComponent(),
LoadingComponent(),
StaticTextComponent("reference-load", "1.12 Referenzbeladung Sterilisator"),
StaticTextComponent("equipment", "2 Eingesetzte Prüfmittel"),
EquipmentComponent(),
StaticTextComponent("equipment-thermo", "2.2 Prüfmittel zur thermoelektrischen Untersuchung"),
StaticTextComponent("configuration", "2.3 Prüfkonfiguration"),
StaticTextComponent("lq", "3 Leistungsqualifikation (LQ)"),
StaticTextComponent("thermo-tests", "3.1 Leistungsbeurteilung Thermoelektrische Prüfungen"),
MeasurementComponent(),
StaticTextComponent("run-1", "3.2 Standardbeladung Testlauf 1"),
StaticTextComponent("test-1", "3.2.1 Test 1"),
StaticTextComponent("run-2", "3.3 Standardbeladung Testlauf 2"),
StaticTextComponent("test-2", "3.3.1 Test 2"),
StaticTextComponent("run-3", "3.4 Standardbeladung Testlauf 3"),
StaticTextComponent("test-3", "3.4.1 Test 3"),
StaticTextComponent("results", "4 Ergebnisse der Validierung"),
StaticTextComponent("results-vacuum", "4.1 Vakuumtest"),
StaticTextComponent("results-runs", "4.1.2 Testläufe 1 bis 3"),
DryingComponent(),
RecommendationComponent(),
StaticTextComponent("appendix", "5 Anhang"),
AttachmentComponent(),
StaticTextComponent("winlog", "5.1 Winlog-Auswertungen"),
StaticTextComponent("release-docs", "5.2 Freigabedokumentation / Routineprüfungen"),
StaticTextComponent("indicators", "5.3 Nachweis der umgeschlagenen Indikatoren"),
StaticTextComponent("cycles", "6 Programmabläufe / Zyklen"),
StaticTextComponent("practice-certificates", "6.1 Zertifikate Praxis"),
StaticTextComponent("risk", "7 Risikoeinstufung nach RKI"),
StaticTextComponent("closing-talk", "7.1 Abschlussgespräch"),
StaticTextComponent("certificates", "8. Zertifikate"),
StaticTextComponent("calibration-certificates", "9. Werkskalibrierzertifikate Sensoren"),
]
return [CoverComponent(), SummaryComponent(), TocComponent(chapters), CustomerComponent(), *chapters]
def _append_pdf_attachments(self, context: ReportContext, output_path: Path) -> None:
pdf_paths = []
for item in context.validation.attachments or []:
filename = str(item.get("filename") or "")
content_type = str(item.get("content_type") or "")
if not filename.lower().endswith(".pdf") and content_type != "application/pdf":
continue
storage_path = item.get("storage_path")
if storage_path and Path(str(storage_path)).exists():
pdf_paths.append(Path(str(storage_path)))
if not pdf_paths:
return
try:
from pypdf import PdfReader, PdfWriter
writer = PdfWriter()
for page in PdfReader(str(output_path)).pages:
writer.add_page(page)
for pdf_path in pdf_paths:
try:
for page in PdfReader(str(pdf_path)).pages:
writer.add_page(page)
except Exception:
logger.exception("Could not append attachment PDF %s", pdf_path)
with output_path.open("wb") as handle:
writer.write(handle)
except Exception:
logger.exception("Could not merge Orion PDF attachments for validation %s", context.validation.id)