From f4a4bd7ddddb8b858e378ac0cfe251704cf01269 Mon Sep 17 00:00:00 2001 From: Miwory Date: Wed, 15 Apr 2026 16:15:29 +0300 Subject: [PATCH] =?UTF-8?q?=D0=A4=D0=B8=D0=BA=D1=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pyproject.toml | 4 +- src/apps/users/v1/router.py | 18 +++++-- src/apps/users/v1/schema.py | 22 -------- src/apps/xml/__init__.py | 0 src/apps/xml/clinical_document.py | 90 +++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 27 deletions(-) create mode 100644 src/apps/xml/__init__.py create mode 100644 src/apps/xml/clinical_document.py diff --git a/pyproject.toml b/pyproject.toml index cd8daa2..df7c187 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "HospitalAssistantBackend" -version = "1.5.5" +version = "1.5.6" description = "Backend for Hospital Assistant" readme = "README.md" requires-python = ">=3.13,<3.14" @@ -35,6 +35,8 @@ dependencies = [ "lxml==6.0.2; sys_platform != 'win32'", # CLI "typer-slim==0.16.1", + "xsdata[cli]>=26.2", + "xsdata-pydantic>=24.5", ] [dependency-groups] diff --git a/src/apps/users/v1/router.py b/src/apps/users/v1/router.py index 6ccdbcd..ed90282 100644 --- a/src/apps/users/v1/router.py +++ b/src/apps/users/v1/router.py @@ -7,11 +7,14 @@ from typing import Annotated from fastapi import APIRouter, Body, Depends, UploadFile, status from orjson import loads +from xsdata.formats.dataclass.parsers.config import ParserConfig +from xsdata_pydantic.bindings import XmlParser from apps.remd.dependencies import convert_aemd_to_pdf, get_parsable_ids from apps.tdn.auth import token from apps.users.auth import login from apps.users.models import User +from apps.xml.clinical_document import ClinicalDocument from clients import clients as c from clients.tmk import schema as ts from clients.vitacore import schema as vs @@ -28,6 +31,12 @@ router = APIRouter( ], ) +config = ParserConfig( + fail_on_unknown_attributes=False, fail_on_unknown_properties=False +) + +parser = XmlParser(config=config) + @router.get( '/getProfile', @@ -118,9 +127,10 @@ async def get_vaccs_report_download( filename = f'vaccs_report_{resultId or user.vita_id}.doc' xml_data = base64.b64decode(file.content) - xml_data = xml_data.strip() - xml_model = s.ClinicalDocument.from_xml(xml_data) - inner_base64 = xml_model.component.non_xml_body.text.content.strip() + xml_data = xml_data.decode('utf-8').strip() + xml_model = parser.from_string(xml_data, ClinicalDocument) + pdf_string = xml_model.component.non_xmlbody.text.value.strip() + pdf_bytes = base64.b64decode(pdf_string) temp_link_token = token_urlsafe(32) await cache.set( @@ -129,7 +139,7 @@ async def get_vaccs_report_download( { 'filename': filename, 'content_type': 'application/pdf', - 'data': inner_base64, + 'data': pdf_bytes, } ), ex=600, diff --git a/src/apps/users/v1/schema.py b/src/apps/users/v1/schema.py index adc1338..8737b4e 100644 --- a/src/apps/users/v1/schema.py +++ b/src/apps/users/v1/schema.py @@ -1,7 +1,6 @@ from typing import TypedDict from pydantic import BaseModel -from pydantic_xml import BaseXmlModel, attr, element class AEMDFile(TypedDict): @@ -36,26 +35,5 @@ class Complaints(BaseModel): complaints: str -NS = 'urn:hl7-org:v3' - - -class TextElement(BaseXmlModel, tag='text', ns=NS): - representation: str | None = attr(default=None) - media_type: str | None = attr(name='mediaType', default=None) - content: str - - -class NonXMLBody(BaseXmlModel, tag='nonXMLBody', ns=NS): - text: TextElement = element(tag='text') - - -class Component(BaseXmlModel, tag='component', ns=NS): - non_xml_body: NonXMLBody = element(tag='nonXMLBody') - - -class ClinicalDocument(BaseXmlModel, tag='ClinicalDocument', ns=NS): - component: Component = element(tag='component') - - class DownloadFile(BaseModel): link: str diff --git a/src/apps/xml/__init__.py b/src/apps/xml/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/xml/clinical_document.py b/src/apps/xml/clinical_document.py new file mode 100644 index 0000000..ffe0a06 --- /dev/null +++ b/src/apps/xml/clinical_document.py @@ -0,0 +1,90 @@ +from __future__ import annotations + +from pydantic import BaseModel, ConfigDict +from xsdata_pydantic.fields import field + +__NAMESPACE__ = 'urn:hl7-org:v3' + + +class Text(BaseModel): + class Meta: + name = 'text' + namespace = 'urn:hl7-org:v3' + + model_config = ConfigDict(defer_build=True) + representation: str = field( # type: ignore + metadata={ + 'type': 'Attribute', + } + ) + media_type: str = field( # type: ignore + metadata={ + 'name': 'mediaType', + 'type': 'Attribute', + } + ) + value: str = field(default='') # type: ignore + + +class NonXmlbody(BaseModel): + class Meta: + name = 'nonXMLBody' + namespace = 'urn:hl7-org:v3' + + model_config = ConfigDict(defer_build=True) + class_code: str = field( # type: ignore + metadata={ + 'name': 'classCode', + 'type': 'Attribute', + } + ) + mood_code: str = field( # type: ignore + metadata={ + 'name': 'moodCode', + 'type': 'Attribute', + } + ) + text: Text = field( # type: ignore + metadata={ + 'type': 'Element', + } + ) + + +class Component(BaseModel): + class Meta: + name = 'component' + namespace = 'urn:hl7-org:v3' + + model_config = ConfigDict(defer_build=True) + non_xmlbody: NonXmlbody = field( # type: ignore + metadata={ + 'name': 'nonXMLBody', + 'type': 'Element', + } + ) + + +class ClinicalDocument(BaseModel): + class Meta: + namespace = 'urn:hl7-org:v3' + + model_config = ConfigDict(defer_build=True) + schema_location: str = field( # type: ignore + metadata={ + 'name': 'schemaLocation', + 'type': 'Attribute', + 'namespace': 'http://www.w3.org/2001/XMLSchema-instance', + } + ) + mood_code: str = field( # type: ignore + metadata={ + 'name': 'moodCode', + 'type': 'Attribute', + } + ) + component: Component = field( # type: ignore + metadata={ + 'type': 'Element', + } + )