diff --git a/pyproject.toml b/pyproject.toml index c6d3ecf..c5b95bb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -29,6 +29,7 @@ dependencies = [ "pydantic-extra-types==2.10.5", "semver==3.0.4", "pyjwt==2.10.1", + "python-multipart==0.0.20", # CLI "typer-slim==0.16.1", ] diff --git a/src/apps/remd/__init__.py b/src/apps/remd/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/remd/v1/__init__.py b/src/apps/remd/v1/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/remd/v1/router.py b/src/apps/remd/v1/router.py new file mode 100644 index 0000000..9b0d31c --- /dev/null +++ b/src/apps/remd/v1/router.py @@ -0,0 +1,15 @@ +from logging import getLogger + +from fastapi import APIRouter + +logger = getLogger(__name__) +router = APIRouter( + prefix='/remd', + tags=[ + 'REMD', + ], +) + + +@router.get('/callback') +async def callback(): ... diff --git a/src/apps/users/v1/router.py b/src/apps/users/v1/router.py index f3ecbfd..d4c2f2c 100644 --- a/src/apps/users/v1/router.py +++ b/src/apps/users/v1/router.py @@ -3,7 +3,7 @@ from json import dumps from logging import getLogger from typing import Annotated -from fastapi import APIRouter, Body, Depends, status +from fastapi import APIRouter, Body, Depends, UploadFile, status from apps.tdn.auth import token from apps.users.auth import login @@ -91,7 +91,7 @@ async def get_routes_list(user: Annotated[User, Depends(login)]): @router.get('/getHospExaminations') async def get_hosp_examinations( - user: Annotated[User, Depends(login)], examId: str + user: Annotated[User, Depends(login)], examId: str | None = None ): """ Get list of hospital examinations. @@ -178,37 +178,6 @@ async def aemd(user: Annotated[User, Depends(login)]): ) -# @router.post('/measurement', status_code=status.HTTP_202_ACCEPTED) -# async def measurement(tdn_access_token: Annotated[str, Depends(token)]): -# patientId = '6debe050-b57e-442b-9b0e-8d304ca382b0' -# observations = await c.tdn_api.observations_search( -# tdn_access_token, patientId -# ) - -# if observations.total == 0: -# raise e.NotFoundException(detail='No observations found') - -# observation = observations.items[-1] -# observation_measurements = await -# c.tdn_api.observations_measurement_search( -# tdn_access_token, observation.uid -# ) - -# return observation_measurements - -# created = created_at.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}' -# cache.set(cache_key, dumps(data)) - - @router.post('/measurement', status_code=status.HTTP_202_ACCEPTED) async def measurement( tdn_access_token: Annotated[str, Depends(token)], @@ -217,7 +186,9 @@ async def measurement( sd: Annotated[int, Body()], pulse: Annotated[int, Body()], comment: Annotated[str, Body()], - status: Annotated[str, Body()], + status: Annotated[int, Body(ge=1, le=3)], + serial_number: Annotated[str, Body()], + ekg: UploadFile, ): observations = await c.tdn_api.observations_search( tdn_access_token, user.vita_id @@ -235,6 +206,7 @@ async def measurement( dad_measurement = None pulse_measurement = None health_measurement = None + health_measurement_created_at = None observations = observations.items[::-1] for observation in observations: @@ -264,8 +236,12 @@ async def measurement( health_observationUid = measurement.observationUid for metric in measurement.obsrvMtMetrics: - if metric.metric.code == 'HEALTH': + 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 @@ -276,6 +252,7 @@ async def measurement( or not health_obsrvMeasurementUid or not health_observationUid or not health_measurement + or not health_measurement_created_at ): ad_obsrvMeasurementUid = None sad_measurement = None @@ -285,6 +262,7 @@ async def measurement( health_obsrvMeasurementUid = None health_observationUid = None health_measurement = None + health_measurement_created_at = None else: break @@ -320,17 +298,17 @@ async def measurement( # SAD await c.tdn_api.create_series_values( - tdn_access_token, ad_series_uid, sad_measurement, nvalue=str(sd) + 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=str(ad) + 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=str(pulse) + tdn_access_token, ad_series_uid, pulse_measurement, nvalue=pulse ) health_series = await c.tdn_api.create_series( @@ -346,7 +324,16 @@ async def measurement( tdn_access_token, health_series_uid, health_measurement, - svalue=str(comment), + nvalue=status, + svalue=comment, + ) + + # EKG + await c.tdn_api.ekg( + tdn_access_token, + user.vita_id, + serial_number, + ekg, ) created = datetime.now(UTC).strftime('%Y-%m-%d %H:%M:%S') @@ -364,7 +351,9 @@ async def measurement( @router.get('/measurements') async def measurements( - user: Annotated[str, Depends(login)], + user: Annotated[User, Depends(login)], ): - data = [cache.get(key) for key in cache.keys(f'tdn:measurement:{user}:*')] + data = [ + cache.get(key) for key in cache.keys(f'tdn:measurement:{user.id}:*') + ] return data diff --git a/src/clients/tdn/api.py b/src/clients/tdn/api.py index def77a5..542c369 100644 --- a/src/clients/tdn/api.py +++ b/src/clients/tdn/api.py @@ -3,6 +3,7 @@ from json import dumps from logging import getLogger from urllib.parse import quote, urlencode +from fastapi import UploadFile from fastapi import status as st from httpx import AsyncClient @@ -130,11 +131,11 @@ class TDN_API(AsyncClient): seriesUid: str, obsrvMtMetricUid: str, *, - nvalue: str | None = None, - fvalue: str | None = None, + nvalue: int | None = None, + fvalue: float | None = None, svalue: str | None = None, ): - data = { + data: dict[str, str | int | float] = { 'seriesUid': seriesUid, 'obsrvMtMetricUid': obsrvMtMetricUid, } @@ -160,3 +161,27 @@ class TDN_API(AsyncClient): case _: self.logger.error(res.json()) raise e.UnknownException + + async def ekg( + self, + access_token: str, + patientUid: str, + serial_number: str, + file: UploadFile, + ): + req = await self.post( + '/ddn/observation/series-values/ecg/krb02', + headers={'Authorization': f'Bearer {access_token}'}, + files={'ekg': (file.filename, file.file, file.content_type)}, + json={ + 'patientUid': patientUid, + 'serialNumber': serial_number, + }, + ) + + match req.status_code: + case st.HTTP_200_OK: + return s.EkgModel.model_validate(req.json()) + case _: + self.logger.error(req.json()) + raise e.UnknownException diff --git a/src/clients/tdn/schema.py b/src/clients/tdn/schema.py index b8ee38a..1c14360 100644 --- a/src/clients/tdn/schema.py +++ b/src/clients/tdn/schema.py @@ -108,3 +108,8 @@ class SeriesValueModel(BaseModel): filepath: str | None mobileId: str | None tisId: str | None + + +class EkgModel(BaseModel): + success: bool + # result: ... diff --git a/src/clients/vitacore/api.py b/src/clients/vitacore/api.py index 2feb85e..35741df 100644 --- a/src/clients/vitacore/api.py +++ b/src/clients/vitacore/api.py @@ -199,7 +199,7 @@ class VITACORE_API(AsyncClient): self.logger.error(req.json()) raise e.UnknownException - async def getHospExaminations(self, patId: str, examId: str): + async def getHospExaminations(self, patId: str, examId: str | None = None): patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' token = await self.get_token() req = await self.get( diff --git a/src/core/routers/v1.py b/src/core/routers/v1.py index 330f0df..d71913d 100644 --- a/src/core/routers/v1.py +++ b/src/core/routers/v1.py @@ -1,6 +1,7 @@ from fastapi import APIRouter, HTTPException from apps.esia.v1.router import router as esia_router +from apps.remd.v1.router import router as remd_router from apps.users.v1.router import router as users_router from . import get_openapi_schema, get_swagger_html @@ -9,6 +10,7 @@ router = APIRouter(prefix='/v1') router.include_router(esia_router) router.include_router(users_router) +router.include_router(remd_router) openapi_schema = get_openapi_schema(router) swagger_ui_html = get_swagger_html(router)