Spaces:
Sleeping
Sleeping
Update api/routes.py
Browse files- api/routes.py +42 -34
api/routes.py
CHANGED
|
@@ -22,7 +22,7 @@ import uuid
|
|
| 22 |
import re
|
| 23 |
import subprocess
|
| 24 |
from tempfile import NamedTemporaryFile, TemporaryDirectory
|
| 25 |
-
from string import Template
|
| 26 |
|
| 27 |
# Configure logging
|
| 28 |
logging.basicConfig(
|
|
@@ -569,8 +569,9 @@ async def add_note(
|
|
| 569 |
|
| 570 |
@router.get("/ehr/patients/{patient_id}/pdf", response_class=Response)
|
| 571 |
async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get_current_user)):
|
| 572 |
-
#
|
| 573 |
-
logger.
|
|
|
|
| 574 |
|
| 575 |
try:
|
| 576 |
if current_user.get('role') not in ['doctor', 'admin']:
|
|
@@ -587,42 +588,42 @@ async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get
|
|
| 587 |
if not patient:
|
| 588 |
raise HTTPException(status_code=404, detail="Patient not found")
|
| 589 |
|
| 590 |
-
# Format rows safely using escape_latex_special_chars
|
| 591 |
note_rows = " \\\\\n".join(
|
| 592 |
"{} & {} & {}".format(
|
| 593 |
-
escape_latex_special_chars(n.get("date", "")),
|
| 594 |
-
escape_latex_special_chars(n.get("type", "")),
|
| 595 |
-
escape_latex_special_chars(n.get("text", ""))
|
| 596 |
)
|
| 597 |
for n in patient.get("notes", [])
|
| 598 |
)
|
| 599 |
condition_rows = " \\\\\n".join(
|
| 600 |
"{} & {} & {} & {} & {}".format(
|
| 601 |
-
escape_latex_special_chars(c.get("id", "")),
|
| 602 |
-
escape_latex_special_chars(c.get("code", "")),
|
| 603 |
-
escape_latex_special_chars(c.get("status", "")),
|
| 604 |
-
escape_latex_special_chars(c.get("onset_date", "")),
|
| 605 |
-
escape_latex_special_chars(c.get("verification_status", ""))
|
| 606 |
)
|
| 607 |
for c in patient.get("conditions", [])
|
| 608 |
)
|
| 609 |
medication_rows = " \\\\\n".join(
|
| 610 |
"{} & {} & {} & {} & {}".format(
|
| 611 |
-
escape_latex_special_chars(m.get("id", "")),
|
| 612 |
-
escape_latex_special_chars(m.get("name", "")),
|
| 613 |
-
escape_latex_special_chars(m.get("status", "")),
|
| 614 |
-
escape_latex_special_chars(m.get("prescribed_date", "")),
|
| 615 |
-
escape_latex_special_chars(m.get("dosage", ""))
|
| 616 |
)
|
| 617 |
for m in patient.get("medications", [])
|
| 618 |
)
|
| 619 |
encounter_rows = " \\\\\n".join(
|
| 620 |
"{} & {} & {} & {} & {}".format(
|
| 621 |
-
escape_latex_special_chars(e.get("id", "")),
|
| 622 |
-
escape_latex_special_chars(e.get("type", "")),
|
| 623 |
-
escape_latex_special_chars(e.get("status", "")),
|
| 624 |
-
escape_latex_special_chars(e.get("period", {}).get("start", "")),
|
| 625 |
-
escape_latex_special_chars(e.get("service_provider", ""))
|
| 626 |
)
|
| 627 |
for e in patient.get("encounters", [])
|
| 628 |
)
|
|
@@ -704,15 +705,15 @@ $encounters
|
|
| 704 |
\end{document}
|
| 705 |
""")
|
| 706 |
|
| 707 |
-
# Set the generated_on date to 03:
|
| 708 |
-
generated_on = datetime.strptime("2025-05-16 15:
|
| 709 |
|
| 710 |
latex_filled = latex_template.substitute(
|
| 711 |
generated_on=generated_on,
|
| 712 |
-
fhir_id=escape_latex_special_chars(patient.get("fhir_id", "")),
|
| 713 |
-
full_name=escape_latex_special_chars(patient.get("full_name", "")),
|
| 714 |
-
gender=escape_latex_special_chars(patient.get("gender", "")),
|
| 715 |
-
dob=escape_latex_special_chars(patient.get("date_of_birth", "")),
|
| 716 |
age=escape_latex_special_chars(str(calculate_age(patient.get("date_of_birth", "")) or "N/A")),
|
| 717 |
address=escape_latex_special_chars(", ".join(filter(None, [
|
| 718 |
patient.get("address", ""),
|
|
@@ -721,8 +722,8 @@ $encounters
|
|
| 721 |
patient.get("postal_code", ""),
|
| 722 |
patient.get("country", "")
|
| 723 |
]))),
|
| 724 |
-
marital_status=escape_latex_special_chars(patient.get("marital_status", "")),
|
| 725 |
-
language=escape_latex_special_chars(patient.get("language", "")),
|
| 726 |
notes=note_rows or "No notes available \\\\",
|
| 727 |
conditions=condition_rows or "No conditions available \\\\",
|
| 728 |
medications=medication_rows or "No medications available \\\\",
|
|
@@ -738,20 +739,25 @@ $encounters
|
|
| 738 |
f.write(latex_filled)
|
| 739 |
|
| 740 |
try:
|
| 741 |
-
|
| 742 |
-
|
|
|
|
| 743 |
cwd=tmpdir,
|
| 744 |
check=True,
|
| 745 |
capture_output=True,
|
| 746 |
text=True
|
| 747 |
)
|
|
|
|
|
|
|
| 748 |
except subprocess.CalledProcessError as e:
|
|
|
|
| 749 |
raise HTTPException(
|
| 750 |
status_code=500,
|
| 751 |
-
detail=f"LaTeX compilation failed: {e.stderr}"
|
| 752 |
)
|
| 753 |
|
| 754 |
if not os.path.exists(pdf_path):
|
|
|
|
| 755 |
raise HTTPException(
|
| 756 |
status_code=500,
|
| 757 |
detail="PDF file was not generated"
|
|
@@ -768,15 +774,17 @@ $encounters
|
|
| 768 |
return response
|
| 769 |
|
| 770 |
except HTTPException as http_error:
|
|
|
|
| 771 |
raise http_error
|
| 772 |
except Exception as e:
|
|
|
|
| 773 |
raise HTTPException(
|
| 774 |
status_code=500,
|
| 775 |
detail=f"Unexpected error generating PDF: {str(e)}"
|
| 776 |
)
|
| 777 |
finally:
|
| 778 |
# Restore the logger level for other routes
|
| 779 |
-
logger.setLevel(
|
| 780 |
|
| 781 |
@router.post("/signup", status_code=status.HTTP_201_CREATED)
|
| 782 |
async def signup(data: SignupForm):
|
|
|
|
| 22 |
import re
|
| 23 |
import subprocess
|
| 24 |
from tempfile import NamedTemporaryFile, TemporaryDirectory
|
| 25 |
+
from string import Template
|
| 26 |
|
| 27 |
# Configure logging
|
| 28 |
logging.basicConfig(
|
|
|
|
| 569 |
|
| 570 |
@router.get("/ehr/patients/{patient_id}/pdf", response_class=Response)
|
| 571 |
async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get_current_user)):
|
| 572 |
+
# Temporarily enable logging to debug LaTeX compilation issues
|
| 573 |
+
original_log_level = logger.level
|
| 574 |
+
logger.setLevel(logging.DEBUG)
|
| 575 |
|
| 576 |
try:
|
| 577 |
if current_user.get('role') not in ['doctor', 'admin']:
|
|
|
|
| 588 |
if not patient:
|
| 589 |
raise HTTPException(status_code=404, detail="Patient not found")
|
| 590 |
|
| 591 |
+
# Format rows safely using escape_latex_special_chars with additional safety for None values
|
| 592 |
note_rows = " \\\\\n".join(
|
| 593 |
"{} & {} & {}".format(
|
| 594 |
+
escape_latex_special_chars(n.get("date", "") or ""),
|
| 595 |
+
escape_latex_special_chars(n.get("type", "") or ""),
|
| 596 |
+
escape_latex_special_chars(n.get("text", "") or "")
|
| 597 |
)
|
| 598 |
for n in patient.get("notes", [])
|
| 599 |
)
|
| 600 |
condition_rows = " \\\\\n".join(
|
| 601 |
"{} & {} & {} & {} & {}".format(
|
| 602 |
+
escape_latex_special_chars(c.get("id", "") or ""),
|
| 603 |
+
escape_latex_special_chars(c.get("code", "") or ""),
|
| 604 |
+
escape_latex_special_chars(c.get("status", "") or ""),
|
| 605 |
+
escape_latex_special_chars(c.get("onset_date", "") or ""),
|
| 606 |
+
escape_latex_special_chars(c.get("verification_status", "") or "")
|
| 607 |
)
|
| 608 |
for c in patient.get("conditions", [])
|
| 609 |
)
|
| 610 |
medication_rows = " \\\\\n".join(
|
| 611 |
"{} & {} & {} & {} & {}".format(
|
| 612 |
+
escape_latex_special_chars(m.get("id", "") or ""),
|
| 613 |
+
escape_latex_special_chars(m.get("name", "") or ""),
|
| 614 |
+
escape_latex_special_chars(m.get("status", "") or ""),
|
| 615 |
+
escape_latex_special_chars(m.get("prescribed_date", "") or ""),
|
| 616 |
+
escape_latex_special_chars(m.get("dosage", "") or "")
|
| 617 |
)
|
| 618 |
for m in patient.get("medications", [])
|
| 619 |
)
|
| 620 |
encounter_rows = " \\\\\n".join(
|
| 621 |
"{} & {} & {} & {} & {}".format(
|
| 622 |
+
escape_latex_special_chars(e.get("id", "") or ""),
|
| 623 |
+
escape_latex_special_chars(e.get("type", "") or ""),
|
| 624 |
+
escape_latex_special_chars(e.get("status", "") or ""),
|
| 625 |
+
escape_latex_special_chars(e.get("period", {}).get("start", "") or ""),
|
| 626 |
+
escape_latex_special_chars(e.get("service_provider", "") or "")
|
| 627 |
)
|
| 628 |
for e in patient.get("encounters", [])
|
| 629 |
)
|
|
|
|
| 705 |
\end{document}
|
| 706 |
""")
|
| 707 |
|
| 708 |
+
# Set the generated_on date to 03:46 PM CET, May 16, 2025
|
| 709 |
+
generated_on = datetime.strptime("2025-05-16 15:46:00+01:00", "%Y-%m-%d %H:%M:%S%z").strftime("%A, %B %d, %Y at %I:%M %p")
|
| 710 |
|
| 711 |
latex_filled = latex_template.substitute(
|
| 712 |
generated_on=generated_on,
|
| 713 |
+
fhir_id=escape_latex_special_chars(patient.get("fhir_id", "") or ""),
|
| 714 |
+
full_name=escape_latex_special_chars(patient.get("full_name", "") or ""),
|
| 715 |
+
gender=escape_latex_special_chars(patient.get("gender", "") or ""),
|
| 716 |
+
dob=escape_latex_special_chars(patient.get("date_of_birth", "") or ""),
|
| 717 |
age=escape_latex_special_chars(str(calculate_age(patient.get("date_of_birth", "")) or "N/A")),
|
| 718 |
address=escape_latex_special_chars(", ".join(filter(None, [
|
| 719 |
patient.get("address", ""),
|
|
|
|
| 722 |
patient.get("postal_code", ""),
|
| 723 |
patient.get("country", "")
|
| 724 |
]))),
|
| 725 |
+
marital_status=escape_latex_special_chars(patient.get("marital_status", "") or ""),
|
| 726 |
+
language=escape_latex_special_chars(patient.get("language", "") or ""),
|
| 727 |
notes=note_rows or "No notes available \\\\",
|
| 728 |
conditions=condition_rows or "No conditions available \\\\",
|
| 729 |
medications=medication_rows or "No medications available \\\\",
|
|
|
|
| 739 |
f.write(latex_filled)
|
| 740 |
|
| 741 |
try:
|
| 742 |
+
# Added -f to force compilation for debugging
|
| 743 |
+
result = subprocess.run(
|
| 744 |
+
["latexmk", "-pdf", "-interaction=nonstopmode", "-f", tex_path],
|
| 745 |
cwd=tmpdir,
|
| 746 |
check=True,
|
| 747 |
capture_output=True,
|
| 748 |
text=True
|
| 749 |
)
|
| 750 |
+
logger.debug(f"latexmk stdout: {result.stdout}")
|
| 751 |
+
logger.debug(f"latexmk stderr: {result.stderr}")
|
| 752 |
except subprocess.CalledProcessError as e:
|
| 753 |
+
logger.error(f"LaTeX compilation failed: stdout={e.stdout}, stderr={e.stderr}")
|
| 754 |
raise HTTPException(
|
| 755 |
status_code=500,
|
| 756 |
+
detail=f"LaTeX compilation failed: stdout={e.stdout}, stderr={e.stderr}"
|
| 757 |
)
|
| 758 |
|
| 759 |
if not os.path.exists(pdf_path):
|
| 760 |
+
logger.error("PDF file was not generated")
|
| 761 |
raise HTTPException(
|
| 762 |
status_code=500,
|
| 763 |
detail="PDF file was not generated"
|
|
|
|
| 774 |
return response
|
| 775 |
|
| 776 |
except HTTPException as http_error:
|
| 777 |
+
logger.error(f"HTTP exception in generate_patient_pdf: {str(http_error.detail)}")
|
| 778 |
raise http_error
|
| 779 |
except Exception as e:
|
| 780 |
+
logger.error(f"Unexpected error generating PDF: {str(e)}", exc_info=True)
|
| 781 |
raise HTTPException(
|
| 782 |
status_code=500,
|
| 783 |
detail=f"Unexpected error generating PDF: {str(e)}"
|
| 784 |
)
|
| 785 |
finally:
|
| 786 |
# Restore the logger level for other routes
|
| 787 |
+
logger.setLevel(original_log_level)
|
| 788 |
|
| 789 |
@router.post("/signup", status_code=status.HTTP_201_CREATED)
|
| 790 |
async def signup(data: SignupForm):
|