Патч
Some checks failed
Build And Push / publish (push) Has been cancelled

This commit is contained in:
2025-11-27 13:28:58 +03:00
parent 45a4123708
commit f3c9cb42d6
12 changed files with 172 additions and 88 deletions

View File

@ -49,22 +49,20 @@ async def callback(session: AsyncSessionDep, code: str):
token.access_token, token.id_token token.access_token, token.id_token
) )
try:
vita_user = await c.vitacore_api.findBySnils(esia_user.snils) vita_user = await c.vitacore_api.findBySnils(esia_user.snils)
except e.UnknownException:
if len(vita_user.patients) == 0: raise e.BadRequestException(detail='Patient not found') from None
raise e.BadRequestException(detail='Patient not found')
vita_user = vita_user.patients[0]
existing_user_stmt = ( existing_user_stmt = (
select(User).where(User.vita_id == vita_user.id).limit(1) select(User).where(User.vita_id == vita_user.patId).limit(1)
) )
existing_user = ( existing_user = (
await session.execute(existing_user_stmt) await session.execute(existing_user_stmt)
).scalar_one_or_none() ).scalar_one_or_none()
if existing_user is None: if existing_user is None:
user = User(vita_id=vita_user.id) user = User(vita_id=vita_user.patId)
session.add(user) session.add(user)
await session.commit() await session.commit()
await session.refresh(user) await session.refresh(user)

0
src/apps/tmk/__init__.py Normal file
View File

View File

41
src/apps/tmk/v1/router.py Normal file
View File

@ -0,0 +1,41 @@
from json import dumps
from logging import getLogger
from typing import Annotated
from fastapi import APIRouter, Body
from sqlmodel import select
from apps.users.models import User
from clients import clients as c
from database import AsyncSessionDep
from shared.exceptions import UnknownException
from shared.redis import client as cache
logger = getLogger(__name__)
router = APIRouter(
prefix='/tmk',
tags=[
'TMK',
],
)
@router.post('/update')
async def update(session: AsyncSessionDep, tmk_id: Annotated[str, Body()]):
info = await c.tmk_api.getInfo(tmk_id)
snils = info.patient_snils
try:
patient = await c.vitacore_api.findBySnils(snils)
except UnknownException:
return
user_stmt = select(User).where(User.vita_id == patient.patId).limit(1)
user = await session.scalar(user_stmt)
if user is None:
return
key = f'tmk:{user.id}:{tmk_id}'
value = {'id': info.id, 'status': info.tmk_status_name, 'is_read': False}
await cache.set(key, dumps(value))

View File

@ -4,6 +4,7 @@ from logging import getLogger
from typing import Annotated from typing import Annotated
from fastapi import APIRouter, Body, Depends, UploadFile, status from fastapi import APIRouter, Body, Depends, UploadFile, status
from orjson import loads
from apps.tdn.auth import token from apps.tdn.auth import token
from apps.users.auth import login from apps.users.auth import login
@ -387,22 +388,31 @@ async def delete_account(user: Annotated[User, Depends(login)]):
@router.get('/notifications') @router.get('/notifications')
async def notifications(user: Annotated[User, Depends(login)]): 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( return s.Notifications(
notifications=[ notifications=notif,
{
'id': 1,
'title': 'Заголовок уведомления 1',
'description': 'Описание уведомления',
},
{
'id': 2,
'title': 'Заголовок уведомления 2',
'description': 'Описание уведомления',
},
{
'id': 3,
'title': 'Заголовок уведомления 3',
'description': 'Описание уведомления',
},
]
) )
@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))

View File

@ -1,11 +1,11 @@
from typing import TypedDict from typing import TypedDict
from pydantic import BaseModel
class Notification(TypedDict):
id: int
title: str
description: str
class Notifications(TypedDict): class Notifications(TypedDict):
notifications: list[Notification] notifications: list[dict[str, str | bool]]
class Complaints(BaseModel):
complaints: str

View File

@ -2,6 +2,10 @@ from logging import getLogger
from fastapi import APIRouter from fastapi import APIRouter
from shared.redis import client as cache
from . import schema as s
logger = getLogger(__name__) logger = getLogger(__name__)
router = APIRouter( router = APIRouter(
prefix='/vitacore', prefix='/vitacore',
@ -12,5 +16,14 @@ router = APIRouter(
@router.post('/hospComplaint') @router.post('/hospComplaint')
async def callback(): async def callback(complaint: s.HospComplaint):
pass value = await cache.get(f'complaint:{complaint.patID}')
value = value.decode() if value else ''
return s.ComplaintData(
patId=complaint.patID,
complaints=(
f'{value} | {complaint.eventId} | {complaint.datetime} | '
f'{complaint.MO_id}'
),
)

View File

@ -0,0 +1,16 @@
from datetime import datetime
from typing import TypedDict
from pydantic import BaseModel
class HospComplaint(BaseModel):
patID: str
eventId: str
datetime: datetime
MO_id: str
class ComplaintData(TypedDict):
patId: str
complaints: str

View File

@ -93,3 +93,19 @@ class TMK_API(AsyncClient):
case _: case _:
self.logger.error(req.json()) self.logger.error(req.json())
raise e.UnknownException raise e.UnknownException
async def getInfo(self, guid: str):
token = await self.get_token()
req = await self.get(
'/getTMKInfo',
headers={'Authorization': f'Bearer {token}'},
params={'guid': guid},
)
match req.status_code:
case st.HTTP_200_OK:
return s.QueueModel.model_validate(req.json())
case _:
self.logger.error(req.json())
raise e.UnknownException

View File

@ -58,27 +58,28 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException raise e.UnknownException
async def findBySnils(self, snils: str): async def findBySnils(self, snils: str):
data = await self.get_cache(f'vitacore_findBySnils:{snils}') data = await self.get_cache(f'vitacore_findBySnils2:{snils}')
if data: if data:
return s.PatientsModel.model_validate(data) return s.PatientModel.model_validate(data)
token = await self.get_token() token = await self.get_token()
req = await self.get( req = await self.get(
'/findBySnils', '/findBySnils2',
params={'snils': snils}, params={'snils': snils},
headers={'Authorization': f'Bearer {token}'}, headers={'Authorization': f'Bearer {token}'},
) )
match req.status_code: match req.status_code:
case st.HTTP_200_OK: case st.HTTP_200_OK:
model = s.PatientsModel.model_validate(req.json()) model = s.PatientModel.model_validate(req.json())
await self.set_cache( await self.set_cache(
f'vitacore_findBySnils:{snils}', f'vitacore_findBySnils2:{snils}',
model.model_dump_json(), model.model_dump_json(),
14400, 14400,
) )
return model return model
case _: case _:
self.logger.error(req.json()) self.logger.error(req.json())
raise e.UnknownException raise e.UnknownException
@ -177,10 +178,7 @@ class VITACORE_API(AsyncClient):
async def getEntries(self, patId: str): async def getEntries(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
@ -204,11 +202,9 @@ class VITACORE_API(AsyncClient):
async def getVaccsReport(self, patId: str): async def getVaccsReport(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getVaccsReport', '/getVaccsReport',
params={'patId': patId}, params={'patId': patId},
@ -221,7 +217,11 @@ class VITACORE_API(AsyncClient):
case st.HTTP_206_PARTIAL_CONTENT: case st.HTTP_206_PARTIAL_CONTENT:
error = s.ErrorModel.model_validate(req.json()) error = s.ErrorModel.model_validate(req.json())
if error.error == 'Не найдены записи по указанному patId': if (
error.error == 'Не найдены записи по указанному patId'
or error.error
== 'Не найдены вакцинации по данному пациенту'
):
return s.VaccsReportModel(content='') return s.VaccsReportModel(content='')
case _: case _:
self.logger.error(req.json()) self.logger.error(req.json())
@ -242,11 +242,9 @@ class VITACORE_API(AsyncClient):
async def getRoutesList(self, patId: str): async def getRoutesList(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getRoutesList', '/getRoutesList',
params={'patId': patId}, params={'patId': patId},
@ -273,11 +271,9 @@ class VITACORE_API(AsyncClient):
raise e.UnknownException raise e.UnknownException
async def getHospExaminations(self, patId: str, examId: str | None = None): async def getHospExaminations(self, patId: str, examId: str | None = None):
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
token = await self.get_token() token = await self.get_token()
req = await self.get( req = await self.get(
'/getHospExaminations', '/getHospExaminations',
@ -306,11 +302,9 @@ class VITACORE_API(AsyncClient):
async def getCurrHosp(self, patId: str): async def getCurrHosp(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getCurrHosp', '/getCurrHosp',
params={'patId': patId}, params={'patId': patId},
@ -335,11 +329,9 @@ class VITACORE_API(AsyncClient):
async def getHosps(self, patId: str): async def getHosps(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getHosps', '/getHosps',
params={'patId': patId}, params={'patId': patId},
@ -367,11 +359,9 @@ class VITACORE_API(AsyncClient):
async def getHospRecommendations(self, patId: str): async def getHospRecommendations(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getHospRecommendations', '/getHospRecommendations',
params={'patId': patId}, params={'patId': patId},
@ -401,11 +391,9 @@ class VITACORE_API(AsyncClient):
async def getHospRoutes(self, patId: str): async def getHospRoutes(self, patId: str):
token = await self.get_token() token = await self.get_token()
if ( if patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd':
patId == 'a72d18cf-c152-4b9e-b8be-313234b87400'
or patId == '9a4d4b06-5928-4101-b95e-e5ba03a1abfd'
):
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e' patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
req = await self.get( req = await self.get(
'/getHospRoutes', '/getHospRoutes',
params={'patId': patId}, params={'patId': patId},

View File

@ -1,14 +1,19 @@
from datetime import datetime from datetime import datetime
from pydantic import BaseModel, Field, field_validator from pydantic import BaseModel, ConfigDict, Field, field_validator
class ErrorModel(BaseModel): class ErrorModel(BaseModel):
error: str = Field(title='Текст ошибки') model_config = ConfigDict(
validate_by_alias=True,
validate_by_name=True,
)
error: str = Field(title='Текст ошибки', alias='message')
class PatientModel(BaseModel): class PatientModel(BaseModel):
id: str = Field( patId: str = Field(
title='Идентификатор пациента', title='Идентификатор пациента',
examples=['b62e9f22-a871-4c52-96d6-559c707a716d'], examples=['b62e9f22-a871-4c52-96d6-559c707a716d'],
) )
@ -18,18 +23,6 @@ class PatientModel(BaseModel):
middleName: str = Field(title='Отчество', examples=['Пациентович']) middleName: str = Field(title='Отчество', examples=['Пациентович'])
birthDate: datetime = Field(title='Дата рождения', examples=['2024-10-16']) birthDate: datetime = Field(title='Дата рождения', examples=['2024-10-16'])
gender: str = Field(title='Пол', examples=['М']) 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): class TrustedPersonModel(BaseModel):
@ -215,7 +208,9 @@ class WorkerModel(BaseModel):
firstName: str = Field(title='Имя', examples=['Владимир']) firstName: str = Field(title='Имя', examples=['Владимир'])
lastName: str = Field(title='Фамилия', examples=['Камашев']) lastName: str = Field(title='Фамилия', examples=['Камашев'])
middleName: str = Field(title='Отчество', examples=['Михайлович']) middleName: str = Field(title='Отчество', examples=['Михайлович'])
birthDate: datetime = Field(title='Дата рождения', examples=['30.05.1961']) birthDate: datetime | None = Field(
title='Дата рождения', examples=['30.05.1961'], default=None
)
positions: list[WorkersPositionModel] positions: list[WorkersPositionModel]
@ -411,9 +406,10 @@ class ExaminationModel(BaseModel):
'style="BORDER-TOP: #ffffff 1px..... </TABLE>' 'style="BORDER-TOP: #ffffff 1px..... </TABLE>'
], ],
) )
Recommendation: str = Field( Recommendation: str | None = Field(
title='Идентификатор результата исследования', title='Идентификатор результата исследования',
examples=['рекомендации 1 тест'], examples=['рекомендации 1 тест'],
default=None,
) )
SEMDs: list[SEMDModel] | None = Field(title='Список СЭМД', default=None) SEMDs: list[SEMDModel] | None = Field(title='Список СЭМД', default=None)
@ -688,6 +684,10 @@ class PatientFLGModel(BaseModel):
title='Контингент (флюорография)', title='Контингент (флюорография)',
examples=['Неорганизованное население'], examples=['Неорганизованное население'],
) )
PrgDecision: str | None = Field(
title='Решение (флюорография)',
examples=['Требует дообследования'],
)
class DiagResultFileModel(BaseModel): class DiagResultFileModel(BaseModel):

View File

@ -2,6 +2,7 @@ from fastapi import APIRouter, HTTPException
from apps.esia.v1.router import router as esia_router from apps.esia.v1.router import router as esia_router
from apps.remd.v1.router import router as remd_router from apps.remd.v1.router import router as remd_router
from apps.tmk.v1.router import router as tmk_router
from apps.users.v1.router import router as users_router from apps.users.v1.router import router as users_router
from apps.vitacore.v1.router import router as vitacore_router from apps.vitacore.v1.router import router as vitacore_router
@ -13,6 +14,7 @@ router.include_router(esia_router)
router.include_router(users_router) router.include_router(users_router)
router.include_router(remd_router) router.include_router(remd_router)
router.include_router(vitacore_router) router.include_router(vitacore_router)
router.include_router(tmk_router)
openapi_schema = get_openapi_schema(router) openapi_schema = get_openapi_schema(router)
swagger_ui_html = get_swagger_html(router) swagger_ui_html = get_swagger_html(router)