""" Mock extraction engine for testing without external dependencies. """ import os import datetime from typing import Dict, Any, Optional from lxml import etree from fastapi_app.lib.extraction import BaseExtractor from fastapi_app.lib.utils.tei_utils import ( create_tei_document, create_tei_header, create_revision_desc_with_status, serialize_tei_with_formatted_header, get_file_id_from_options, create_encoding_desc_with_extractor, ) from fastapi_app.lib.utils.doi_utils import encode_for_xml_id class MockExtractor(BaseExtractor): """Mock extractor for testing without external dependencies.""" SUPPORTED_VARIANTS = [ "mock-default", "mock-variant-a", "mock-variant-b", ] @classmethod def get_info(cls) -> Dict[str, Any]: """Return information about the mock extractor.""" return { "id": "mock-extractor", "name": "Mock Extractor", "description": "Mock extractor for testing without external dependencies (available in testing mode only)", "input": ["pdf", "xml"], "output": ["tei-document"], "variants": list(cls.SUPPORTED_VARIANTS), "options": { "doi": { "type": "string", "label": "DOI", "description": "DOI of the document for metadata enrichment", "required": False }, "variant_id": { "type": "string", "label": "Variant identifier", "description": "Variant identifier for the mock extraction", "required": False, "options": list(cls.SUPPORTED_VARIANTS) } }, "navigation_xpath": { "mock-default": [ { "value": "//tei:biblStruct", "label": "" } ] } } @classmethod def is_available(cls) -> bool: """Mock extractor available only in testing mode.""" app_mode = os.environ.get("FASTAPI_APPLICATION_MODE", "development") return app_mode == "testing" async def extract(self, pdf_path: Optional[str] = None, xml_content: Optional[str] = None, options: Optional[Dict[str, Any]] = None) -> str: """ Mock extraction that returns a simple TEI document. Args: pdf_path: Path to the PDF file (for PDF input) xml_content: XML content string (for XML input) options: Extraction options Returns: Complete TEI document as XML string """ if not pdf_path and not xml_content: raise ValueError("Either PDF path or XML content is required for mock extraction") if options is None: options = {} # Create TEI document tei_doc = create_tei_document() # Create basic TEI header doi = options.get("doi", "") tei_header = create_tei_header(doi, {}) assert tei_header is not None timestamp = datetime.datetime.now(datetime.timezone.utc).isoformat().replace("+00:00", "Z") file_id = get_file_id_from_options(options, pdf_path) if not file_id: file_id = f"mock-extracted-{datetime.datetime.now().strftime('%Y%m%d-%H%M%S')}" fileDesc = tei_header.find("fileDesc") assert fileDesc is not None # Set xml:id on fileDesc for document identity (NCName-safe encoding) fileDesc.set("{http://www.w3.org/XML/1998/namespace}id", encode_for_xml_id(file_id)) # Create encodingDesc with applications existing_encodingDesc = tei_header.find("encodingDesc") if existing_encodingDesc is not None: tei_header.remove(existing_encodingDesc) # Get variant_id from options info = self.get_info() default_variant_id = info["options"]["variant_id"]["options"][0] # "mock-default" variant_id = options.get("variant_id", default_variant_id) encodingDesc = create_encoding_desc_with_extractor( timestamp=timestamp, extractor_name="Mock Extractor", extractor_ident="mock-extractor", extractor_version="1.0", variant_id=variant_id, ) tei_header.append(encodingDesc) # Replace revisionDesc with mock-specific version existing_revisionDesc = tei_header.find("revisionDesc") if existing_revisionDesc is not None: tei_header.remove(existing_revisionDesc) revision_desc = create_revision_desc_with_status(timestamp, "extraction", "Generated with mock extractor for testing") tei_header.append(revision_desc) tei_doc.append(tei_header) # Create mock content with sample references standOff = etree.SubElement(tei_doc, "standOff") listBibl = etree.SubElement(standOff, "listBibl") # Add a few mock bibliography entries for i in range(3): biblStruct = etree.SubElement(listBibl, "biblStruct", status="verified") analytic = etree.SubElement(biblStruct, "analytic") title = etree.SubElement(analytic, "title", level="a", type="main") title.text = f"Mock Reference Title {i+1}" author = etree.SubElement(analytic, "author") persName = etree.SubElement(author, "persName") forename = etree.SubElement(persName, "forename", type="first") forename.text = f"John{i+1}" surname = etree.SubElement(persName, "surname") surname.text = f"Doe{i+1}" monogr = etree.SubElement(biblStruct, "monogr") title_monogr = etree.SubElement(monogr, "title", level="j") title_monogr.text = f"Mock Journal {i+1}" imprint = etree.SubElement(monogr, "imprint") date_imprint = etree.SubElement(imprint, "date", type="published", when=f"202{i}") date_imprint.text = f"202{i}" # Serialize to XML with formatted header (no schema validation for mock extractor) return serialize_tei_with_formatted_header(tei_doc, [])