566 lines
21 KiB
Python
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": [],
|
|
}
|