Ali2206 commited on
Commit
7f4d78a
·
verified ·
1 Parent(s): c036c19

Update api/routes.py

Browse files
Files changed (1) hide show
  1. api/routes.py +105 -30
api/routes.py CHANGED
@@ -5,6 +5,7 @@ from db.mongo import users_collection, patients_collection, appointments_collect
5
  from core.security import hash_password, verify_password, create_access_token, get_current_user
6
  from datetime import datetime, timedelta
7
  from bson import ObjectId
 
8
  from typing import Optional, List, Dict
9
  from pydantic import BaseModel, Field
10
  from pymongo import UpdateOne, InsertOne, IndexModel
@@ -113,6 +114,26 @@ def standardize_language(language: str) -> str:
113
  return 'en' # Default to English
114
  return LANGUAGE_MAP.get(language, 'en')
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  async def process_synthea_patient(bundle: dict, file_path: str) -> Optional[dict]:
117
  logger.debug(f"Processing patient from file: {file_path}")
118
  patient_data = {}
@@ -640,64 +661,110 @@ async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get
640
  \end{document}
641
  """
642
 
643
- # Build the LaTeX-safe rows
644
  notes = "\n".join([
645
- "{} & {} & {} \\\\".format(n.get("date", ""), n.get("type", ""), n.get("text", "").replace("&", "\\&"))
 
 
 
 
646
  for n in patient.get("notes", [])
647
  ])
648
  conditions = "\n".join([
649
  "{} & {} & {} & {} & {} \\\\".format(
650
- c.get("id", ""), c.get("code", ""), c.get("status", ""), c.get("onset_date", ""), c.get("verification_status", "")
 
 
 
 
651
  )
652
  for c in patient.get("conditions", [])
653
  ])
654
  medications = "\n".join([
655
  "{} & {} & {} & {} & {} \\\\".format(
656
- m.get("id", ""), m.get("name", ""), m.get("status", ""), m.get("prescribed_date", ""), m.get("dosage", "")
 
 
 
 
657
  )
658
  for m in patient.get("medications", [])
659
  ])
660
  encounters = "\n".join([
661
  "{} & {} & {} & {} & {} \\\\".format(
662
- e.get("id", ""), e.get("type", ""), e.get("status", ""), e.get("period", {}).get("start", ""), e.get("service_provider", "")
 
 
 
 
663
  )
664
  for e in patient.get("encounters", [])
665
  ])
666
 
667
- # Update the generated_on date to reflect the current time: 03:10 PM CET, May 16, 2025
668
  latex_filled = latex_template % {
669
- "generated_on": datetime.strptime("2025-05-16 15:10:00+01:00", "%Y-%m-%d %H:%M:%S%z").strftime("%A, %B %d, %Y at %I:%M %p"),
670
- "fhir_id": patient.get("fhir_id", ""),
671
- "full_name": patient.get("full_name", ""),
672
- "gender": patient.get("gender", ""),
673
- "dob": patient.get("date_of_birth", ""),
674
- "age": calculate_age(patient.get("date_of_birth", "")) or "N/A",
675
- "address": "{}, {}, {}, {}, {}".format(
676
- patient.get("address", ""), patient.get("city", ""), patient.get("state", ""),
677
- patient.get("postal_code", ""), patient.get("country", "")
678
- ),
679
- "marital_status": patient.get("marital_status", ""),
680
- "language": patient.get("language", ""),
 
 
 
681
  "notes": notes or "No notes available \\\\",
682
  "conditions": conditions or "No conditions available \\\\",
683
  "medications": medications or "No medications available \\\\",
684
  "encounters": encounters or "No encounters available \\\\"
685
  }
686
 
687
- # Write LaTeX to file
688
- with NamedTemporaryFile(suffix=".tex", mode="w", delete=False) as tex_file:
689
- tex_file.write(latex_filled)
690
- tex_path = tex_file.name
691
-
692
  try:
693
- subprocess.run(["latexmk", "-pdf", tex_path], check=True, capture_output=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
694
  pdf_path = tex_path.replace(".tex", ".pdf")
 
 
 
 
 
 
695
  with open(pdf_path, "rb") as f:
696
  pdf_bytes = f.read()
697
 
698
- subprocess.run(["latexmk", "-c", tex_path], check=True)
699
- os.remove(tex_path)
700
- os.remove(pdf_path)
 
 
 
 
 
 
 
701
 
702
  return Response(
703
  content=pdf_bytes,
@@ -705,11 +772,19 @@ async def generate_patient_pdf(patient_id: str, current_user: dict = Depends(get
705
  headers={"Content-Disposition": f"attachment; filename=patient_{patient.get('fhir_id', 'unknown')}_record.pdf"}
706
  )
707
 
708
- except subprocess.CalledProcessError as e:
709
- raise HTTPException(status_code=500, detail="LaTeX compilation failed")
 
 
 
710
 
 
 
711
  except Exception as e:
712
- raise HTTPException(status_code=500, detail=f"Failed to generate PDF: {str(e)}")
 
 
 
713
  finally:
714
  # Restore the logger level for other routes
715
  logger.setLevel(logging.INFO)
 
5
  from core.security import hash_password, verify_password, create_access_token, get_current_user
6
  from datetime import datetime, timedelta
7
  from bson import ObjectId
8
+ from bson.errors import InvalidId # Added to fix the 'InvalidId' not defined error
9
  from typing import Optional, List, Dict
10
  from pydantic import BaseModel, Field
11
  from pymongo import UpdateOne, InsertOne, IndexModel
 
114
  return 'en' # Default to English
115
  return LANGUAGE_MAP.get(language, 'en')
116
 
117
+ def escape_latex_special_chars(text: str) -> str:
118
+ """Escape special LaTeX characters to prevent compilation errors."""
119
+ if not isinstance(text, str):
120
+ return ""
121
+ replacements = {
122
+ "&": "\\&",
123
+ "%": "\\%",
124
+ "$": "\\$",
125
+ "#": "\\#",
126
+ "_": "\\_",
127
+ "{": "\\{",
128
+ "}": "\\}",
129
+ "~": "\\textasciitilde{}",
130
+ "^": "\\textasciicircum{}",
131
+ "\\": "\\textbackslash{}"
132
+ }
133
+ for char, escape in replacements.items():
134
+ text = text.replace(char, escape)
135
+ return text
136
+
137
  async def process_synthea_patient(bundle: dict, file_path: str) -> Optional[dict]:
138
  logger.debug(f"Processing patient from file: {file_path}")
139
  patient_data = {}
 
661
  \end{document}
662
  """
663
 
664
+ # Build the LaTeX-safe rows with escaped special characters
665
  notes = "\n".join([
666
+ "{} & {} & {} \\\\".format(
667
+ escape_latex_special_chars(n.get("date", "")),
668
+ escape_latex_special_chars(n.get("type", "")),
669
+ escape_latex_special_chars(n.get("text", ""))
670
+ )
671
  for n in patient.get("notes", [])
672
  ])
673
  conditions = "\n".join([
674
  "{} & {} & {} & {} & {} \\\\".format(
675
+ escape_latex_special_chars(c.get("id", "")),
676
+ escape_latex_special_chars(c.get("code", "")),
677
+ escape_latex_special_chars(c.get("status", "")),
678
+ escape_latex_special_chars(c.get("onset_date", "")),
679
+ escape_latex_special_chars(c.get("verification_status", ""))
680
  )
681
  for c in patient.get("conditions", [])
682
  ])
683
  medications = "\n".join([
684
  "{} & {} & {} & {} & {} \\\\".format(
685
+ escape_latex_special_chars(m.get("id", "")),
686
+ escape_latex_special_chars(m.get("name", "")),
687
+ escape_latex_special_chars(m.get("status", "")),
688
+ escape_latex_special_chars(m.get("prescribed_date", "")),
689
+ escape_latex_special_chars(m.get("dosage", ""))
690
  )
691
  for m in patient.get("medications", [])
692
  ])
693
  encounters = "\n".join([
694
  "{} & {} & {} & {} & {} \\\\".format(
695
+ escape_latex_special_chars(e.get("id", "")),
696
+ escape_latex_special_chars(e.get("type", "")),
697
+ escape_latex_special_chars(e.get("status", "")),
698
+ escape_latex_special_chars(e.get("period", {}).get("start", "")),
699
+ escape_latex_special_chars(e.get("service_provider", ""))
700
  )
701
  for e in patient.get("encounters", [])
702
  ])
703
 
704
+ # Update the generated_on date to reflect the current time: 03:14 PM CET, May 16, 2025
705
  latex_filled = latex_template % {
706
+ "generated_on": datetime.strptime("2025-05-16 15:14:00+01:00", "%Y-%m-%d %H:%M:%S%z").strftime("%A, %B %d, %Y at %I:%M %p"),
707
+ "fhir_id": escape_latex_special_chars(patient.get("fhir_id", "")),
708
+ "full_name": escape_latex_special_chars(patient.get("full_name", "")),
709
+ "gender": escape_latex_special_chars(patient.get("gender", "")),
710
+ "dob": escape_latex_special_chars(patient.get("date_of_birth", "")),
711
+ "age": escape_latex_special_chars(str(calculate_age(patient.get("date_of_birth", "")) or "N/A")),
712
+ "address": escape_latex_special_chars("{}, {}, {}, {}, {}".format(
713
+ patient.get("address", ""),
714
+ patient.get("city", ""),
715
+ patient.get("state", ""),
716
+ patient.get("postal_code", ""),
717
+ patient.get("country", "")
718
+ )),
719
+ "marital_status": escape_latex_special_chars(patient.get("marital_status", "")),
720
+ "language": escape_latex_special_chars(patient.get("language", "")),
721
  "notes": notes or "No notes available \\\\",
722
  "conditions": conditions or "No conditions available \\\\",
723
  "medications": medications or "No medications available \\\\",
724
  "encounters": encounters or "No encounters available \\\\"
725
  }
726
 
727
+ # Write LaTeX to file with error handling
 
 
 
 
728
  try:
729
+ with NamedTemporaryFile(suffix=".tex", mode="w", delete=False, encoding="utf-8") as tex_file:
730
+ tex_file.write(latex_filled)
731
+ tex_path = tex_file.name
732
+
733
+ # Compile LaTeX to PDF
734
+ try:
735
+ result = subprocess.run(
736
+ ["latexmk", "-pdf", tex_path],
737
+ check=True,
738
+ capture_output=True,
739
+ text=True
740
+ )
741
+ except subprocess.CalledProcessError as e:
742
+ raise HTTPException(
743
+ status_code=500,
744
+ detail=f"LaTeX compilation failed: {e.stderr}"
745
+ )
746
+
747
+ # Read the generated PDF
748
  pdf_path = tex_path.replace(".tex", ".pdf")
749
+ if not os.path.exists(pdf_path):
750
+ raise HTTPException(
751
+ status_code=500,
752
+ detail="PDF file was not generated"
753
+ )
754
+
755
  with open(pdf_path, "rb") as f:
756
  pdf_bytes = f.read()
757
 
758
+ # Clean up temporary files
759
+ try:
760
+ subprocess.run(["latexmk", "-c", tex_path], check=True, capture_output=True)
761
+ os.remove(tex_path)
762
+ os.remove(pdf_path)
763
+ except Exception as cleanup_error:
764
+ raise HTTPException(
765
+ status_code=500,
766
+ detail=f"Failed to clean up temporary files: {str(cleanup_error)}"
767
+ )
768
 
769
  return Response(
770
  content=pdf_bytes,
 
772
  headers={"Content-Disposition": f"attachment; filename=patient_{patient.get('fhir_id', 'unknown')}_record.pdf"}
773
  )
774
 
775
+ except IOError as io_error:
776
+ raise HTTPException(
777
+ status_code=500,
778
+ detail=f"Failed to write LaTeX file: {str(io_error)}"
779
+ )
780
 
781
+ except HTTPException as http_error:
782
+ raise http_error
783
  except Exception as e:
784
+ raise HTTPException(
785
+ status_code=500,
786
+ detail=f"Failed to generate PDF: {str(e)}"
787
+ )
788
  finally:
789
  # Restore the logger level for other routes
790
  logger.setLevel(logging.INFO)