Spaces:
Sleeping
Sleeping
Update api/routes.py
Browse files- api/routes.py +34 -39
api/routes.py
CHANGED
|
@@ -96,18 +96,7 @@ async def create_indexes():
|
|
| 96 |
logger.error(f"Failed to create indexes: {str(e)}")
|
| 97 |
raise
|
| 98 |
|
| 99 |
-
|
| 100 |
-
def calculate_age(birth_date: str) -> Optional[int]:
|
| 101 |
-
if not birth_date:
|
| 102 |
-
return None
|
| 103 |
-
try:
|
| 104 |
-
birth_date = datetime.strptime(birth_date.split('T')[0], "%Y-%m-%d")
|
| 105 |
-
today = datetime.now()
|
| 106 |
-
return today.year - birth_date.year - (
|
| 107 |
-
(today.month, today.day) < (birth_date.month, birth_date.day))
|
| 108 |
-
except ValueError as e:
|
| 109 |
-
logger.warning(f"Invalid birth date format: {birth_date}, error: {str(e)}")
|
| 110 |
-
return None
|
| 111 |
|
| 112 |
def standardize_language(language: str) -> str:
|
| 113 |
"""Convert language to MongoDB-compatible language code."""
|
|
@@ -568,6 +557,14 @@ async def add_note(
|
|
| 568 |
)
|
| 569 |
|
| 570 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 571 |
@router.get("/ehr/patients/{patient_id}/pdf", response_class=Response)
|
| 572 |
async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get_current_user)):
|
| 573 |
logger.info(f"Generating PDF for patient: {patient_id} by user {current_user.get('email')}")
|
|
@@ -586,7 +583,13 @@ async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get
|
|
| 586 |
if not patient:
|
| 587 |
raise HTTPException(status_code=404, detail="Patient not found")
|
| 588 |
|
| 589 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 590 |
latex_template = Template(r"""
|
| 591 |
\documentclass[a4paper,12pt]{article}
|
| 592 |
\usepackage[utf8]{inputenc}
|
|
@@ -605,7 +608,7 @@ async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get
|
|
| 605 |
\begin{center}
|
| 606 |
\Large\textbf{Patient Medical Report} \\
|
| 607 |
\vspace{0.2cm}
|
| 608 |
-
\textit{Generated on $
|
| 609 |
\end{center}
|
| 610 |
|
| 611 |
\section*{Demographics}
|
|
@@ -633,15 +636,8 @@ $notes
|
|
| 633 |
\end{document}
|
| 634 |
""")
|
| 635 |
|
| 636 |
-
|
| 637 |
-
|
| 638 |
-
f"{n.get('date', '')} & {n.get('type', '')} & {n.get('text', '').replace('&', '\\&')}"
|
| 639 |
-
for n in patient.get("notes", [])
|
| 640 |
-
)
|
| 641 |
-
|
| 642 |
-
# Fill in template
|
| 643 |
-
latex = latex_template.substitute(
|
| 644 |
-
date=datetime.now().strftime('%A, %B %d, %Y at %I:%M %p'),
|
| 645 |
fhir_id=patient.get("fhir_id", ""),
|
| 646 |
full_name=patient.get("full_name", ""),
|
| 647 |
gender=patient.get("gender", ""),
|
|
@@ -656,35 +652,34 @@ $notes
|
|
| 656 |
])),
|
| 657 |
marital_status=patient.get("marital_status", ""),
|
| 658 |
language=patient.get("language", ""),
|
| 659 |
-
notes=
|
| 660 |
)
|
| 661 |
|
| 662 |
-
#
|
| 663 |
with tempfile.TemporaryDirectory() as tmpdir:
|
| 664 |
tex_path = os.path.join(tmpdir, "report.tex")
|
| 665 |
pdf_path = os.path.join(tmpdir, "report.pdf")
|
| 666 |
|
| 667 |
with open(tex_path, "w", encoding="utf-8") as f:
|
| 668 |
-
f.write(
|
| 669 |
-
|
| 670 |
-
try:
|
| 671 |
-
subprocess.run(["latexmk", "-pdf", "-interaction=nonstopmode", tex_path],
|
| 672 |
-
check=True, cwd=tmpdir)
|
| 673 |
|
| 674 |
-
|
| 675 |
-
|
| 676 |
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
return response
|
| 680 |
|
| 681 |
-
|
| 682 |
-
|
| 683 |
-
|
| 684 |
|
|
|
|
|
|
|
|
|
|
| 685 |
except Exception as e:
|
| 686 |
logger.exception("Unexpected error generating PDF")
|
| 687 |
-
raise HTTPException(status_code=500, detail="
|
|
|
|
| 688 |
@router.post("/signup", status_code=status.HTTP_201_CREATED)
|
| 689 |
async def signup(data: SignupForm):
|
| 690 |
logger.info(f"Signup attempt for email: {data.email}")
|
|
|
|
| 96 |
logger.error(f"Failed to create indexes: {str(e)}")
|
| 97 |
raise
|
| 98 |
|
| 99 |
+
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 100 |
|
| 101 |
def standardize_language(language: str) -> str:
|
| 102 |
"""Convert language to MongoDB-compatible language code."""
|
|
|
|
| 557 |
)
|
| 558 |
|
| 559 |
|
| 560 |
+
def calculate_age(birth_date: str):
|
| 561 |
+
try:
|
| 562 |
+
date = datetime.strptime(birth_date.split("T")[0], "%Y-%m-%d")
|
| 563 |
+
today = datetime.now()
|
| 564 |
+
return today.year - date.year - ((today.month, today.day) < (date.month, date.day))
|
| 565 |
+
except:
|
| 566 |
+
return "N/A"
|
| 567 |
+
|
| 568 |
@router.get("/ehr/patients/{patient_id}/pdf", response_class=Response)
|
| 569 |
async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get_current_user)):
|
| 570 |
logger.info(f"Generating PDF for patient: {patient_id} by user {current_user.get('email')}")
|
|
|
|
| 583 |
if not patient:
|
| 584 |
raise HTTPException(status_code=404, detail="Patient not found")
|
| 585 |
|
| 586 |
+
# Format note rows safely
|
| 587 |
+
note_rows = " \\\\\n".join(
|
| 588 |
+
f"{n.get('date','')} & {n.get('type','')} & {n.get('text','').replace('&', '\\&')}"
|
| 589 |
+
for n in patient.get("notes", [])
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
# Use Template for safe insertion
|
| 593 |
latex_template = Template(r"""
|
| 594 |
\documentclass[a4paper,12pt]{article}
|
| 595 |
\usepackage[utf8]{inputenc}
|
|
|
|
| 608 |
\begin{center}
|
| 609 |
\Large\textbf{Patient Medical Report} \\
|
| 610 |
\vspace{0.2cm}
|
| 611 |
+
\textit{Generated on $generated_on}
|
| 612 |
\end{center}
|
| 613 |
|
| 614 |
\section*{Demographics}
|
|
|
|
| 636 |
\end{document}
|
| 637 |
""")
|
| 638 |
|
| 639 |
+
latex_filled = latex_template.substitute(
|
| 640 |
+
generated_on=datetime.now().strftime('%A, %B %d, %Y at %I:%M %p'),
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 641 |
fhir_id=patient.get("fhir_id", ""),
|
| 642 |
full_name=patient.get("full_name", ""),
|
| 643 |
gender=patient.get("gender", ""),
|
|
|
|
| 652 |
])),
|
| 653 |
marital_status=patient.get("marital_status", ""),
|
| 654 |
language=patient.get("language", ""),
|
| 655 |
+
notes=note_rows
|
| 656 |
)
|
| 657 |
|
| 658 |
+
# Compile LaTeX in a temporary directory
|
| 659 |
with tempfile.TemporaryDirectory() as tmpdir:
|
| 660 |
tex_path = os.path.join(tmpdir, "report.tex")
|
| 661 |
pdf_path = os.path.join(tmpdir, "report.pdf")
|
| 662 |
|
| 663 |
with open(tex_path, "w", encoding="utf-8") as f:
|
| 664 |
+
f.write(latex_filled)
|
|
|
|
|
|
|
|
|
|
|
|
|
| 665 |
|
| 666 |
+
subprocess.run(["latexmk", "-pdf", "-interaction=nonstopmode", tex_path],
|
| 667 |
+
cwd=tmpdir, check=True)
|
| 668 |
|
| 669 |
+
with open(pdf_path, "rb") as f:
|
| 670 |
+
pdf_bytes = f.read()
|
|
|
|
| 671 |
|
| 672 |
+
response = Response(content=pdf_bytes, media_type="application/pdf")
|
| 673 |
+
response.headers["Content-Disposition"] = f"attachment; filename=patient_{patient.get('fhir_id', 'unknown')}_report.pdf"
|
| 674 |
+
return response
|
| 675 |
|
| 676 |
+
except subprocess.CalledProcessError as e:
|
| 677 |
+
logger.error("LaTeX compilation error: %s", str(e))
|
| 678 |
+
raise HTTPException(status_code=500, detail="LaTeX compilation failed")
|
| 679 |
except Exception as e:
|
| 680 |
logger.exception("Unexpected error generating PDF")
|
| 681 |
+
raise HTTPException(status_code=500, detail="Unexpected error generating PDF")
|
| 682 |
+
|
| 683 |
@router.post("/signup", status_code=status.HTTP_201_CREATED)
|
| 684 |
async def signup(data: SignupForm):
|
| 685 |
logger.info(f"Signup attempt for email: {data.email}")
|