This commit is contained in:
@ -1,6 +1,6 @@
|
||||
from logging import getLogger
|
||||
|
||||
from fastapi import APIRouter
|
||||
from fastapi import APIRouter, HTTPException, Request
|
||||
|
||||
logger = getLogger(__name__)
|
||||
router = APIRouter(
|
||||
@ -12,5 +12,18 @@ router = APIRouter(
|
||||
|
||||
|
||||
@router.post('/callback')
|
||||
async def callback():
|
||||
return
|
||||
async def callback(request: Request):
|
||||
if not request.headers.get('content-type', '').startswith(
|
||||
'application/xml'
|
||||
):
|
||||
logger.warning('Content-Type must be application/xml')
|
||||
raise HTTPException(
|
||||
status_code=400, detail='Content-Type must be application/xml'
|
||||
)
|
||||
|
||||
body_bytes = await request.body()
|
||||
if not body_bytes:
|
||||
logger.warning('Empty body')
|
||||
raise HTTPException(status_code=400, detail='Empty body')
|
||||
|
||||
print(body_bytes)
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
from datetime import UTC, datetime
|
||||
from json import dumps
|
||||
from logging import getLogger
|
||||
from secrets import token_urlsafe
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, UploadFile, status
|
||||
@ -180,20 +181,42 @@ async def queue(_: Annotated[User, Depends(login)]):
|
||||
|
||||
|
||||
@router.get('/aemd')
|
||||
async def aemd(user: Annotated[User, Depends(login)]):
|
||||
async def get_aemd(user: Annotated[User, Depends(login)]):
|
||||
profile = await c.vitacore_api.getProfile(user.vita_id)
|
||||
snils = profile.SNILS.replace('-', '').replace(' ', '')
|
||||
docs = await c.aemd_api.searchRegistryItem(patient_snils=snils)
|
||||
doc = docs['items'][0]
|
||||
items: list[s.AEMDFile] = docs['items']
|
||||
return_items: list[s.AEMDReturnFile] = []
|
||||
|
||||
from datetime import datetime
|
||||
for item in items:
|
||||
is_cached = await cache.get(f'aemd:{item["localUid"]}')
|
||||
|
||||
msg = f'AEMD Current datetime: {datetime.now(UTC).isoformat()}'
|
||||
logger.info(msg)
|
||||
return_items.append(
|
||||
s.AEMDReturnFile(
|
||||
emdrId=item['emdrId'],
|
||||
registrationDate=item['registrationDate'],
|
||||
DocKind=item['DocKind'],
|
||||
IsSemd=item['IsSemd'],
|
||||
isCached=bool(is_cached),
|
||||
)
|
||||
)
|
||||
|
||||
return await c.aemd_api.demandContent(
|
||||
messageId='test123', emdrId=doc['emdrId']
|
||||
return return_items
|
||||
|
||||
|
||||
@router.post('/aemd', status_code=status.HTTP_202_ACCEPTED)
|
||||
async def post_aemd(user: Annotated[User, Depends(login)], emdrId: str):
|
||||
messageId = token_urlsafe(32)
|
||||
data = s.AEMDDemandContent(
|
||||
messageId=messageId, emdrId=emdrId, vitaId=user.vita_id
|
||||
)
|
||||
await cache.set(f'aemd_messages:{messageId}', dumps(data))
|
||||
await c.aemd_api.demandContent(messageId=messageId, emdrId=emdrId)
|
||||
|
||||
|
||||
@router.get('/aemd/{emdrId}')
|
||||
async def get_aemd_file(user: Annotated[User, Depends(login)], emdrId: str):
|
||||
return
|
||||
|
||||
|
||||
@router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
||||
|
||||
@ -3,6 +3,30 @@ from typing import TypedDict
|
||||
from pydantic import BaseModel
|
||||
|
||||
|
||||
class AEMDFile(TypedDict):
|
||||
emdrId: str
|
||||
localUid: str
|
||||
registrationDate: str
|
||||
registrationDateTime: str
|
||||
storeTillDate: str
|
||||
DocKind: str
|
||||
IsSemd: bool
|
||||
|
||||
|
||||
class AEMDReturnFile(TypedDict):
|
||||
emdrId: str
|
||||
registrationDate: str
|
||||
DocKind: str
|
||||
IsSemd: bool
|
||||
isCached: bool
|
||||
|
||||
|
||||
class AEMDDemandContent(TypedDict):
|
||||
messageId: str
|
||||
emdrId: str
|
||||
vitaId: str
|
||||
|
||||
|
||||
class Notifications(TypedDict):
|
||||
notifications: list[dict[str, str | bool]]
|
||||
|
||||
|
||||
@ -1,146 +0,0 @@
|
||||
from datetime import datetime
|
||||
from typing import Literal
|
||||
|
||||
from pydantic_xml import BaseXmlModel, attr, element, wrapped
|
||||
|
||||
# SOAP Envelope namespaces
|
||||
NS_SOAP = 'http://www.w3.org/2003/05/soap-envelope'
|
||||
NS_ADDR = 'http://www.w3.org/2005/08/addressing'
|
||||
NS_WSSE = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd'
|
||||
NS_WSU = 'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd'
|
||||
NS_DS = 'http://www.w3.org/2000/09/xmldsig#'
|
||||
NS_APP = 'http://egisz.rosminzdrav.ru/iehr/emdr/service/'
|
||||
|
||||
NSMAP = {
|
||||
's': NS_SOAP,
|
||||
'a': NS_ADDR,
|
||||
'wsse': NS_WSSE,
|
||||
'wsu': NS_WSU,
|
||||
'ds': NS_DS,
|
||||
'app': NS_APP,
|
||||
}
|
||||
|
||||
|
||||
class Reference(BaseXmlModel, tag='Reference', ns='ds', nsmap=NSMAP):
|
||||
uri: str = attr(name='URI')
|
||||
digest_method: Literal['http://www.w3.org/2001/04/xmlenc#sha256'] = (
|
||||
element(tag='DigestMethod', ns='ds', attr_name='Algorithm')
|
||||
)
|
||||
digest_value: str = element(tag='DigestValue', ns='ds')
|
||||
|
||||
|
||||
class SignedInfo(BaseXmlModel, tag='SignedInfo', ns=NS_DS, nsmap=NSMAP):
|
||||
canon_method: Literal['http://www.w3.org/2001/10/xml-exc-c14n#'] = element(
|
||||
tag='CanonicalizationMethod', ns=NS_DS, attr_name='Algorithm'
|
||||
)
|
||||
sig_method: Literal[
|
||||
'urn:ietf:params:xml:ns:cpxmlsec:algorithms:gostr34102012-gostr34112012-256'
|
||||
] = element(tag='SignatureMethod', ns=NS_DS, attr_name='Algorithm')
|
||||
references: list[Reference] = element(tag='Reference', ns=NS_DS)
|
||||
|
||||
|
||||
class SecurityTokenReference(
|
||||
BaseXmlModel,
|
||||
tag='SecurityTokenReference',
|
||||
ns=NS_WSSE,
|
||||
nsmap={'wsse': NS_WSSE},
|
||||
):
|
||||
ref_uri: str = wrapped('Reference', attr(name='URI'))
|
||||
ref_type: Literal[
|
||||
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
|
||||
] = wrapped('Reference', attr(name='ValueType'))
|
||||
|
||||
|
||||
class KeyInfo(BaseXmlModel, tag='KeyInfo', ns=NS_DS, nsmap=NSMAP):
|
||||
token_ref: SecurityTokenReference = element(
|
||||
tag='SecurityTokenReference', ns=NS_WSSE
|
||||
)
|
||||
|
||||
|
||||
class Signature(BaseXmlModel, tag='Signature', ns=NS_DS, nsmap=NSMAP):
|
||||
signed_info: SignedInfo = element()
|
||||
signature_value: str = element(tag='SignatureValue')
|
||||
key_info: KeyInfo = element()
|
||||
|
||||
|
||||
class BinarySecurityToken(
|
||||
BaseXmlModel,
|
||||
tag='BinarySecurityToken',
|
||||
ns=NS_WSSE,
|
||||
nsmap=NSMAP,
|
||||
):
|
||||
id: str = attr(name='Id', ns=NS_WSU)
|
||||
value_type: Literal[
|
||||
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-x509-token-profile-1.0#X509v3'
|
||||
] = attr(name='ValueType')
|
||||
encoding_type: Literal[
|
||||
'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-soap-message-security-1.0#Base64Binary'
|
||||
] = attr(name='EncodingType')
|
||||
value: str
|
||||
|
||||
|
||||
class Security(BaseXmlModel, tag='Security', ns=NS_WSSE, nsmap=NSMAP):
|
||||
token: BinarySecurityToken = element()
|
||||
signature: Signature = element()
|
||||
|
||||
|
||||
class Action(BaseXmlModel, tag='Action', ns=NS_ADDR, nsmap=NSMAP):
|
||||
id: str = attr(name='Id', ns=NS_WSU)
|
||||
|
||||
|
||||
class MessageID(BaseXmlModel, tag='MessageID', ns=NS_ADDR, nsmap=NSMAP):
|
||||
id: str = attr(name='Id', ns=NS_WSU)
|
||||
value: str
|
||||
|
||||
|
||||
class Header(BaseXmlModel, tag='Header', ns=NS_SOAP, nsmap=NSMAP):
|
||||
action: Action | None = element(tag='Action', ns=NS_ADDR)
|
||||
message_id: MessageID | None = element(tag='MessageID', ns=NS_ADDR)
|
||||
security: Security = element(tag='Security', ns=NS_WSSE)
|
||||
|
||||
|
||||
class Page(BaseXmlModel, tag='page', ns=NS_APP):
|
||||
items_per_page: int = element(tag='itemsPerPage')
|
||||
has_next: bool = element(tag='hasNext')
|
||||
|
||||
|
||||
class Item(BaseXmlModel, tag='item', ns=NS_APP):
|
||||
emdr_id: str = element(tag='emdrId')
|
||||
local_uid: str = element(tag='localUid')
|
||||
registration_date: datetime = element(tag='registrationDate')
|
||||
registration_date_time: datetime = element(tag='registrationDateTime')
|
||||
store_till_date: str = element(tag='storeTillDate')
|
||||
doc_kind: str = element(tag='DocKind')
|
||||
is_semd: bool = element(tag='IsSemd')
|
||||
|
||||
|
||||
class Matches(BaseXmlModel, tag='matches', ns=NS_APP):
|
||||
items: list[Item] = element(tag='item')
|
||||
page: Page = element()
|
||||
|
||||
|
||||
class SearchRegistryItemResponse(
|
||||
BaseXmlModel,
|
||||
tag='searchRegistryItemResponse',
|
||||
ns=NS_APP,
|
||||
nsmap={'app': NS_APP},
|
||||
):
|
||||
status: Literal['success'] = element()
|
||||
matches: Matches = element()
|
||||
|
||||
|
||||
class Body(BaseXmlModel, tag='Body', ns=NS_SOAP, nsmap=NSMAP):
|
||||
id: str = attr(name='Id', ns=NS_WSU)
|
||||
response: SearchRegistryItemResponse = element(
|
||||
tag='searchRegistryItemResponse', ns=NS_APP
|
||||
)
|
||||
|
||||
|
||||
class Envelope(
|
||||
BaseXmlModel,
|
||||
tag='Envelope',
|
||||
ns=NS_SOAP,
|
||||
nsmap=NSMAP,
|
||||
):
|
||||
header: Header = element(tag='Header', ns=NS_SOAP)
|
||||
body: Body = element(tag='Body', ns=NS_SOAP)
|
||||
@ -135,6 +135,11 @@ class VITACORE_API(AsyncClient):
|
||||
raise e.UnknownException
|
||||
|
||||
async def getWorkers(self, departmentId: str):
|
||||
data = await self.get_cache(f'vitacore_getWorkers:{departmentId}')
|
||||
|
||||
if data:
|
||||
return s.WorkersModel.model_validate(data)
|
||||
|
||||
token = await self.get_token()
|
||||
req = await self.get(
|
||||
'/getWorkers',
|
||||
@ -144,7 +149,13 @@ class VITACORE_API(AsyncClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.HTTP_200_OK:
|
||||
return s.WorkersModel.model_validate(req.json())
|
||||
data = s.WorkersModel.model_validate(req.json())
|
||||
await self.set_cache(
|
||||
f'vitacore_getWorkers:{departmentId}',
|
||||
data.model_dump_json(),
|
||||
14400,
|
||||
)
|
||||
return data
|
||||
|
||||
case st.HTTP_206_PARTIAL_CONTENT:
|
||||
return s.WorkersModel(Workers=[])
|
||||
@ -154,6 +165,11 @@ class VITACORE_API(AsyncClient):
|
||||
raise e.UnknownException
|
||||
|
||||
async def getSpecsV021(self):
|
||||
data = await self.get_cache('vitacore_getSpecsV021')
|
||||
|
||||
if data:
|
||||
return s.SpecsV021Model.model_validate(data)
|
||||
|
||||
token = await self.get_token()
|
||||
req = await self.get(
|
||||
'/getSpecsV021', headers={'Authorization': f'Bearer {token}'}
|
||||
@ -161,7 +177,13 @@ class VITACORE_API(AsyncClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.HTTP_200_OK:
|
||||
return s.SpecsV021Model.model_validate(req.json())
|
||||
data = s.SpecsV021Model.model_validate(req.json())
|
||||
await self.set_cache(
|
||||
'vitacore_getSpecsV021',
|
||||
data.model_dump_json(),
|
||||
14400,
|
||||
)
|
||||
return data
|
||||
case _:
|
||||
self.logger.error(req.json())
|
||||
raise e.UnknownException
|
||||
@ -218,6 +240,11 @@ class VITACORE_API(AsyncClient):
|
||||
raise e.UnknownException
|
||||
|
||||
async def getMedExamDict(self):
|
||||
data = await self.get_cache('vitacore_getMedExamDict')
|
||||
|
||||
if data:
|
||||
return s.MedExamDictModel.model_validate(data)
|
||||
|
||||
token = await self.get_token()
|
||||
req = await self.get(
|
||||
'/getMedExamDict', headers={'Authorization': f'Bearer {token}'}
|
||||
@ -225,7 +252,13 @@ class VITACORE_API(AsyncClient):
|
||||
|
||||
match req.status_code:
|
||||
case st.HTTP_200_OK:
|
||||
return s.MedExamDictModel.model_validate(req.json())
|
||||
data = s.MedExamDictModel.model_validate(req.json())
|
||||
await self.set_cache(
|
||||
'vitacore_getMedExamDict',
|
||||
data.model_dump_json(),
|
||||
14400,
|
||||
)
|
||||
return data
|
||||
case _:
|
||||
self.logger.error(req.json())
|
||||
raise e.UnknownException
|
||||
|
||||
Reference in New Issue
Block a user