Files
medods_to_n3_health/src/app/routers/api.py
T
2026-03-03 22:43:26 +03:00

566 lines
21 KiB
Python

from pathlib import Path
from fastapi import APIRouter, Depends, HTTPException
from api.medods import findPatientByPhone
from api.n3health import resend, revoke, sendForSigning
from db.schemas.documents import Document
from db.schemas.patients import Patient
from db.schemas.practitioners import Practitioner
from db.schemas.settings import Settings
from db.schemas.signings import Signing
from db.schemas.statuses import Statuses
from utils import requestDict, logger, convert_docs_to_pdfs
from utils.attachment import pdfGet
router = APIRouter()
@router.get("/health", summary="Health")
async def health():
return {"status": "healthy"}
@router.post("/pre_singing", summary="pre_singing")
async def pre_singing(
reqData: dict = Depends(requestDict),
):
# signature - type: eSignature / eAttorney
logger.info(f"📥 Получен запрос на /pre_singing")
practitioner = await Practitioner.getPractitionerByIdLpu(
reqData.get("body", {}).get("userId", 0)
)
signature = {}
if practitioner.success:
signature["type"] = (
"eSignature" if practitioner.data.get("esiaAuth", False) else "eAttorney"
)
signature["expiration"] = practitioner.data.get("expired_at")[:10]
daysRemainingControl = 0
deliveryType = None
settings = await Settings.getSettings()
if settings.success:
daysRemainingControl = settings.data.get("expirationAlert", 0)
deliveryData = settings.data.get("deliveryType", {})
deliveryType = [k for k, v in deliveryData.items() if v]
multipleSending = settings.data.get("multipleSending", False)
inProgress = []
complete = []
idPatientMis = reqData.get("body", {}).get("patientId", 0)
if type(idPatientMis) != str:
idPatientMis = str(idPatientMis)
patient = await Patient.getPatientByIdPatientMis(idPatientMis, False, True)
if patient.success:
docNumbers = reqData.get("body", {}).get("docNumbers", [])
for docNumber in docNumbers:
if type(docNumber) != int:
docNumber = int(docNumber)
docs = await Document.getDocumentsByIdPatientMisAndNumber(
idPatientMis, docNumber, True
)
if docs.success:
for doc in docs.data:
docSingingId = doc.singingId
docSinging = await Signing.getSigningById(docSingingId)
if docSinging.success:
if docSinging.data.get("storagePath"):
complete.append(docNumber)
else:
if docSinging.data.get("trackingId"):
inProgress.append(docNumber)
else:
logger.info(f"{docNumber} - {docs.message}")
exitData = {
"signature": signature,
"inProgress": inProgress,
"complete": complete,
"daysRemainingControl": daysRemainingControl,
"deliveryType": deliveryType,
"multipleSending": multipleSending,
}
return exitData
@router.post("/singing", summary="singing")
async def singing(
reqData: dict = Depends(requestDict),
):
def correctData(data):
data["practitioner"]["userIdLpu"] = str(data["practitioner"]["userIdLpu"])
data["patient"]["idPatientMis"] = str(data["patient"]["idPatientMis"])
for recipient in data.get("recipients", []):
recipient["idPatientMis"] = str(recipient["idPatientMis"])
return data
logger.info(f"📥 Получен запрос на /singing")
# Получаем данные из запроса
body = reqData.get("body", {})
docs = body.pop("docs", [])
# Проверяем наличие документов
if not docs:
logger.warning("Нет документов для обработки")
return {"status": "ERROR", "message": "Нет документов для обработки"}
practitioner = await Practitioner.getPractitionerByIdLpu(
body.get("practitioner", {}).get("userIdLpu", 0)
)
if not practitioner.success:
logger.error(
f"Сотрудник не найден, idLpu: {body.get('practitioner', {}).get('userIdLpu', 0)}"
)
return {"status": "ERROR", "message": "Сотрудник не найден"}
esiaAuth = practitioner.data.get("esiaAuth", False)
userIdLpu = practitioner.data.get("userIdLpu", 0)
body["practitioner"]["snils"] = practitioner.data.get("snils")
if not esiaAuth:
body["practitioner"]["mrProxyNumber"] = practitioner.data.get("attorney")
patient = body.get("patient", None)
if not patient:
logger.error("Данные пациента не получены, пациент не найден")
return {
"status": "ERROR",
"message": "Данные пациента не получены, пациент не найден",
}
idPatientMis = patient.get("idPatientMis", None)
if not idPatientMis:
logger.error("Не указан идентификатор пациента")
return {"status": "ERROR", "message": "Не указан идентификатор пациента"}
patientDB = await Patient.getPatientByIdPatientMis(idPatientMis, isCheck=True)
if not patientDB.success:
patientData = {
"idPatientMis": idPatientMis,
"familyName": patient.get("familyName", "N/A"),
"givenName": patient.get("givenName", "N/A"),
"middleName": patient.get("middleName", "N/A"),
"birthDate": patient.get("birthDate", "N/A"),
"sex": patient.get("sex", "N/A"),
}
newPatient = await Patient.addPatient(**patientData)
if not newPatient.success:
return {
"status": "ERROR",
"message": "Не удалось создать пациента",
}
patientData = newPatient.data.toDict()
else:
patientData = patientDB.data
recipients = body.pop("recipients", [])
recipientsData = []
recipientsIds = []
for recipient in recipients:
idRecipientMis = recipient.get("idPatientMis", None)
if not idRecipientMis:
logger.error("Не указан идентификатор пациента")
return {
"status": "ERROR",
"message": "Не указан идентификатор пациента",
}
recipientDB = await Patient.getPatientByIdPatientMis(
idRecipientMis, isCheck=True
)
if not recipientDB.success:
recipientData = {
"idPatientMis": idRecipientMis,
"familyName": recipient.get("surname", "N/A"),
"givenName": recipient.get("name", "N/A"),
"middleName": recipient.get("secondName", "N/A"),
"birthDate": recipient.get("birthDate", "N/A"),
"sex": recipient.get("sex", "N/A"),
}
newRecipient = await Patient.addPatient(**recipientData)
if not newRecipient.success:
return {
"status": "ERROR",
"message": newRecipient.message,
}
recipientsData.append(newRecipient.data.toDict())
recipientsIds.append(newRecipient.data.idPatientMis)
else:
logger.info(recipientDB.data)
recipientsData.append(recipientDB.data)
recipientsIds.append(recipientDB.data.get("idPatientMis"))
recipient["documentDto"] = [
{
"docN": recipient.pop("snils"),
"docS": "",
"documentName": "СНИЛС",
"idDocumentType": 223,
"providerName": "ПФР",
}
]
recipient["telecom"] = [
{
"system": "Telephone",
"value": f'+{recipient.pop("phone")}',
}
]
body["recipients"] = recipients
# Логируем информацию о документах
logger.info(f"👨‍⚕️ Медработник: {practitioner.data.get("name")}")
logger.info(f"👤 Пациент: {patientData.get('name')}")
logger.info(
f"👥 Получатели ({len(recipientsData)}): {', '.join([f'{p.get('name')}' for p in recipientsData])}"
)
logger.info(f"📄 Документов для обработки: {len(docs)}")
logger.info(f"🔐 ЕСИА: {esiaAuth}")
try:
# Конвертируем документы в PDF
conversion_result = convert_docs_to_pdfs(docs, idPatientMis)
if conversion_result.get("status", "") != "SUCCESS":
return {
"status": "ERROR",
"message": "Не удалось сохранить документы",
}
newSinging = await Signing.addSigning(
userIdLpu, idPatientMis, body["deliveryType"], recipientsIds
)
if not newSinging:
return {
"status": "ERROR",
"message": "Не удалось добавить подписание",
}
logger.info(f"🔐 Подписание добавлено: {newSinging.id}")
pdfFiles = conversion_result.get("files", [])
body["medDocument"] = {
"esiaAuth": esiaAuth,
"attachments": [],
}
body["files"] = []
for doc in pdfFiles:
for idPatient in [idPatientMis, *recipientsIds]:
newDocument = await Document.addDocument(
singingId=newSinging.id, idPatientMis=str(idPatient), **doc
)
if not newDocument:
logger.error(
f"Не удалось добавить документ для получателя {idPatient}"
)
return {
"status": "ERROR",
"message": "Не удалось добавить документ",
}
logger.debug(
f"📄 Документ добавлен: {newDocument.id} для получателя {idPatient}"
)
partName = f'file{len(body["medDocument"]["attachments"])}'
body["medDocument"]["attachments"].append(
{
"partName": partName,
}
)
body["files"].append(
{
"field": partName,
"path": Path(doc["storagePath"]),
"content_type": "application/pdf",
}
)
if esiaAuth:
partName = f'file{len(body["medDocument"]["attachments"])}'
body["medDocument"]["attachments"].append(
{
"partName": partName,
}
)
body["files"].append(
{
"field": partName,
"path": Path(
f"src/attachments/secure/{userIdLpu}/signature.p7s"
).resolve(),
"content_type": "application/pkcs7-signature",
}
)
signingResult = await sendForSigning(correctData(body))
if not signingResult or signingResult.get("status", "") != "created":
return {
"status": "ERROR",
"message": "Не удалось отправить документы на подписание",
}
newSinging.trackingId = signingResult.get("trackingId", "")
await newSinging.save()
logger.debug(
f"🔐 Подписание обновлено: {newSinging.id}. Добавлен Трек-номер подписания"
)
await Statuses.updateStatus(newSinging.id, True)
logger.info(f"✅ Обработка завершена!")
response = {
"status": "SUCCESS",
"message": "Документы успешно обработаны.",
}
return response
except Exception as e:
error_msg = f"Ошибка при обработке документов: {str(e)}"
logger.error(error_msg)
raise HTTPException(status_code=500, detail=error_msg)
@router.post("/statuses", summary="statuses")
async def statuses(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /statuses")
# Получаем данные из запроса
body = reqData.get("body", {})
userIdLpu = str(body.get("userIdLpu"))
filters = body.get("filters", {})
if not userIdLpu or not filters:
logger.error("Не отправлены userIdLpu или filters")
return {"status": "ERROR", "message": "Не отправлены необходимые данные"}
result = await Signing.getFilteredSingings(userIdLpu, filters)
singingsData = result.data
for singingData in singingsData:
documents = singingData.pop("documents", [])
uniqueDocs = []
uniquePaths = []
for doc in documents:
if doc.get("storagePath") not in uniquePaths:
uniquePaths.append(doc.get("storagePath"))
uniqueDocs.append(doc)
singingData["documents"] = uniqueDocs
return singingsData
@router.post("/documents", summary="documents")
async def documents(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /documents")
# Получаем данные из запроса
body = reqData.get("body", {})
if "idPatientMis" not in body:
logger.error("Не отправлен idPatientMis")
return {"status": "ERROR", "message": "Не отправлен idPatientMis"}
idPatientMis = str(body.get("idPatientMis"))
singingData = await Signing.getSigningsByIdPatientMis(idPatientMis)
if not singingData.success:
logger.error("Подписания не найдены")
return {"status": "SUCCESS", "data": [], "message": "Подписания не найдены"}
userIdLpus = set([s.get("userIdLpu") for s in singingData.data])
practitioners = {}
for userIdLpu in userIdLpus:
practitioner = await Practitioner.getPractitionerByIdLpu(userIdLpu, False)
practitioners[userIdLpu] = practitioner.data.name
for s in singingData.data:
s["practitioner"] = practitioners[s.get("userIdLpu")]
for singing in singingData.data:
statusesData = [
{d.get("idPatientMis"): d.get("patient", {}).get("name")}
for d in singing.get("statuses", [])
if d.get("idPatientMis")
]
idMatchName = {}
for statusData in statusesData:
for idPatientMisStatus, name in statusData.items():
if idPatientMisStatus in idMatchName:
continue
idMatchName[idPatientMisStatus] = name
documents = singing.pop("documents")
uniqueDocs = []
for doc in documents:
docOwner = doc.get("storagePath").split("/")[-2:-1][0]
isOwn = docOwner == idPatientMis
if str(doc.get("idPatientMis")) == idPatientMis:
if not isOwn:
ownerName = idMatchName[docOwner]
doc["title"] = (
f"{doc.get('title')} (Пациент: {idMatchName[docOwner]})"
)
uniqueDocs.append(doc)
singing["documents"] = uniqueDocs
settings = await Settings.getSettings(False)
data = {"deliveryType": settings.data.deliveryType, "singings": singingData.data}
responseData = {"status": "SUCCESS", "data": data}
return responseData
@router.post("/document", summary="document")
async def document(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /document")
documentPath = reqData.get("body", {}).get("documentPath", None)
if not documentPath:
return {"status": "ERROR", "message": "Документ не найден"}
docBytes = pdfGet(documentPath)
if not docBytes.success:
return {"status": "ERROR", "message": docBytes.message}
return {"status": "SUCCESS", "data": docBytes.data}
@router.post("/revoke", summary="revoke")
async def revoke_post(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /revoke")
trackingId = reqData.get("body", {}).get("trackingId", None)
if not trackingId:
return {"status": "ERROR", "message": "Документ не найден"}
revokeResult = await revoke(trackingId)
if revokeResult.get("status", "") != "success":
return {
"status": "ERROR",
"message": revokeResult.get("errorMessage", "Отозвать не удалось"),
}
import asyncio
await asyncio.sleep(2)
await Signing.updateStatuses()
return {"status": "SUCCESS", "message": "Документ отозван"}
@router.post("/resend", summary="resend")
async def resend_post(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /resend")
trackingId = reqData.get("body", {}).get("trackingId", None)
idPatientMis = reqData.get("body", {}).get("idPatientMis", None)
deliveryType = reqData.get("body", {}).get("deliveryType", None)
errorResponse = {"status": "ERROR", "message": "Повторная отправка не удалось"}
if not trackingId or not idPatientMis or not deliveryType:
logger.error("Не отправлен trackingId или idPatientMis или deliveryType")
logger.error(
f"trackingId: {trackingId}, idPatientMis: {idPatientMis}, deliveryType: {deliveryType}"
)
return errorResponse
resendResult = await resend(trackingId, idPatientMis, deliveryType)
if not resendResult or not resendResult.get("success", False):
logger.error(resendResult)
return errorResponse
logger.info("✅ Повторная отправка успешна")
return {"status": "SUCCESS", "message": resendResult.get("message", "")}
@router.post("/advanced-search", summary="advanced-search")
async def advanced_search_post(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /advanced-search")
filters = reqData.get("body", {}).get("filters", {})
result = await Signing.getFilteredSingings("", filters)
for singingData in result.data:
documents = singingData.pop("documents", [])
uniqueDocs = []
uniquePaths = []
for doc in documents:
if doc.get("storagePath") not in uniquePaths:
uniquePaths.append(doc.get("storagePath"))
uniqueDocs.append(doc)
singingData["documents"] = uniqueDocs
return {"status": "SUCCESS", "data": result.data}
@router.post("/search-recipients", summary="search-recipients")
async def search_recipients_post(
reqData: dict = Depends(requestDict),
):
logger.info(f"📥 Получен запрос на /search-recipients")
phoneNumber = "".join(
filter(str.isdigit, reqData.get("body", {}).get("phone", ""))
)[-10:]
idPatientMis = str(reqData.get("body", {}).get("idPatientMis", ""))
settings = await Settings.getSettings(False)
if not settings.success:
logger.error(settings.message)
return {"status": "error", "message": settings.message}
settings = settings.data
try:
patientsDB = await findPatientByPhone(
settings.medodsApiHost,
settings.medodsApiPort,
settings.medodsApiIdentity,
settings.medodsApiSecretKey,
f"7{phoneNumber}",
)
if not patientsDB.success or patientsDB.data.get("totalItems") == 0:
logger.error("Пациент не найден")
return {"status": "error", "data": []}
patients = patientsDB.data.get("data", [])
result = [
{
"idPatientMis": str(patient.get("id")),
"name": patient.get("name"),
"surname": patient.get("surname"),
"secondName": patient.get("secondName"),
"birthDate": patient.get("birthdate"),
"sex": patient.get("sex"),
"snils": patient.get("snils"),
"phone": patient.get("phone"),
}
for patient in patients
if str(patient.get("id")) != idPatientMis
]
return {
"status": "ok",
"data": result,
}
except Exception as e:
logger.error(e)
return {
"status": "error",
"data": [],
}