Compare commits
2 Commits
cb3b138241
...
db48b14ac5
| Author | SHA1 | Date | |
|---|---|---|---|
| db48b14ac5 | |||
| a4239a0c52 |
@ -20,3 +20,6 @@ uv.lock
|
|||||||
|
|
||||||
# Container
|
# Container
|
||||||
container
|
container
|
||||||
|
|
||||||
|
# Postgres
|
||||||
|
postgres
|
||||||
|
|||||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -20,3 +20,6 @@ uv.lock
|
|||||||
|
|
||||||
# Container
|
# Container
|
||||||
container
|
container
|
||||||
|
|
||||||
|
# Postgres
|
||||||
|
postgres
|
||||||
|
|||||||
@ -8,13 +8,14 @@ x-app-common: &app-common
|
|||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
stop_signal: SIGINT
|
stop_signal: SIGINT
|
||||||
env_file:
|
env_file:
|
||||||
- .env
|
- .test.env
|
||||||
environment:
|
environment:
|
||||||
DATABASE_URL: "postgresql://postgres:example@db:5432/postgres"
|
DATABASE_URL: "postgresql://postgres:example@db:5432/postgres"
|
||||||
REDIS_URL: "redis://valkey:6379/0"
|
REDIS_URL: "redis://valkey:6379/0"
|
||||||
volumes:
|
volumes:
|
||||||
- "./container/certt.cer:/var/opt/cprocsp/keys/cert.cer"
|
# - "./container/certt.cer:/var/opt/cprocsp/keys/cert.cer"
|
||||||
- "./container/cont:/app/cont"
|
- "./container/test.cer:/var/opt/cprocsp/keys/cert.cer"
|
||||||
|
- "./container/cont2:/app/cont"
|
||||||
|
|
||||||
services:
|
services:
|
||||||
valkey:
|
valkey:
|
||||||
@ -28,6 +29,21 @@ services:
|
|||||||
timeout: 10s
|
timeout: 10s
|
||||||
retries: 5
|
retries: 5
|
||||||
|
|
||||||
|
db:
|
||||||
|
image: postgres:17.2-alpine
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- ${POSTGRES_PORT:-5432}:5432
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: example
|
||||||
|
volumes:
|
||||||
|
- "${POSTGRES_DATA:-./postgres}:/var/lib/postgresql/data/"
|
||||||
|
healthcheck:
|
||||||
|
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
|
||||||
|
interval: 5s
|
||||||
|
timeout: 10s
|
||||||
|
retries: 5
|
||||||
|
|
||||||
web:
|
web:
|
||||||
<<: *app-common
|
<<: *app-common
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@ -50,9 +50,9 @@ _lint = "pre-commit run --all-files"
|
|||||||
|
|
||||||
lint = ["_git", "_lint"]
|
lint = ["_git", "_lint"]
|
||||||
check = "uv pip ls --outdated"
|
check = "uv pip ls --outdated"
|
||||||
run = "uv run --directory ./src/ server.py"
|
run = "uv run --env-file ../.env --directory ./src/ server.py"
|
||||||
manage = "uv run --directory ./src/ manage.py"
|
manage = "uv run --env-file ../.env --directory ./src/ manage.py"
|
||||||
migrate = "uv run --directory ./src/ alembic revision --autogenerate"
|
migrate = "uv run --env-file ../.env --directory ./src/ alembic revision --autogenerate"
|
||||||
|
|
||||||
[tool.uv]
|
[tool.uv]
|
||||||
required-version = ">=0.7.0"
|
required-version = ">=0.7.0"
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
SCOPES = [
|
SCOPES = [
|
||||||
'openid',
|
'openid',
|
||||||
'fullname',
|
'fullname',
|
||||||
'email',
|
# 'email',
|
||||||
'birthdate',
|
# 'birthdate',
|
||||||
'gender',
|
# 'gender',
|
||||||
'snils',
|
# 'snils',
|
||||||
'id_doc',
|
# 'id_doc',
|
||||||
'mobile',
|
# 'mobile',
|
||||||
]
|
]
|
||||||
|
|||||||
@ -2,9 +2,12 @@ import secrets
|
|||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from fastapi import APIRouter
|
from fastapi import APIRouter
|
||||||
|
from sqlmodel import select
|
||||||
|
|
||||||
from apps.esia.sign import get_url
|
from apps.esia.sign import get_url
|
||||||
|
from apps.users.models import User
|
||||||
from clients import clients as c
|
from clients import clients as c
|
||||||
|
from database import AsyncSessionDep
|
||||||
from shared import exceptions as e
|
from shared import exceptions as e
|
||||||
from shared.redis import client as cache
|
from shared.redis import client as cache
|
||||||
|
|
||||||
@ -26,7 +29,7 @@ async def login():
|
|||||||
|
|
||||||
|
|
||||||
@router.post('/callback')
|
@router.post('/callback')
|
||||||
async def callback(code: str):
|
async def callback(session: AsyncSessionDep, code: str):
|
||||||
token = None
|
token = None
|
||||||
for i in range(3):
|
for i in range(3):
|
||||||
try:
|
try:
|
||||||
@ -42,9 +45,34 @@ async def callback(code: str):
|
|||||||
if token is None:
|
if token is None:
|
||||||
raise e.BadRequestException
|
raise e.BadRequestException
|
||||||
|
|
||||||
await c.esia_api.get_user_info(token.access_token, token.id_token)
|
esia_user = await c.esia_api.get_user_info(
|
||||||
|
token.access_token, token.id_token
|
||||||
|
)
|
||||||
|
|
||||||
|
vita_user = await c.vitacore_api.findBySnils(esia_user.snils)
|
||||||
|
|
||||||
|
if len(vita_user.patients) == 0:
|
||||||
|
raise e.BadRequestException(detail='Patient not found')
|
||||||
|
|
||||||
|
vita_user = vita_user.patients[0]
|
||||||
|
|
||||||
|
existing_user_stmt = (
|
||||||
|
select(User).where(User.vita_id == vita_user.id).limit(1)
|
||||||
|
)
|
||||||
|
existing_user = (
|
||||||
|
await session.execute(existing_user_stmt)
|
||||||
|
).scalar_one_or_none()
|
||||||
|
|
||||||
|
if existing_user is None:
|
||||||
|
user = User(vita_id=vita_user.id)
|
||||||
|
session.add(user)
|
||||||
|
await session.commit()
|
||||||
|
await session.refresh(user)
|
||||||
|
|
||||||
|
else:
|
||||||
|
user = existing_user
|
||||||
|
|
||||||
access_token = secrets.token_urlsafe(32)
|
access_token = secrets.token_urlsafe(32)
|
||||||
cache.set(access_token, access_token)
|
cache.set(access_token, f'user:{user.id}')
|
||||||
|
|
||||||
return s.Token(access_token=access_token)
|
return s.Token(access_token=access_token)
|
||||||
|
|||||||
@ -2,7 +2,10 @@ from typing import Annotated
|
|||||||
|
|
||||||
from fastapi import Depends
|
from fastapi import Depends
|
||||||
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
|
||||||
|
from sqlmodel import select
|
||||||
|
|
||||||
|
from apps.users.models import User
|
||||||
|
from database import AsyncSessionDep
|
||||||
from shared import exceptions as e
|
from shared import exceptions as e
|
||||||
from shared.redis import client as cache
|
from shared.redis import client as cache
|
||||||
|
|
||||||
@ -10,11 +13,20 @@ BEARER = HTTPBearer()
|
|||||||
|
|
||||||
|
|
||||||
async def login(
|
async def login(
|
||||||
|
session: AsyncSessionDep,
|
||||||
credentials: Annotated[HTTPAuthorizationCredentials, Depends(BEARER)],
|
credentials: Annotated[HTTPAuthorizationCredentials, Depends(BEARER)],
|
||||||
):
|
):
|
||||||
is_exist = cache.get(credentials.credentials)
|
user = cache.get(credentials.credentials)
|
||||||
|
|
||||||
if is_exist is None:
|
if user is None:
|
||||||
raise e.UnauthorizedException
|
raise e.UnauthorizedException
|
||||||
|
|
||||||
return is_exist.decode()
|
_, user_id = user.decode().split(':')
|
||||||
|
|
||||||
|
user_model_stmt = select(User).where(User.id == int(user_id))
|
||||||
|
user_model = (await session.execute(user_model_stmt)).scalar_one_or_none()
|
||||||
|
|
||||||
|
if user_model is None:
|
||||||
|
raise e.UnauthorizedException
|
||||||
|
|
||||||
|
return user_model
|
||||||
|
|||||||
8
src/apps/users/models.py
Normal file
8
src/apps/users/models.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
from sqlmodel import Field, SQLModel
|
||||||
|
|
||||||
|
|
||||||
|
class User(SQLModel, table=True):
|
||||||
|
__tablename__: str = 'users' # type: ignore
|
||||||
|
|
||||||
|
id: int = Field(default=None, primary_key=True)
|
||||||
|
vita_id: str = Field(unique=True)
|
||||||
@ -6,6 +6,7 @@ from typing import Annotated
|
|||||||
from fastapi import APIRouter, Body, Depends, status
|
from fastapi import APIRouter, Body, Depends, status
|
||||||
|
|
||||||
from apps.users.auth import login
|
from apps.users.auth import login
|
||||||
|
from apps.users.models import User
|
||||||
from clients import clients as c
|
from clients import clients as c
|
||||||
from clients.vitacore import schema as s
|
from clients.vitacore import schema as s
|
||||||
from shared.redis import client as cache
|
from shared.redis import client as cache
|
||||||
@ -20,16 +21,17 @@ router = APIRouter(
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/getProfile', response_model=s.ProfileModel)
|
@router.get('/getProfile', response_model=s.ProfileModel)
|
||||||
async def get_profile():
|
async def get_profile(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get profile of user by id.
|
Get profile of user.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getProfile(
|
return await c.vitacore_api.getProfile(user.vita_id)
|
||||||
'b62e9f22-a871-4c52-96d6-559c707a716d'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getDepartments', response_model=list[s.DepartmentModel])
|
@router.get(
|
||||||
|
'/getDepartments',
|
||||||
|
response_model=list[s.OrganizationsModel]
|
||||||
|
)
|
||||||
async def get_departments():
|
async def get_departments():
|
||||||
"""
|
"""
|
||||||
Get list of departments.
|
Get list of departments.
|
||||||
@ -38,7 +40,9 @@ async def get_departments():
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/getWorkers', response_model=s.WorkersModel)
|
@router.get('/getWorkers', response_model=s.WorkersModel)
|
||||||
async def get_workers(departmentId: str):
|
async def get_workers(
|
||||||
|
user: Annotated[User, Depends(login)], departmentId: str
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get list of workers by department id.
|
Get list of workers by department id.
|
||||||
"""
|
"""
|
||||||
@ -46,7 +50,7 @@ async def get_workers(departmentId: str):
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/getSpecs', response_model=s.SpecsV021Model)
|
@router.get('/getSpecs', response_model=s.SpecsV021Model)
|
||||||
async def get_specs():
|
async def get_specs(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of specialties.
|
Get list of specialties.
|
||||||
"""
|
"""
|
||||||
@ -54,27 +58,23 @@ async def get_specs():
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/getEntries', response_model=s.EntriesModel)
|
@router.get('/getEntries', response_model=s.EntriesModel)
|
||||||
async def get_entries():
|
async def get_entries(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of entries for user by id.
|
Get list of entries for user by id.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getEntries(
|
return await c.vitacore_api.getEntries(user.vita_id)
|
||||||
'6c7978f0-c573-4ccf-8c6e-f0cd9aceb1e1'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getVaccsReport')
|
@router.get('/getVaccsReport')
|
||||||
async def get_vaccs_report():
|
async def get_vaccs_report(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get report of vaccinations for user by id.
|
Get report of vaccinations for user by id.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getVaccsReport(
|
return await c.vitacore_api.getVaccsReport(user.vita_id)
|
||||||
'6fe66cae-409a-4f56-8ae9-d55d3c38569b'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getMedExamDict')
|
@router.get('/getMedExamDict')
|
||||||
async def get_med_exam_dict():
|
async def get_med_exam_dict(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get medical examination dictionary.
|
Get medical examination dictionary.
|
||||||
"""
|
"""
|
||||||
@ -82,92 +82,80 @@ async def get_med_exam_dict():
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/getRoutesList')
|
@router.get('/getRoutesList')
|
||||||
async def get_routes_list():
|
async def get_routes_list(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of routes.
|
Get list of routes.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getRoutesList(
|
return await c.vitacore_api.getRoutesList(user.vita_id)
|
||||||
'4e6de5f7-4dc9-451b-bf0d-7a64a9b8c279'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getHospExaminations')
|
@router.get('/getHospExaminations')
|
||||||
async def get_hosp_examinations():
|
async def get_hosp_examinations(
|
||||||
|
user: Annotated[User, Depends(login)], examId: str
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Get list of hospital examinations.
|
Get list of hospital examinations.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getHospExaminations(
|
return await c.vitacore_api.getHospExaminations(
|
||||||
'7bbdac30-9a33-4f13-9458-2c229c0c20f5',
|
user.vita_id,
|
||||||
'f22be2c9-8e68-42d6-851e-fbf4a5e8f657',
|
examId,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getCurrHosp')
|
@router.get('/getCurrHosp')
|
||||||
async def get_curr_hosp():
|
async def get_curr_hosp(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get current hospitalization.
|
Get current hospitalization.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getCurrHosp(
|
return await c.vitacore_api.getCurrHosp(user.vita_id)
|
||||||
'b708e782-4f83-4f3b-8639-512c0c9637bf'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getHosps')
|
@router.get('/getHosps')
|
||||||
async def get_hosps():
|
async def get_hosps(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of hospitals.
|
Get list of hospitals.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getHosps(
|
return await c.vitacore_api.getHosps(user.vita_id)
|
||||||
'b708e782-4f83-4f3b-8639-512c0c9637bf'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getHospRecommendations')
|
@router.get('/getHospRecommendations')
|
||||||
async def get_hosp_recommendations():
|
async def get_hosp_recommendations(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of recommended hospitals.
|
Get list of recommended hospitals.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getHospRecommendations(
|
return await c.vitacore_api.getHospRecommendations(user.vita_id)
|
||||||
'b708e782-4f83-4f3b-8639-512c0c9637bf'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getHospRoutes')
|
@router.get('/getHospRoutes')
|
||||||
async def get_hosp_routes():
|
async def get_hosp_routes(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of recommended hospitals.
|
Get list of recommended hospitals.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getHospRoutes(
|
return await c.vitacore_api.getHospRoutes(user.vita_id)
|
||||||
'3092e1c5-e08b-4654-a027-82be90fe8a49'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getDiagnosticResults')
|
@router.get('/getDiagnosticResults')
|
||||||
async def get_diagnostic_results():
|
async def get_diagnostic_results(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of diagnostic results.
|
Get list of diagnostic results.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getDiagnosticResults(
|
return await c.vitacore_api.getDiagnosticResults(user.vita_id)
|
||||||
'4867cc79-9805-4ae2-98d3-2f822848635e'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getELNs')
|
@router.get('/getELNs')
|
||||||
async def get_eln():
|
async def get_eln(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of ELNs.
|
Get list of ELNs.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getELNs('d4493f1c-fcbb-4242-99e6-32328bed53b9')
|
return await c.vitacore_api.getELNs(user.vita_id)
|
||||||
|
|
||||||
|
|
||||||
@router.get('/getPatFLG')
|
@router.get('/getPatFLG')
|
||||||
async def get_pat_flg():
|
async def get_pat_flg(user: Annotated[User, Depends(login)]):
|
||||||
"""
|
"""
|
||||||
Get list of ELNs.
|
Get list of ELNs.
|
||||||
"""
|
"""
|
||||||
return await c.vitacore_api.getPatFLG(
|
return await c.vitacore_api.getPatFLG(user.vita_id)
|
||||||
'0bf2e271-e565-42a8-924e-0017bcdedecd'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# @router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
# @router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
||||||
@ -191,7 +179,7 @@ async def get_pat_flg():
|
|||||||
|
|
||||||
@router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
@router.post('/measurement', status_code=status.HTTP_202_ACCEPTED)
|
||||||
async def measurement(
|
async def measurement(
|
||||||
user: Annotated[str, Depends(login)],
|
user: Annotated[User, Depends(login)],
|
||||||
ad: Annotated[int, Body()],
|
ad: Annotated[int, Body()],
|
||||||
sd: Annotated[int, Body()],
|
sd: Annotated[int, Body()],
|
||||||
pulse: Annotated[int, Body()],
|
pulse: Annotated[int, Body()],
|
||||||
@ -208,7 +196,7 @@ async def measurement(
|
|||||||
'comment': comment,
|
'comment': comment,
|
||||||
'status': status,
|
'status': status,
|
||||||
}
|
}
|
||||||
cache_key = f'tdn:measurement:{user}:{created}'
|
cache_key = f'tdn:measurement:{user.id}:{created}'
|
||||||
cache.set(cache_key, dumps(data))
|
cache.set(cache_key, dumps(data))
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -221,28 +209,35 @@ async def measurements(
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
@router.get('/test')
|
||||||
|
async def test_route():
|
||||||
|
return await c.aemd_api.searchRegistryItem('16247900267')
|
||||||
|
|
||||||
|
|
||||||
@router.get('/queue')
|
@router.get('/queue')
|
||||||
async def queue(user: Annotated[bool, Depends(login)]):
|
async def queue(user: Annotated[bool, Depends(login)]):
|
||||||
return {
|
return [
|
||||||
'id': 60,
|
{
|
||||||
'guid': '92b3343d-1cb2-47b2-8497-a37e38b6ba24',
|
'id': 60,
|
||||||
'tmk_date': None,
|
'guid': '92b3343d-1cb2-47b2-8497-a37e38b6ba24',
|
||||||
'created_at': '2025-04-02 15:21:19.890343',
|
'tmk_date': None,
|
||||||
'code_mo': '166502',
|
'created_at': '2025-04-02 15:21:19.890343',
|
||||||
'mo_name': 'ГАУЗ "ГКБ№7 ИМ. М.Н.САДЫКОВА"',
|
'code_mo': '166502',
|
||||||
'doctor_spec': '109',
|
'mo_name': 'ГАУЗ "ГКБ№7 ИМ. М.Н.САДЫКОВА"',
|
||||||
'doctor_snils': None,
|
'doctor_spec': '109',
|
||||||
'patient_name': 'Иванов Петр Федорович',
|
'doctor_snils': None,
|
||||||
'patient_birthday': '1997-03-01',
|
'patient_name': 'Иванов Петр Федорович',
|
||||||
'patient_snils': '099-678-666 12',
|
'patient_birthday': '1997-03-01',
|
||||||
'patient_policy': None,
|
'patient_snils': '099-678-666 12',
|
||||||
'patient_phone': '+79123456789',
|
'patient_policy': None,
|
||||||
'patient_email': None,
|
'patient_phone': '+79123456789',
|
||||||
'tmk_status': 1,
|
'patient_email': None,
|
||||||
'tmk_status_name': 'Создана',
|
'tmk_status': 1,
|
||||||
'tmk_cancel_reason': None,
|
'tmk_status_name': 'Создана',
|
||||||
'tmk_cancel_reason_name': None,
|
'tmk_cancel_reason': None,
|
||||||
'vks_doctor_link': None,
|
'tmk_cancel_reason_name': None,
|
||||||
'vks_patient_link': None,
|
'vks_doctor_link': None,
|
||||||
'doctor_spec_name': 'врач-терапевт',
|
'vks_patient_link': None,
|
||||||
}
|
'doctor_spec_name': 'врач-терапевт',
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
from .aemd.api import AEMD_API
|
||||||
from .esia.api import ESIA_API
|
from .esia.api import ESIA_API
|
||||||
from .tdn.api import TDN_API
|
from .tdn.api import TDN_API
|
||||||
from .vitacore.api import VITACORE_API
|
from .vitacore.api import VITACORE_API
|
||||||
@ -7,6 +8,7 @@ class ClientsObject:
|
|||||||
_esia_api = None
|
_esia_api = None
|
||||||
_vitacore_api = None
|
_vitacore_api = None
|
||||||
_tdn_api = None
|
_tdn_api = None
|
||||||
|
_aemd_api = None
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def esia_api(self):
|
def esia_api(self):
|
||||||
@ -29,5 +31,12 @@ class ClientsObject:
|
|||||||
|
|
||||||
return self._tdn_api
|
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()
|
clients = ClientsObject()
|
||||||
|
|||||||
0
src/clients/aemd/__init__.py
Normal file
0
src/clients/aemd/__init__.py
Normal file
46
src/clients/aemd/api.py
Normal file
46
src/clients/aemd/api.py
Normal 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
|
||||||
@ -33,7 +33,7 @@ class IDTokenModel(BaseModel):
|
|||||||
exp: int
|
exp: int
|
||||||
iat: int
|
iat: int
|
||||||
iss: str
|
iss: str
|
||||||
acr: IDTokenACRModel
|
# acr: IDTokenACRModel
|
||||||
urn_esia_amd: str = Field(alias='urn:esia:amd')
|
urn_esia_amd: str = Field(alias='urn:esia:amd')
|
||||||
urn_esia_sid: str = Field(alias='urn:esia:sid')
|
urn_esia_sid: str = Field(alias='urn:esia:sid')
|
||||||
urn_esia_sbj: IDTokenSBJModel = Field(alias='urn:esia:sbj')
|
urn_esia_sbj: IDTokenSBJModel = Field(alias='urn:esia:sbj')
|
||||||
@ -44,10 +44,10 @@ class UserInfoModel(BaseModel):
|
|||||||
firstName: str
|
firstName: str
|
||||||
lastName: str
|
lastName: str
|
||||||
middleName: str
|
middleName: str
|
||||||
birthDate: str
|
# birthDate: str
|
||||||
gender: str
|
# gender: str
|
||||||
trusted: bool
|
trusted: bool
|
||||||
citizenship: str
|
# citizenship: str
|
||||||
snils: str
|
snils: str
|
||||||
inn: int
|
inn: int
|
||||||
updatedOn: int
|
updatedOn: int
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
|
from datetime import UTC, datetime
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from fastapi import status as st
|
from fastapi import status as st
|
||||||
from httpx import AsyncClient
|
from httpx import AsyncClient, BasicAuth
|
||||||
|
|
||||||
from core.config import settings
|
from core.config import settings
|
||||||
from shared import exceptions as e
|
from shared import exceptions as e
|
||||||
|
from shared.redis import client as cache
|
||||||
|
|
||||||
from . import schema as s
|
from . import schema as s
|
||||||
|
|
||||||
@ -14,11 +16,56 @@ class VITACORE_API(AsyncClient):
|
|||||||
self.logger = getLogger(__name__)
|
self.logger = getLogger(__name__)
|
||||||
super().__init__(base_url=settings.VITACORE_BASE_URL)
|
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):
|
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):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -28,18 +75,24 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getDepartments(self):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.OrganizationsModel.model_validate(req.json())
|
return s.OrganizationsModel.model_validate(req.json())
|
||||||
case _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.text)
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getWorkers(self, departmentId: str):
|
async def getWorkers(self, departmentId: str):
|
||||||
|
token = await self.get_token()
|
||||||
req = await self.get(
|
req = await self.get(
|
||||||
'/getWorkers', params={'departmentId': departmentId}
|
'/getWorkers',
|
||||||
|
params={'departmentId': departmentId},
|
||||||
|
headers={'Authorization': f'Bearer {token}'},
|
||||||
)
|
)
|
||||||
|
|
||||||
match req.status_code:
|
match req.status_code:
|
||||||
@ -50,7 +103,10 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getSpecsV021(self):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -60,7 +116,13 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getEntries(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -76,17 +138,31 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getVaccsReport(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.VaccsReportModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getMedExamDict(self):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -96,19 +172,39 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getRoutesList(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.RoutesListModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getHospExaminations(self, patId: str, examId: str):
|
async def getHospExaminations(self, patId: str, examId: str):
|
||||||
|
token = await self.get_token()
|
||||||
req = await self.get(
|
req = await self.get(
|
||||||
'/getHospExaminations',
|
'/getHospExaminations',
|
||||||
params={'patId': patId, 'examId': examId},
|
params={'patId': patId, 'examId': examId},
|
||||||
|
headers={'Authorization': f'Bearer {token}'},
|
||||||
)
|
)
|
||||||
|
|
||||||
match req.status_code:
|
match req.status_code:
|
||||||
@ -119,49 +215,123 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getCurrHosp(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.CurHospitalizationsModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getHosps(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.HospitalizationsModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getHospRecommendations(self, patId: str):
|
async def getHospRecommendations(self, patId: str):
|
||||||
|
token = await self.get_token()
|
||||||
|
patId = 'b66a85f1-4aaa-4db8-942a-2de44341824e'
|
||||||
req = await self.get(
|
req = await self.get(
|
||||||
'/getHospRecommendations', params={'patId': patId}
|
'/getHospRecommendations',
|
||||||
|
params={'patId': patId},
|
||||||
|
headers={'Authorization': f'Bearer {token}'},
|
||||||
)
|
)
|
||||||
|
|
||||||
match req.status_code:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.HospRecommendationsModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getHospRoutes(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
return s.HospRoutesModel.model_validate(req.json())
|
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 _:
|
case _:
|
||||||
self.logger.error(req.json())
|
self.logger.error(req.json())
|
||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getDiagnosticResults(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -171,7 +341,12 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getELNs(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
@ -181,7 +356,12 @@ class VITACORE_API(AsyncClient):
|
|||||||
raise e.UnknownException
|
raise e.UnknownException
|
||||||
|
|
||||||
async def getPatFLG(self, patId: str):
|
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:
|
match req.status_code:
|
||||||
case st.HTTP_200_OK:
|
case st.HTTP_200_OK:
|
||||||
|
|||||||
@ -7,6 +7,31 @@ class ErrorModel(BaseModel):
|
|||||||
error: str = Field(title='Текст ошибки')
|
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):
|
class TrustedPersonModel(BaseModel):
|
||||||
parentSnils: str = Field(
|
parentSnils: str = Field(
|
||||||
title='СНИЛС представителя', examples=['156-125-394 57']
|
title='СНИЛС представителя', examples=['156-125-394 57']
|
||||||
@ -66,7 +91,7 @@ class ProfileModel(BaseModel):
|
|||||||
# examples=['99'],
|
# examples=['99'],
|
||||||
# )
|
# )
|
||||||
trustedPersons: list[TrustedPersonModel] = Field(
|
trustedPersons: list[TrustedPersonModel] = Field(
|
||||||
title='Информация о представителе',
|
title='Информация о представителе', default=[]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -79,10 +104,10 @@ class DepartmentAddressModel(BaseModel):
|
|||||||
title='Адрес строкой',
|
title='Адрес строкой',
|
||||||
examples=['420097, г.Казань, ул.Заслонова, д.5'],
|
examples=['420097, г.Казань, ул.Заслонова, д.5'],
|
||||||
)
|
)
|
||||||
latitude: float | None = Field(
|
latitude: str | None = Field(
|
||||||
title='Широта, при наличии', examples=[55.789], default=None
|
title='Широта, при наличии', examples=[55.789], default=None
|
||||||
)
|
)
|
||||||
longitude: float | None = Field(
|
longitude: str | None = Field(
|
||||||
title='Долгота, при наличии', examples=[37.789], default=None
|
title='Долгота, при наличии', examples=[37.789], default=None
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -92,16 +117,19 @@ class DepartmentModel(BaseModel):
|
|||||||
title='Идентификатор МО/Филиала',
|
title='Идентификатор МО/Филиала',
|
||||||
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
|
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
|
||||||
)
|
)
|
||||||
OID: str = Field(
|
OID: str | None = Field(
|
||||||
title='OID МО / Филиала', examples=['1.2.643.5.1.13.13.12.2.16.1084']
|
title='OID МО / Филиала',
|
||||||
|
examples=['1.2.643.5.1.13.13.12.2.16.1084'],
|
||||||
|
default=None,
|
||||||
)
|
)
|
||||||
parentId: str | None = Field(
|
parentId: str | None = Field(
|
||||||
title='Идентификатор вышестоящего подразделения',
|
title='Идентификатор вышестоящего подразделения',
|
||||||
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
|
examples=['a3677271-3385-4f27-a65d-c3430b7c61c2'],
|
||||||
)
|
)
|
||||||
fullname: str = Field(
|
fullname: str | None = Field(
|
||||||
title='Полное наименование',
|
title='Полное наименование',
|
||||||
examples=['ГБУЗС "Тестовая медицинская организация"'],
|
examples=['ГБУЗС "Тестовая медицинская организация"'],
|
||||||
|
default=None,
|
||||||
)
|
)
|
||||||
shortname: str = Field(
|
shortname: str = Field(
|
||||||
title='Краткое наименование',
|
title='Краткое наименование',
|
||||||
@ -112,14 +140,12 @@ class DepartmentModel(BaseModel):
|
|||||||
', для филиалов: Стационар / Поликлиника / ФАП / Амбулатория)',
|
', для филиалов: Стационар / Поликлиника / ФАП / Амбулатория)',
|
||||||
examples=['Юридическое лицо'],
|
examples=['Юридическое лицо'],
|
||||||
)
|
)
|
||||||
inn: str = Field(title='ИНН', examples=['0000000000'])
|
inn: str | None = Field(title='ИНН', examples=['0000000000'], default=None)
|
||||||
ogrn: str = Field(title='ОГРН', examples=['1149204047816'])
|
ogrn: str | None = Field(
|
||||||
|
title='ОГРН', examples=['1149204047816'], default=None
|
||||||
|
)
|
||||||
kpp: str | None = Field(title='КПП', examples=['0000000000'], default=None)
|
kpp: str | None = Field(title='КПП', examples=['0000000000'], default=None)
|
||||||
address: list[DepartmentAddressModel]
|
address: list[DepartmentAddressModel] | None = None
|
||||||
# code: str = Field(
|
|
||||||
# title='Региональный код или код ТФОМС',
|
|
||||||
# examples=['0000000000'],
|
|
||||||
# )
|
|
||||||
|
|
||||||
|
|
||||||
class OrganizationsModel(BaseModel):
|
class OrganizationsModel(BaseModel):
|
||||||
@ -642,7 +668,7 @@ class PatientFLGModel(BaseModel):
|
|||||||
title='Дата следующего флюорографического осмотра',
|
title='Дата следующего флюорографического осмотра',
|
||||||
examples=['2021-09-24'],
|
examples=['2021-09-24'],
|
||||||
)
|
)
|
||||||
PrgContingent: str = Field(
|
PrgContingent: str | None = Field(
|
||||||
title='Контингент (флюорография)',
|
title='Контингент (флюорография)',
|
||||||
examples=['Неорганизованное население'],
|
examples=['Неорганизованное население'],
|
||||||
)
|
)
|
||||||
|
|||||||
@ -56,6 +56,14 @@ class Settings(BaseSettings):
|
|||||||
VITACORE_BASE_URL: str = Field(
|
VITACORE_BASE_URL: str = Field(
|
||||||
default='https://gist-cws.ezdrav.ru:8899/MP_API'
|
default='https://gist-cws.ezdrav.ru:8899/MP_API'
|
||||||
)
|
)
|
||||||
|
VITACORE_USERNAME: str = Field(default='')
|
||||||
|
VITACORE_PASSWORD: str = Field(default='')
|
||||||
|
|
||||||
|
# AEMD
|
||||||
|
AEMD_BASE_URL: str = Field(
|
||||||
|
default='http://gist-sdw.ezdrav.ru:8708/EMDAService'
|
||||||
|
)
|
||||||
|
AEMD_TOKEN: str = Field(default='')
|
||||||
|
|
||||||
# TDN
|
# TDN
|
||||||
TDN_BASE_URL: str = Field(default='https://tdn.tatar.ru/api')
|
TDN_BASE_URL: str = Field(default='https://tdn.tatar.ru/api')
|
||||||
|
|||||||
@ -3,7 +3,7 @@ from logging import getLogger
|
|||||||
from fastapi import FastAPI
|
from fastapi import FastAPI
|
||||||
from fastapi.responses import ORJSONResponse
|
from fastapi.responses import ORJSONResponse
|
||||||
|
|
||||||
# from database import lifespan
|
from database import lifespan
|
||||||
from middlewares import register_middlewares
|
from middlewares import register_middlewares
|
||||||
|
|
||||||
from .config import settings
|
from .config import settings
|
||||||
@ -19,7 +19,7 @@ app = FastAPI(
|
|||||||
version=str(settings.VERSION),
|
version=str(settings.VERSION),
|
||||||
openapi_url=None,
|
openapi_url=None,
|
||||||
default_response_class=ORJSONResponse,
|
default_response_class=ORJSONResponse,
|
||||||
# lifespan=lifespan,
|
lifespan=lifespan,
|
||||||
docs_url=None,
|
docs_url=None,
|
||||||
redoc_url=None,
|
redoc_url=None,
|
||||||
)
|
)
|
||||||
|
|||||||
@ -4,6 +4,7 @@ from alembic import context
|
|||||||
from sqlalchemy import engine_from_config, pool
|
from sqlalchemy import engine_from_config, pool
|
||||||
from sqlmodel import SQLModel
|
from sqlmodel import SQLModel
|
||||||
|
|
||||||
|
from apps.users.models import * # noqa: F403
|
||||||
from core.log import config as log_config
|
from core.log import config as log_config
|
||||||
from database import db_manager
|
from database import db_manager
|
||||||
|
|
||||||
|
|||||||
@ -1,28 +1,32 @@
|
|||||||
"""${message}
|
"""
|
||||||
|
${message}
|
||||||
|
|
||||||
Revision ID: ${up_revision}
|
Revision ID: ${up_revision}
|
||||||
Revises: ${down_revision | comma,n}
|
Revises: ${down_revision | comma,n}
|
||||||
Create Date: ${create_date}
|
Create Date: ${create_date}
|
||||||
|
|
||||||
"""
|
"""
|
||||||
from typing import Sequence, Union
|
from collections.abc import Sequence
|
||||||
|
|
||||||
from alembic import op
|
|
||||||
import sqlalchemy as sa
|
import sqlalchemy as sa
|
||||||
${imports if imports else ""}
|
import sqlmodel.sql.sqltypes
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
# revision identifiers, used by Alembic.
|
# revision identifiers, used by Alembic.
|
||||||
revision: str = ${repr(up_revision)}
|
revision: str = ${repr(up_revision)}
|
||||||
down_revision: Union[str, None] = ${repr(down_revision)}
|
down_revision: str | None = ${repr(down_revision)}
|
||||||
branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)}
|
branch_labels: str | Sequence[str] | None = ${repr(branch_labels)}
|
||||||
depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)}
|
depends_on: str | Sequence[str] | None = ${repr(depends_on)}
|
||||||
|
|
||||||
|
|
||||||
|
if not sqlmodel.sql:
|
||||||
|
msg = 'Something went wrong'
|
||||||
|
raise Exception(msg)
|
||||||
|
|
||||||
|
|
||||||
def upgrade() -> None:
|
def upgrade() -> None:
|
||||||
"""Upgrade schema."""
|
|
||||||
${upgrades if upgrades else "pass"}
|
${upgrades if upgrades else "pass"}
|
||||||
|
|
||||||
|
|
||||||
def downgrade() -> None:
|
def downgrade() -> None:
|
||||||
"""Downgrade schema."""
|
|
||||||
${downgrades if downgrades else "pass"}
|
${downgrades if downgrades else "pass"}
|
||||||
|
|||||||
46
src/migrations/versions/2025.10.09_17-42-25_a74bcd05c7b8.py
Normal file
46
src/migrations/versions/2025.10.09_17-42-25_a74bcd05c7b8.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
"""
|
||||||
|
empty message
|
||||||
|
|
||||||
|
Revision ID: a74bcd05c7b8
|
||||||
|
Revises:
|
||||||
|
Create Date: 2025-10-09 17:42:25.347412
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections.abc import Sequence
|
||||||
|
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlmodel.sql.sqltypes
|
||||||
|
from alembic import op
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = 'a74bcd05c7b8'
|
||||||
|
down_revision: str | None = None
|
||||||
|
branch_labels: str | Sequence[str] | None = None
|
||||||
|
depends_on: str | Sequence[str] | None = None
|
||||||
|
|
||||||
|
|
||||||
|
if not sqlmodel.sql:
|
||||||
|
msg = 'Something went wrong'
|
||||||
|
raise Exception(msg)
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table(
|
||||||
|
'users',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column(
|
||||||
|
'vita_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False
|
||||||
|
),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('id'),
|
||||||
|
sa.UniqueConstraint('vita_id'),
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_table('users')
|
||||||
|
# ### end Alembic commands ###
|
||||||
0
src/migrations/versions/__init__.py
Normal file
0
src/migrations/versions/__init__.py
Normal file
Reference in New Issue
Block a user