479 lines
13 KiB
Python
479 lines
13 KiB
Python
import base64
|
|
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
|
|
from orjson import loads
|
|
|
|
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 clients import clients as c
|
|
from clients.tmk import schema as ts
|
|
from clients.vitacore import schema as vs
|
|
from shared import exceptions as e
|
|
from shared.redis import client as cache
|
|
|
|
from . import schema as s
|
|
|
|
logger = getLogger(__name__)
|
|
router = APIRouter(
|
|
prefix='/user',
|
|
tags=[
|
|
'User',
|
|
],
|
|
)
|
|
|
|
|
|
@router.get(
|
|
'/getProfile',
|
|
responses={
|
|
status.HTTP_200_OK: {'model': vs.ProfileModel},
|
|
},
|
|
)
|
|
async def get_profile(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get profile of user.
|
|
"""
|
|
return await c.vitacore_api.getProfile(user.vita_id)
|
|
|
|
|
|
@router.get(
|
|
'/getDepartments',
|
|
responses={
|
|
status.HTTP_200_OK: {'model': vs.OrganizationsModel},
|
|
},
|
|
)
|
|
async def get_departments():
|
|
"""
|
|
Get list of departments.
|
|
"""
|
|
return await c.vitacore_api.getDepartments()
|
|
|
|
|
|
@router.get(
|
|
'/getWorkers', responses={status.HTTP_200_OK: {'model': vs.WorkersModel}}
|
|
)
|
|
async def get_workers(
|
|
user: Annotated[User, Depends(login)], departmentId: str
|
|
):
|
|
"""
|
|
Get list of workers by department id.
|
|
"""
|
|
return await c.vitacore_api.getWorkers(departmentId)
|
|
|
|
|
|
@router.get(
|
|
'/getSpecs',
|
|
responses={status.HTTP_200_OK: {'model': vs.SpecsV021Model}},
|
|
)
|
|
async def get_specs(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of specialties.
|
|
"""
|
|
return await c.vitacore_api.getSpecsV021()
|
|
|
|
|
|
@router.get(
|
|
'/getEntries', responses={status.HTTP_200_OK: {'model': vs.EntriesModel}}
|
|
)
|
|
async def get_entries(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of entries for user by id.
|
|
"""
|
|
return await c.vitacore_api.getEntries(user.vita_id)
|
|
|
|
|
|
@router.get('/getVaccsReport')
|
|
async def get_vaccs_report(
|
|
user: Annotated[User, Depends(login)], resultId: str | None = None
|
|
):
|
|
"""
|
|
Get report of vaccinations for user by id.
|
|
"""
|
|
if resultId is not None:
|
|
return await c.vitacore_api.getDiagResultFile(resultId)
|
|
|
|
return await c.vitacore_api.getVaccsReport(user.vita_id)
|
|
|
|
|
|
@router.get('/getMedExamDict')
|
|
async def get_med_exam_dict(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get medical examination dictionary.
|
|
"""
|
|
return await c.vitacore_api.getMedExamDict()
|
|
|
|
|
|
@router.get('/getRoutesList')
|
|
async def get_routes_list(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of routes.
|
|
"""
|
|
return await c.vitacore_api.getRoutesList(user.vita_id)
|
|
|
|
|
|
@router.get('/getHospExaminations')
|
|
async def get_hosp_examinations(
|
|
user: Annotated[User, Depends(login)], examId: str | None = None
|
|
):
|
|
"""
|
|
Get list of hospital examinations.
|
|
"""
|
|
return await c.vitacore_api.getHospExaminations(
|
|
user.vita_id,
|
|
examId,
|
|
)
|
|
|
|
|
|
@router.get('/getCurrHosp')
|
|
async def get_curr_hosp(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get current hospitalization.
|
|
"""
|
|
return await c.vitacore_api.getCurrHosp(user.vita_id)
|
|
|
|
|
|
@router.get('/getHosps')
|
|
async def get_hosps(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of hospitals.
|
|
"""
|
|
return await c.vitacore_api.getHosps(user.vita_id)
|
|
|
|
|
|
@router.get('/getHospRecommendations')
|
|
async def get_hosp_recommendations(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of recommended hospitals.
|
|
"""
|
|
return await c.vitacore_api.getHospRecommendations(user.vita_id)
|
|
|
|
|
|
@router.get('/getHospRoutes')
|
|
async def get_hosp_routes(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of recommended hospitals.
|
|
"""
|
|
return await c.vitacore_api.getHospRoutes(user.vita_id)
|
|
|
|
|
|
@router.get('/getDiagnosticResults')
|
|
async def get_diagnostic_results(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of diagnostic results.
|
|
"""
|
|
return await c.vitacore_api.getDiagnosticResults(user.vita_id)
|
|
|
|
|
|
@router.get('/getELNs')
|
|
async def get_eln(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of ELNs.
|
|
"""
|
|
return await c.vitacore_api.getELNs(user.vita_id)
|
|
|
|
|
|
@router.get('/getPatFLG')
|
|
async def get_pat_flg(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of ELNs.
|
|
"""
|
|
return await c.vitacore_api.getPatFLG(user.vita_id)
|
|
|
|
|
|
@router.get('/queue', response_model=list[ts.QueueModel])
|
|
async def queue(user: Annotated[User, Depends(login)]):
|
|
"""
|
|
Get list of VKS queues.
|
|
"""
|
|
profile = await c.vitacore_api.getProfile(user.vita_id)
|
|
return await c.tmk_api.getQueue(patient_snils=profile.SNILS)
|
|
|
|
|
|
@router.get('/aemd')
|
|
async def get_aemd(
|
|
user: Annotated[User, Depends(login)],
|
|
parsable_ids: Annotated[list[str], Depends(get_parsable_ids)],
|
|
):
|
|
profile = await c.vitacore_api.getProfile(user.vita_id)
|
|
snils = profile.SNILS.replace('-', '').replace(' ', '')
|
|
docs = await c.aemd_api.searchRegistryItem(patient_snils=snils)
|
|
items: list[s.AEMDFile] = docs['items']
|
|
return_items: list[s.AEMDReturnFile] = []
|
|
|
|
for item in items:
|
|
if item['DocKind'] not in parsable_ids:
|
|
continue
|
|
|
|
is_cached = await cache.get(f'aemd:{user.vita_id}:{item["emdrId"]}')
|
|
|
|
return_items.append(
|
|
s.AEMDReturnFile(
|
|
emdrId=item['emdrId'],
|
|
registrationDate=item['registrationDate'],
|
|
DocKind=item['DocKind'],
|
|
IsSemd=item['IsSemd'],
|
|
isCached=bool(is_cached),
|
|
)
|
|
)
|
|
|
|
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, docKind: str
|
|
):
|
|
data = await cache.get(f'aemd:{user.vita_id}:{emdrId}')
|
|
|
|
if not data:
|
|
raise e.NotFoundException(status_code=404, detail='File not found')
|
|
|
|
b64 = loads(data)['data']
|
|
decoded = base64.b64decode(b64)
|
|
pdf = await convert_aemd_to_pdf(decoded, docKind)
|
|
b64_pdf = base64.b64encode(pdf).decode('utf-8')
|
|
|
|
return {
|
|
'filename': f'{emdrId}.pdf',
|
|
'content_type': 'application/pdf',
|
|
'data': b64_pdf,
|
|
}
|
|
|
|
|
|
@router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
|
async def measurement(
|
|
tdn_access_token: Annotated[str, Depends(token)],
|
|
user: Annotated[User, Depends(login)],
|
|
ad: Annotated[int, Body()],
|
|
sd: Annotated[int, Body()],
|
|
pulse: Annotated[int, Body()],
|
|
comment: Annotated[str, Body()],
|
|
status: Annotated[int, Body(ge=1, le=3)],
|
|
serial_number: Annotated[str, Body()],
|
|
ekg: UploadFile,
|
|
):
|
|
vitaId = '109df850-2268-49fb-bb78-2e0f3c57314d'
|
|
patient = await c.tdn_api.patient_search(tdn_access_token, vitaId)
|
|
patientUid = patient['items'][0]['uid']
|
|
observations = await c.tdn_api.observations_search(
|
|
tdn_access_token, patientUid
|
|
)
|
|
|
|
if observations.total == 0:
|
|
raise e.NotFoundException(detail='No observations found')
|
|
|
|
ad_obsrvMeasurementUid = None
|
|
ad_observationUid = None
|
|
health_obsrvMeasurementUid = None
|
|
health_observationUid = None
|
|
|
|
sad_measurement = None
|
|
dad_measurement = None
|
|
pulse_measurement = None
|
|
health_measurement = None
|
|
health_measurement_created_at = None
|
|
|
|
observations = observations.items[::-1]
|
|
for observation in observations:
|
|
observation_measurements = (
|
|
await c.tdn_api.observations_measurement_search(
|
|
tdn_access_token, observation.uid
|
|
)
|
|
)
|
|
|
|
for measurement in observation_measurements.items:
|
|
if measurement.measurement.code == 'ADPULSE':
|
|
ad_obsrvMeasurementUid = measurement.uid
|
|
ad_observationUid = measurement.observationUid
|
|
|
|
for metric in measurement.obsrvMtMetrics:
|
|
if metric.metric.code == 'SAD':
|
|
sad_measurement = metric.uid
|
|
|
|
if metric.metric.code == 'DAD':
|
|
dad_measurement = metric.uid
|
|
|
|
if metric.metric.code == 'PULSE':
|
|
pulse_measurement = metric.uid
|
|
|
|
if measurement.measurement.code == 'HEALTH':
|
|
health_obsrvMeasurementUid = measurement.uid
|
|
health_observationUid = measurement.observationUid
|
|
|
|
for metric in measurement.obsrvMtMetrics:
|
|
if metric.metric.code == 'HEALTH' and (
|
|
health_measurement_created_at is None
|
|
or metric.createdAt > health_measurement_created_at
|
|
):
|
|
health_measurement = metric.uid
|
|
health_measurement_created_at = metric.createdAt
|
|
|
|
if (
|
|
not ad_obsrvMeasurementUid
|
|
or not sad_measurement
|
|
or not dad_measurement
|
|
or not pulse_measurement
|
|
or not ad_observationUid
|
|
or not health_obsrvMeasurementUid
|
|
or not health_observationUid
|
|
or not health_measurement
|
|
or not health_measurement_created_at
|
|
):
|
|
ad_obsrvMeasurementUid = None
|
|
sad_measurement = None
|
|
dad_measurement = None
|
|
pulse_measurement = None
|
|
ad_observationUid = None
|
|
health_obsrvMeasurementUid = None
|
|
health_observationUid = None
|
|
health_measurement = None
|
|
health_measurement_created_at = None
|
|
|
|
else:
|
|
break
|
|
|
|
if not ad_obsrvMeasurementUid or not ad_observationUid:
|
|
raise e.NotFoundException(detail='No ADPULSE measurement found')
|
|
|
|
if not sad_measurement:
|
|
raise e.NotFoundException(detail='No SAD measurement found')
|
|
|
|
if not dad_measurement:
|
|
raise e.NotFoundException(detail='No DAD measurement found')
|
|
|
|
if not pulse_measurement:
|
|
raise e.NotFoundException(detail='No PULSE measurement found')
|
|
|
|
if not health_obsrvMeasurementUid or not health_observationUid:
|
|
raise e.NotFoundException(detail='No HEALTH measurement found')
|
|
|
|
if not health_measurement:
|
|
raise e.NotFoundException(detail='No HEALTH measurement found')
|
|
|
|
if not health_obsrvMeasurementUid or not health_observationUid:
|
|
raise e.NotFoundException(detail='No HEALTH measurement found')
|
|
|
|
ad_series = await c.tdn_api.create_series(
|
|
tdn_access_token,
|
|
ad_observationUid,
|
|
ad_obsrvMeasurementUid,
|
|
)
|
|
|
|
ad_series_uid = ad_series.uid
|
|
|
|
# SAD
|
|
await c.tdn_api.create_series_values(
|
|
tdn_access_token, ad_series_uid, sad_measurement, nvalue=sd
|
|
)
|
|
|
|
# DAD
|
|
await c.tdn_api.create_series_values(
|
|
tdn_access_token, ad_series_uid, dad_measurement, nvalue=ad
|
|
)
|
|
|
|
# PULSE
|
|
await c.tdn_api.create_series_values(
|
|
tdn_access_token, ad_series_uid, pulse_measurement, nvalue=pulse
|
|
)
|
|
|
|
health_series = await c.tdn_api.create_series(
|
|
tdn_access_token,
|
|
health_observationUid,
|
|
health_obsrvMeasurementUid,
|
|
)
|
|
|
|
health_series_uid = health_series.uid
|
|
|
|
# HEALTH
|
|
await c.tdn_api.create_series_values(
|
|
tdn_access_token,
|
|
health_series_uid,
|
|
health_measurement,
|
|
nvalue=status,
|
|
svalue=comment,
|
|
)
|
|
|
|
# EKG
|
|
await c.tdn_api.ekg(
|
|
tdn_access_token,
|
|
patientUid,
|
|
serial_number,
|
|
ekg,
|
|
)
|
|
|
|
created = datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S')
|
|
data = {
|
|
'ad': ad,
|
|
'sd': sd,
|
|
'pulse': pulse,
|
|
'created_at': created,
|
|
'comment': comment,
|
|
'status': status,
|
|
}
|
|
cache_key = f'tdn:measurement:{user.id}:{created}'
|
|
await cache.set(cache_key, dumps(data))
|
|
|
|
|
|
@router.get('/measurements')
|
|
async def measurements(
|
|
user: Annotated[User, Depends(login)],
|
|
):
|
|
data = [
|
|
await cache.get(key)
|
|
for key in await cache.keys(f'tdn:measurement:{user.id}:*')
|
|
]
|
|
return data
|
|
|
|
|
|
@router.delete('/account', status_code=status.HTTP_204_NO_CONTENT)
|
|
async def delete_account(user: Annotated[User, Depends(login)]):
|
|
return
|
|
|
|
|
|
@router.get('/notifications')
|
|
async def notifications(user: Annotated[User, Depends(login)]):
|
|
keys = await cache.keys(f'tmk:{user.id}:*')
|
|
notif: list[dict[str, str | bool]] = []
|
|
|
|
for key in keys:
|
|
val = await cache.get(key)
|
|
|
|
if val is None:
|
|
continue
|
|
|
|
value = loads(val)
|
|
notif_val = value.copy()
|
|
notif.append(notif_val)
|
|
|
|
if value['is_read'] is False:
|
|
value['is_read'] = True
|
|
await cache.set(key, dumps(value))
|
|
|
|
return s.Notifications(
|
|
notifications=notif,
|
|
)
|
|
|
|
|
|
@router.post('/complaint', status_code=status.HTTP_204_NO_CONTENT)
|
|
async def complaint(
|
|
user: Annotated[User, Depends(login)], complaints: s.Complaints
|
|
):
|
|
cache_key = f'complaint:{user.vita_id}'
|
|
await cache.set(cache_key, dumps(complaints.complaints))
|