Spaces:
Sleeping
Sleeping
| """ViewerProjection builder — converts CanonicalDocument into viewer data. | |
| The ViewerProjection is a lightweight structure that the front-end | |
| consumes directly. It never parses XML. It derives from the canonical | |
| document after validation and enrichment. | |
| """ | |
| from __future__ import annotations | |
| from typing import TYPE_CHECKING | |
| from src.app.domain.models.readiness import ExportEligibility | |
| from src.app.domain.models.viewer_projection import InspectionData, OverlayItem, ViewerProjection | |
| from src.app.viewer.overlays import ( | |
| line_to_inspection, | |
| line_to_overlay, | |
| non_text_to_overlay, | |
| region_to_inspection, | |
| region_to_overlay, | |
| word_to_inspection, | |
| word_to_overlay, | |
| ) | |
| if TYPE_CHECKING: | |
| from src.app.domain.models import CanonicalDocument | |
| def build_projection( | |
| doc: CanonicalDocument, | |
| page_index: int = 0, | |
| *, | |
| export_status: ExportEligibility | None = None, | |
| ) -> ViewerProjection: | |
| """Build a ViewerProjection for a single page. | |
| Args: | |
| doc: The canonical document. | |
| page_index: Which page to project (default: first). | |
| export_status: Export eligibility to include in the projection. | |
| Returns: | |
| A ViewerProjection ready for the front-end. | |
| """ | |
| if page_index >= len(doc.pages): | |
| raise ValueError( | |
| f"Page index {page_index} out of range" | |
| f" (document has {len(doc.pages)} pages)" | |
| ) | |
| page = doc.pages[page_index] | |
| block_overlays: list[OverlayItem] = [] | |
| line_overlays: list[OverlayItem] = [] | |
| word_overlays: list[OverlayItem] = [] | |
| non_text_overlays: list[OverlayItem] = [] | |
| inspection_index: dict[str, InspectionData] = {} | |
| validation_flags: list[str] = list(page.warnings) | |
| for region in page.text_regions: | |
| block_overlays.append(region_to_overlay(region)) | |
| inspection_index[region.id] = region_to_inspection(region) | |
| for line in region.lines: | |
| line_overlays.append(line_to_overlay(line)) | |
| inspection_index[line.id] = line_to_inspection(line) | |
| for word in line.words: | |
| word_overlays.append(word_to_overlay(word)) | |
| inspection_index[word.id] = word_to_inspection(word) | |
| for ntr in page.non_text_regions: | |
| non_text_overlays.append(non_text_to_overlay(ntr)) | |
| image_ref = "" | |
| if doc.source.filename: | |
| image_ref = doc.source.filename | |
| if page.metadata and "image_ref" in page.metadata: | |
| image_ref = page.metadata["image_ref"] | |
| return ViewerProjection( | |
| image_ref=image_ref, | |
| image_width=int(page.width), | |
| image_height=int(page.height), | |
| block_overlays=block_overlays, | |
| line_overlays=line_overlays, | |
| word_overlays=word_overlays, | |
| non_text_overlays=non_text_overlays, | |
| inspection_index=inspection_index, | |
| validation_flags=validation_flags, | |
| export_status=export_status or ExportEligibility(), | |
| ) | |
| def build_all_projections( | |
| doc: CanonicalDocument, | |
| *, | |
| export_status: ExportEligibility | None = None, | |
| ) -> list[ViewerProjection]: | |
| """Build ViewerProjections for all pages.""" | |
| return [ | |
| build_projection(doc, i, export_status=export_status) | |
| for i in range(len(doc.pages)) | |
| ] | |