This commit is contained in:
2025-10-16 16:43:18 +03:00
parent cb3b138241
commit a4239a0c52
21 changed files with 524 additions and 140 deletions

View File

@ -1,3 +1,4 @@
from .aemd.api import AEMD_API
from .esia.api import ESIA_API
from .tdn.api import TDN_API
from .vitacore.api import VITACORE_API
@ -7,6 +8,7 @@ class ClientsObject:
_esia_api = None
_vitacore_api = None
_tdn_api = None
_aemd_api = None
@property
def esia_api(self):
@ -29,5 +31,12 @@ class ClientsObject:
return self._tdn_api
@property
def aemd_api(self):
if not self._aemd_api:
self._aemd_api = AEMD_API()
return self._aemd_api
clients = ClientsObject()

View File

46
src/clients/aemd/api.py Normal file
View File

@ -0,0 +1,46 @@
from logging import getLogger
from httpx import AsyncClient
from core.config import settings
class AEMD_API(AsyncClient):
def __init__(self):
self.logger = getLogger(__name__)
super().__init__(
base_url=settings.AEMD_BASE_URL,
headers={'Content-Type': 'application/soap+xml'},
)
def get_envelope(self, endpoint: str, body: str):
return (
'<?xml version="1.0" encoding="utf-8"?>'
'<s:Envelope xmlns:s= "http://www.w3.org/2003/05/soap-envelope" '
'xmlns:a= "http://www.w3.org/2005/08/addressing">'
'<s:Header>'
f'<a:Action s:mustUnderstand="1">{endpoint}</a:Action>'
'<h:transportHeader xmlns:h="http://egisz.rosminzdrav.ru" '
'xmlns="http://egisz.rosminzdrav.ru" '
'xmlns:xsd="http://www.w3.org/2001/XMLSchema" '
'xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance">'
'<authInfo>'
f'<clientEntityId>{settings.AEMD_TOKEN}</clientEntityId>'
'</authInfo>'
'</h:transportHeader>'
'<a:To s:mustUnderstand= "1">http://gist-sdw.ezdrav.ru:8708/EMDAService</a:To>'
'</s:Header>'
'<s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">'
f'{body}'
'</s:Body>'
'</s:Envelope>'
)
async def searchRegistryItem(self, patient_snils: str):
envelope = self.get_envelope(
'searchRegistryItem',
f'<searchRegistryItemRequest xmlns="http://egisz.rosminzdrav.ru/iehr/emdr/service/"><patientSnils>{patient_snils}</patientSnils></searchRegistryItemRequest>',
)
req = await self.post('/', content=envelope)
return req.text

View File

@ -33,7 +33,7 @@ class IDTokenModel(BaseModel):
exp: int
iat: int
iss: str
acr: IDTokenACRModel
# acr: IDTokenACRModel
urn_esia_amd: str = Field(alias='urn:esia:amd')
urn_esia_sid: str = Field(alias='urn:esia:sid')
urn_esia_sbj: IDTokenSBJModel = Field(alias='urn:esia:sbj')
@ -44,10 +44,10 @@ class UserInfoModel(BaseModel):
firstName: str
lastName: str
middleName: str
birthDate: str
gender: str
# birthDate: str
# gender: str
trusted: bool
citizenship: str
# citizenship: str
snils: str
inn: int
updatedOn: int

View File

@ -1,10 +1,12 @@
from datetime import UTC, datetime
from logging import getLogger
from fastapi import status as st
from httpx import AsyncClient
from httpx import AsyncClient, BasicAuth
from core.config import settings
from shared import exceptions as e
from shared.redis import client as cache
from . import schema as s
@ -14,11 +16,56 @@ class VITACORE_API(AsyncClient):
self.logger = getLogger(__name__)
super().__init__(base_url=settings.VITACORE_BASE_URL)
async def get_token(self):
token = cache.get('vitacore_token')
if token is None:
token = await self.login()
cache.set('vitacore_token', token, 10800)
else:
token = token.decode()
return token
async def login(self):
req = await self.post(
'/auth',
auth=BasicAuth(
settings.VITACORE_USERNAME, settings.VITACORE_PASSWORD
),
)
match req.status_code:
case st.HTTP_200_OK:
return req.text
case _:
self.logger.error(req.text)
raise e.UnknownException
async def findBySnils(self, snils: str):
return
token = await self.get_token()
req = await self.get(
'/findBySnils',
params={'snils': snils},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.PatientsModel.model_validate(req.json())
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getProfile(self, patId: str):
req = await self.get('/getProfile', params={'patId': patId})
token = await self.get_token()
req = await self.get(
'/getProfile',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
@ -28,18 +75,24 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getDepartments(self):
req = await self.get('/getDepartments')
token = await self.get_token()
req = await self.get(
'/getDepartments', headers={'Authorization': f'Bearer {token}'}
)
match req.status_code:
case st.HTTP_200_OK:
return s.OrganizationsModel.model_validate(req.json())
case _:
self.logger.error(req.json())
self.logger.error(req.text)
raise e.UnknownException
async def getWorkers(self, departmentId: str):
token = await self.get_token()
req = await self.get(
'/getWorkers', params={'departmentId': departmentId}
'/getWorkers',
params={'departmentId': departmentId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
@ -50,7 +103,10 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getSpecsV021(self):
req = await self.get('/getSpecsV021')
token = await self.get_token()
req = await self.get(
'/getSpecsV021', headers={'Authorization': f'Bearer {token}'}
)
match req.status_code:
case st.HTTP_200_OK:
@ -60,7 +116,13 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getEntries(self, patId: str):
req = await self.get('/getEntries', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getEntries',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
@ -76,17 +138,31 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getVaccsReport(self, patId: str):
req = await self.get('/getVaccsReport', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getVaccsReport',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.VaccsReportModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if error.error == 'Не найдены записи по указанному patId':
return s.VaccsReportModel(content='')
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getMedExamDict(self):
req = await self.get('/getMedExamDict')
token = await self.get_token()
req = await self.get(
'/getMedExamDict', headers={'Authorization': f'Bearer {token}'}
)
match req.status_code:
case st.HTTP_200_OK:
@ -96,19 +172,39 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getRoutesList(self, patId: str):
req = await self.get('/getRoutesList', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getRoutesList',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.RoutesListModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if error.error == 'Не найдены случаи по указанному patId':
return s.RoutesListModel(
EventID='none',
EventDate=datetime.now(UTC),
LpuName='fakeName',
Routes=[],
)
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getHospExaminations(self, patId: str, examId: str):
token = await self.get_token()
req = await self.get(
'/getHospExaminations',
params={'patId': patId, 'examId': examId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
@ -119,49 +215,123 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getCurrHosp(self, patId: str):
req = await self.get('/getCurrHosp', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getCurrHosp',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.CurHospitalizationsModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if error.error == 'Пациент не госпитализирован!':
return s.CurHospitalizationsModel(
Hospitalizations=[],
)
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getHosps(self, patId: str):
req = await self.get('/getHosps', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getHosps',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.HospitalizationsModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if (
error.error
== 'Не найдены госпитализации по указанному patId'
):
return s.HospitalizationsModel(
Hospitalizations=[],
)
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getHospRecommendations(self, patId: str):
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getHospRecommendations', params={'patId': patId}
'/getHospRecommendations',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.HospRecommendationsModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if (
error.error
== 'Не найдены госпитализации по указанному patId'
):
return s.HospRecommendationsModel(
EventID='none',
EventDate=datetime.now(UTC),
Recommendations=[],
)
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getHospRoutes(self, patId: str):
req = await self.get('/getHospRoutes', params={'patId': patId})
token = await self.get_token()
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get(
'/getHospRoutes',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
return s.HospRoutesModel.model_validate(req.json())
case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json())
if error.error == 'Пациент не госпитализирован!':
return s.HospRoutesModel(
EventID='none',
EventDate=datetime.now(UTC),
RoutesToDoctor=[],
RoutesToDiagnostic=[],
)
case _:
self.logger.error(req.json())
raise e.UnknownException
async def getDiagnosticResults(self, patId: str):
req = await self.get('/getDiagnosticResults', params={'patId': patId})
token = await self.get_token()
req = await self.get(
'/getDiagnosticResults',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
@ -171,7 +341,12 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getELNs(self, patId: str):
req = await self.get('/getELNs', params={'patId': patId})
token = await self.get_token()
req = await self.get(
'/getELNs',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:
@ -181,7 +356,12 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException
async def getPatFLG(self, patId: str):
req = await self.get('/getPatFLG', params={'patId': patId})
token = await self.get_token()
req = await self.get(
'/getPatFLG',
params={'patId': patId},
headers={'Authorization': f'Bearer {token}'},
)
match req.status_code:
case st.HTTP_200_OK:

View File

@ -7,6 +7,31 @@ class ErrorModel(BaseModel):
error: str = Field(title='Текст ошибки')
class PatientModel(BaseModel):
id: str = Field(
title='Идентификатор пациента',
examples=['b62e9f22-a871-4c52-96d6-559c707a716d'],
)
SNILS: str = Field(title='СНИЛС', examples=['000-000-600 18'])
lastName: str = Field(title='Фамилия', examples=['Тестовый'])
firstName: str = Field(title='Имя', examples=['Пациент'])
middleName: str = Field(title='Отчество', examples=['Пациентович'])
birthDate: datetime = Field(title='Дата рождения', examples=['2024-10-16'])
gender: str = Field(title='Пол', examples=['М'])
docType: str = Field(title='Тип документа', examples=['Паспорт РФ'])
docSer: str = Field(title='Серия документа', examples=['12 34'])
docNum: str = Field(title='Номер документа', examples=['999999'])
polNum: str = Field(title='Номер полиса', examples=['999999'])
address1: str = Field(
title='Адрес проживания',
examples=['г. Москва, ул. Пушкина, д. 1'],
)
class PatientsModel(BaseModel):
patients: list[PatientModel]
class TrustedPersonModel(BaseModel):
parentSnils: str = Field(
title='СНИЛС представителя', examples=['156-125-394 57']
@ -66,7 +91,7 @@ class ProfileModel(BaseModel):
# examples=['99'],
# )
trustedPersons: list[TrustedPersonModel] = Field(
title='Информация о представителе',
title='Информация о представителе', default=[]
)
@ -79,10 +104,10 @@ class DepartmentAddressModel(BaseModel):
title='Адрес строкой',
examples=['420097, г.Казань, ул.Заслонова, д.5'],
)
latitude: float | None = Field(
latitude: str | None = Field(
title='Широта, при наличии', examples=[55.789], default=None
)
longitude: float | None = Field(
longitude: str | None = Field(
title='Долгота, при наличии', examples=[37.789], default=None
)
@ -92,16 +117,19 @@ class DepartmentModel(BaseModel):
title='Идентификатор МО/Филиала',
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
)
OID: str = Field(
title='OID МО / Филиала', examples=['1.2.643.5.1.13.13.12.2.16.1084']
OID: str | None = Field(
title='OID МО / Филиала',
examples=['1.2.643.5.1.13.13.12.2.16.1084'],
default=None,
)
parentId: str | None = Field(
title='Идентификатор вышестоящего подразделения',
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
)
fullname: str = Field(
fullname: str | None = Field(
title='Полное наименование',
examples=['ГБУЗС "Тестовая медицинская организация"'],
default=None,
)
shortname: str = Field(
title='Краткое наименование',
@ -112,14 +140,12 @@ class DepartmentModel(BaseModel):
', для филиалов: Стационар / Поликлиника / ФАП / Амбулатория)',
examples=['Юридическое лицо'],
)
inn: str = Field(title='ИНН', examples=['0000000000'])
ogrn: str = Field(title='ОГРН', examples=['1149204047816'])
inn: str | None = Field(title='ИНН', examples=['0000000000'], default=None)
ogrn: str | None = Field(
title='ОГРН', examples=['1149204047816'], default=None
)
kpp: str | None = Field(title='КПП', examples=['0000000000'], default=None)
address: list[DepartmentAddressModel]
# code: str = Field(
# title='Региональный код или код ТФОМС',
# examples=['0000000000'],
# )
address: list[DepartmentAddressModel] | None = None
class OrganizationsModel(BaseModel):
@ -642,7 +668,7 @@ class PatientFLGModel(BaseModel):
title='Дата следующего флюорографического осмотра',
examples=['2021-09-24'],
)
PrgContingent: str = Field(
PrgContingent: str | None = Field(
title='Контингент (флюорография)',
examples=['Неорганизованное население'],
)