seapoe1809 commited on
Commit
571f20f
·
verified ·
1 Parent(s): 8a3a49e

Upload 201 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +43 -0
  2. Dockerfile +14 -0
  3. Health_files/ocr_files/Darna_tesseract/USPTF_Intent.txt +28 -0
  4. Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/data_level0.bin +3 -0
  5. Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/header.bin +3 -0
  6. Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/index_metadata.pickle +3 -0
  7. Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/length.bin +3 -0
  8. Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/link_lists.bin +3 -0
  9. Health_files/ocr_files/Darna_tesseract/chroma_storage/chroma.sqlite3 +3 -0
  10. Health_files/ocr_files/Darna_tesseract/combined_output.json +3 -0
  11. Health_files/ocr_files/Darna_tesseract/darnahi_ocr.png +0 -0
  12. Health_files/ocr_files/Darna_tesseract/deidentified_records.txt +49 -0
  13. Health_files/ocr_files/Darna_tesseract/ocr_results.txt +48 -0
  14. Health_files/ocr_files/Darna_tesseract/wordcloud_summary.json +8 -0
  15. Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_medications_wordcloud.png +3 -0
  16. Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_past_medical_history_wordcloud.png +3 -0
  17. Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_screening_wordcloud.png +3 -0
  18. Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_summary_wordcloud.png +3 -0
  19. Health_files/ocr_files/combined_output.json +3 -0
  20. Health_files/ocr_files/fhir_data.pdf +68 -0
  21. Health_files/ocr_files/fhir_output.json +42 -0
  22. Health_files/ocr_files/record_2025-08-07 04-12-02.pdf +0 -0
  23. Health_files/summary/HHI404.PDF +3 -0
  24. Health_files/summary/chart.json +0 -0
  25. Health_files/summary/chest.jpeg +0 -0
  26. Health_files/summary/darna_sample.DCM +3 -0
  27. Health_files/summary/medical_records.db +0 -0
  28. Health_files/summary/sample.xml +133 -0
  29. Health_files/summary/summary.pdf +0 -0
  30. Health_files/upload/Welcome.txt +29 -0
  31. Health_files2/ocr_files/Darna_tesseract/USPTF_Intent.txt +4 -0
  32. Health_files2/ocr_files/Darna_tesseract/darnahi_ocr.png +3 -0
  33. Health_files2/ocr_files/Darna_tesseract/deidentified_records.txt +5 -0
  34. Health_files2/ocr_files/Darna_tesseract/ocr_results.txt +2 -0
  35. Health_files2/ocr_files/Darna_tesseract/wordcloud_summary.json +8 -0
  36. Health_files2/ocr_files/fhir_data.pdf +68 -0
  37. Health_files2/ocr_files/fhir_output.json +13 -0
  38. Health_files2/ocr_files/record_2024-10-01 06-41-08.pdf +0 -0
  39. Health_files2/summary/chart.json +991 -0
  40. Health_files2/summary/darna_sample.DCM +3 -0
  41. Health_files2/summary/medical_records.db +0 -0
  42. Health_files2/summary/record_2024-10-01 06-41-08.pdf +0 -0
  43. Health_files2/summary/sample.xml +133 -0
  44. Health_files2/summary/summary.pdf +0 -0
  45. Health_files2/upload/Welcome.txt +25 -0
  46. README.md +4 -4
  47. analyze.py +987 -0
  48. app.py +1025 -0
  49. darnabot/darnabot.log +11 -0
  50. darnabot/darnabot.py +976 -0
.gitattributes CHANGED
@@ -33,3 +33,46 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ Health_files/ocr_files/Darna_tesseract/chroma_storage/chroma.sqlite3 filter=lfs diff=lfs merge=lfs -text
37
+ Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_medications_wordcloud.png filter=lfs diff=lfs merge=lfs -text
38
+ Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_past_medical_history_wordcloud.png filter=lfs diff=lfs merge=lfs -text
39
+ Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_screening_wordcloud.png filter=lfs diff=lfs merge=lfs -text
40
+ Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_summary_wordcloud.png filter=lfs diff=lfs merge=lfs -text
41
+ Health_files/summary/darna_sample.DCM filter=lfs diff=lfs merge=lfs -text
42
+ Health_files/summary/HHI404.PDF filter=lfs diff=lfs merge=lfs -text
43
+ Health_files2/ocr_files/Darna_tesseract/darnahi_ocr.png filter=lfs diff=lfs merge=lfs -text
44
+ Health_files2/summary/darna_sample.DCM filter=lfs diff=lfs merge=lfs -text
45
+ install_module/\*\*\*{app_name}/favicon.png filter=lfs diff=lfs merge=lfs -text
46
+ install_module/\*\*\*{app_name}/pic1.png filter=lfs diff=lfs merge=lfs -text
47
+ install_module/\*\*\*{app_name}/pic2.png filter=lfs diff=lfs merge=lfs -text
48
+ install_module/Anxiety_Module/favicon.png filter=lfs diff=lfs merge=lfs -text
49
+ install_module/Anxiety_Module/uploads/54321.png filter=lfs diff=lfs merge=lfs -text
50
+ install_module/Anxiety_Module/uploads/anxiety.jpg filter=lfs diff=lfs merge=lfs -text
51
+ install_module/Anxiety_Module/uploads/boston_breath.webp filter=lfs diff=lfs merge=lfs -text
52
+ install_module/Applehealth/pic1.png filter=lfs diff=lfs merge=lfs -text
53
+ install_module/Applehealth/pic2.png filter=lfs diff=lfs merge=lfs -text
54
+ install_module/Dock/pic1.png filter=lfs diff=lfs merge=lfs -text
55
+ install_module/Dock/pic2.png filter=lfs diff=lfs merge=lfs -text
56
+ install_module/Ibs_Module/uploads/boston_breath.webp filter=lfs diff=lfs merge=lfs -text
57
+ install_module/Ibs_Module/uploads/breathing.webp filter=lfs diff=lfs merge=lfs -text
58
+ install_module/Ibs_Module/uploads/breathing2.webp filter=lfs diff=lfs merge=lfs -text
59
+ install_module/Ibs_Module/uploads/clasp.webp filter=lfs diff=lfs merge=lfs -text
60
+ install_module/Strep_Module/favicon.png filter=lfs diff=lfs merge=lfs -text
61
+ install_module/Strep_Module/pic1.png filter=lfs diff=lfs merge=lfs -text
62
+ install_module/Strep_Module/pic2.png filter=lfs diff=lfs merge=lfs -text
63
+ install_module/Tailscale/pic2.png filter=lfs diff=lfs merge=lfs -text
64
+ install_module/Weight_Tracker/pic2.png filter=lfs diff=lfs merge=lfs -text
65
+ static/darna_information.png filter=lfs diff=lfs merge=lfs -text
66
+ static/darnav2_mktg.png filter=lfs diff=lfs merge=lfs -text
67
+ static/deidentify.png filter=lfs diff=lfs merge=lfs -text
68
+ static/favicon.png filter=lfs diff=lfs merge=lfs -text
69
+ static/icons/Anxiety_Module.png filter=lfs diff=lfs merge=lfs -text
70
+ static/icons/Dock.png filter=lfs diff=lfs merge=lfs -text
71
+ static/icons/Ibs_Module.png filter=lfs diff=lfs merge=lfs -text
72
+ static/icons/Immunization_Tracker.png filter=lfs diff=lfs merge=lfs -text
73
+ static/icons/pabv.png filter=lfs diff=lfs merge=lfs -text
74
+ static/icons/PI.png filter=lfs diff=lfs merge=lfs -text
75
+ static/icons/portal.png filter=lfs diff=lfs merge=lfs -text
76
+ static/icons/Strep_Module.png filter=lfs diff=lfs merge=lfs -text
77
+ static/icons/tailscale.png filter=lfs diff=lfs merge=lfs -text
78
+ static/icons/Weight_Tracker.png filter=lfs diff=lfs merge=lfs -text
Dockerfile ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.10
2
+
3
+ WORKDIR /app
4
+
5
+ COPY requirements.txt .
6
+ RUN pip install --no-cache-dir -r requirements.txt
7
+
8
+ COPY . .
9
+
10
+ # For Hugging Face Spaces, they expect port 7860
11
+ EXPOSE 7860
12
+
13
+ # Run the Flask app with gunicorn
14
+ CMD ["gunicorn", "--bind", "0.0.0.0:7860", "app:app"]
Health_files/ocr_files/Darna_tesseract/USPTF_Intent.txt ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ RECOMMENDATIONS:
2
+
3
+
4
+ BP: Blood pressure screening in office screening -- Adults aged 18 years and above
5
+
6
+
7
+ Colonoscopy: Colorectal Cancer: Screening -- Adults aged 45 to 49 years
8
+
9
+
10
+ Bmi screen: If elevated BMI consider Healthy Diet and Physical Activity for Cardiovascular Disease Prevention in Adults With Cardiovascular Risk Factors: Behavioral Counseling Interventions -- Adults with cardiovascular disease risk factors
11
+
12
+
13
+ Tb Screen Test/ Questionnaire: Latent Tuberculosis Infection in Adults: Screening -- Asymptomatic adults at increased risk of latent tuberculosis infection (LTBI)
14
+
15
+
16
+ Fasting Blood Glucose: Prediabetes and Type 2 Diabetes: Screening -- Asymptomatic adults aged 35 to 70 years who have overweight or obesity
17
+
18
+
19
+ Skin Exam: Skin Cancer Prevention: Behavioral Counseling -- Adults, Young adults, adolescents, children, and parents of young children
20
+
21
+
22
+ Heart Disease Questionnaire: Screen for CV risk and consider Statin Use for the Primary Prevention of Cardiovascular Disease in Adults: Preventive Medication -- Adults aged 40 to 75 years who have 1 or more cardiovascular risk factors and an estimated 10-year cardiovascular disease (CVD) risk of 10% or greater
23
+
24
+
25
+ Alcohol Questionnaire: Unhealthy Alcohol Use in Adolescents and Adults: Screening and Behavioral Counseling Interventions -- Adults 18 years or older, including pregnant women
26
+
27
+
28
+ Drug Abuse Questionnaire: Unhealthy Drug Use: Screening -- Adults age 13 years or older
Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/data_level0.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:3c2ab79cf123d0cb09afbd50cc79d2d92cb4a32ed10ff76d68ee6384956ee3bc
3
+ size 5028000
Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/header.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1e9613a5b6cf30e50a1f90bd7fe4811e60080e25726e5823301b1deb935437e9
3
+ size 100
Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/index_metadata.pickle ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:54675c402d61f78ac3a0c31d4322557479be1b67e78b91dbf503181062f2da09
3
+ size 74953
Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/length.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:e9707a4bf58d344364771908bc85816e92b7ab52b2654c5d48c58d538654d43a
3
+ size 12000
Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/link_lists.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:d3040bfc00424cd13e4b00679b35c641c49f5cdcc94935b9520c0393689615a0
3
+ size 25124
Health_files/ocr_files/Darna_tesseract/chroma_storage/chroma.sqlite3 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:265ed0926f5340d73b10e608bc11b4702f87b0eb8a3bd9ee4491c9f810f9fa75
3
+ size 9441280
Health_files/ocr_files/Darna_tesseract/combined_output.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "RECOMMENDATIONS:": "BP: Blood pressure screening in office screening -- Adults aged 18 years and above\nColonoscopy: Colorectal Cancer: Screening -- Adults aged 45 to 49 years\nBmi screen: If elevated BMI consider Healthy Diet and Physical Activity for Cardiovascular Disease Preventi\nTb Screen Test/ Questionnaire: Latent Tuberculosis Infection in Adults: Screening -- Asymptomatic adults at\nFasting Blood Glucose: Prediabetes and Type 2 Diabetes: Screening -- Asymptomatic adults aged 35 to 70\nSkin Exam: Skin Cancer Prevention: Behavioral Counseling -- Adults, Young adults, adolescents, children, a\nHeart Disease Questionnaire: Screen for CV risk and consider Statin Use for the Primary Prevention of Card\nAlcohol Questionnaire: Unhealthy Alcohol Use in Adolescents and Adults: Screening and Behavioral Counse\nDrug Abuse Questionnaire: Unhealthy Drug Use: Screening -- Adults age 13 years or older"
3
+ }
Health_files/ocr_files/Darna_tesseract/darnahi_ocr.png ADDED
Health_files/ocr_files/Darna_tesseract/deidentified_records.txt ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Deidentified Entry | 08/07/2025
2
+ File: darnahi_ocr.png
3
+ Male
4
+ Intolerance: IBS, Mixed
5
+ Ocular: Colonoscopy
6
+ Glucose Pattern & Baldness
7
+ Migraine
8
+
9
+ File: darnahi_summary_wordcloud.png
10
+ Ford Cloud for Darnahi_summai
11
+
12
+ Colonoscopy migraine
13
+
14
+ eGlucose: IBS ocular intolerance
15
+
16
+ Male SS
17
+
18
+ File: darnahi_past_medical_history_wordcloud.png
19
+ Oculi-IgG rising
20
+ Glucose
21
+ Colonoscopy
22
+ Intolerance
23
+ Male
24
+
25
+ File: darnahi_medications_wordcloud.png
26
+ Bid, “Multivitamin Psylliu Strength daily OralCap”
27
+ “Cab Mame”
28
+ C
29
+
30
+ File: darnahi_screening_wordcloud.png
31
+ Word Cloud for darnahi_screening
32
+
33
+ Counseling
34
+
35
+ Questionnaire Use
36
+
37
+ Exams 1d@ runhealthy® s prevention
38
+
39
+ Q Cr Nn 3 4
40
+ Z o
41
+ a Health C | LE ° e Tuberculosis S
42
+ 5 tern ce Veal S.Dise
43
+ $ aged a intection oc dmmadolescents «
44
+ ° a BReart ff Drugs Preventid
45
+ Fasting B Sse £ ° 3
46
+ Glucose mi 2 50 Th |p tbuse 8
47
+ Asymptomatic s 24 Type SBS
48
+ Alcohol @'ss (statin card © Latent Prinary Diet z
49
+
Health_files/ocr_files/Darna_tesseract/ocr_results.txt ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ File: darnahi_ocr.png
2
+ Male
3
+ Intolerance: IBS, Mixed
4
+ Ocular: Colonoscopy
5
+ Glucose Pattern & Baldness
6
+ Migraine
7
+
8
+ File: darnahi_summary_wordcloud.png
9
+ Ford Cloud for Darnahi_summai
10
+
11
+ Colonoscopy migraine
12
+
13
+ eGlucose: IBS ocular intolerance
14
+
15
+ Male SS
16
+
17
+ File: darnahi_past_medical_history_wordcloud.png
18
+ Oculi-IgG rising
19
+ Glucose
20
+ Colonoscopy
21
+ Intolerance
22
+ Male
23
+
24
+ File: darnahi_medications_wordcloud.png
25
+ Bid, “Multivitamin Psylliu Strength daily OralCap”
26
+ “Cab Mame”
27
+ C
28
+
29
+ File: darnahi_screening_wordcloud.png
30
+ Word Cloud for darnahi_screening
31
+
32
+ Counseling
33
+
34
+ Questionnaire Use
35
+
36
+ Exams 1d@ runhealthy® s prevention
37
+
38
+ Q Cr Nn 3 4
39
+ Z o
40
+ a Health C | LE ° e Tuberculosis S
41
+ 5 tern ce Veal S.Dise
42
+ $ aged a intection oc dmmadolescents «
43
+ ° a BReart ff Drugs Preventid
44
+ Fasting B Sse £ ° 3
45
+ Glucose mi 2 50 Th |p tbuse 8
46
+ Asymptomatic s 24 Type SBS
47
+ Alcohol @'ss (statin card © Latent Prinary Diet z
48
+
Health_files/ocr_files/Darna_tesseract/wordcloud_summary.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "darnahi": "Darnahi is open source intiative - self custody of your health data. It is a way to aggregate your health data. One stop to bring all your health data together on your secure computer at home. When you visit a new doctor, you can choose to share your health data with them on demand through email, link etc. This way, you have full custody of your health data and can decide who to share it with and what to do with it.",
3
+ "darnahi_xmr": "Why XMR? cryptocurrencies like Monero can play a role in enhancing users' security and privacy. Monero is a privacy-focused cryptocurrency that provides anonymity and obfuscation of transaction details. This can be particularly relevant in healthcare contexts.",
4
+ "darnahi_summary": "Glucose intolerance Ocular Migraine IBS Mixed Colonoscopy Male pattern baldness",
5
+ "darnahi_past_medical_history": "Glucose intolerance Ocular Migraine IBS Mixed Colonoscopy Male pattern baldness ",
6
+ "darnahi_medications": "{ 'name ' : 'multivitamin ' , 'strength ' : ' 1 tab daily ' , 'instructions ' : `` } { 'name ' : 'Acetaminophen ( Oral Pill ) ' , 'strength ' : '500 mg Cap ' , 'instructions ' : 'as needed for pain ' } { 'name ' : 'Psyllium ( Oral Pill ) ' , 'strength ' : '400 mg Cap ' , 'instructions ' : ' 2 tab bid ' } { 'name ' : 'Minoxidil ( Topical ) ' , 'strength ' : ' 5 % Foam ' , 'instructions ' : 'apply bid ' } { 'name ' : 'Finasteride ( Oral Pill ) ' , 'strength ' : ' 1 mg Tab ' , 'instructions ' : 'po daily ' } ",
7
+ "darnahi_screening": "BP: Blood pressure screening in office screening -- Adults aged 18 years and above\nColonoscopy: Colorectal Cancer: Screening -- Adults aged 45 to 49 years\nBmi screen: If elevated BMI consider Healthy Diet and Physical Activity for Cardiovascular Disease Preventi\nTb Screen Test/ Questionnaire: Latent Tuberculosis Infection in Adults: Screening -- Asymptomatic adults at\nFasting Blood Glucose: Prediabetes and Type 2 Diabetes: Screening -- Asymptomatic adults aged 35 to 70\nSkin Exam: Skin Cancer Prevention: Behavioral Counseling -- Adults, Young adults, adolescents, children, a\nHeart Disease Questionnaire: Screen for CV risk and consider Statin Use for the Primary Prevention of Card\nAlcohol Questionnaire: Unhealthy Alcohol Use in Adolescents and Adults: Screening and Behavioral Counse\nDrug Abuse Questionnaire: Unhealthy Drug Use: Screening -- Adults age 13 years or older"
8
+ }
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_medications_wordcloud.png ADDED

Git LFS Details

  • SHA256: 6f58f78760178df0ffdcc4f74982664ded0b64eb48319731bbb7ca75adbc55e4
  • Pointer size: 131 Bytes
  • Size of remote file: 122 kB
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_past_medical_history_wordcloud.png ADDED

Git LFS Details

  • SHA256: b8a5f098e5fb044b8bc1b87a849f0e77ab1e194ea207dc4e7424335c51b2b16d
  • Pointer size: 131 Bytes
  • Size of remote file: 111 kB
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_screening_wordcloud.png ADDED

Git LFS Details

  • SHA256: f72e8c907c944f83b54f359f5f91cb34e765b9addc234bea7725788fe674060c
  • Pointer size: 131 Bytes
  • Size of remote file: 204 kB
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_summary_wordcloud.png ADDED

Git LFS Details

  • SHA256: 8fe32dde132de0c72a66849495900397568cae40db8793919ecbe28baf46fea6
  • Pointer size: 131 Bytes
  • Size of remote file: 104 kB
Health_files/ocr_files/combined_output.json ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ {
2
+ "RECOMMENDATIONS:": "BP: Blood pressure screening in office screening -- Adults aged 18 years and above\nColonoscopy: Colorectal Cancer: Screening -- Adults aged 45 to 49 years\nBmi screen: If elevated BMI consider Healthy Diet and Physical Activity for Cardiovascular Disease Preventi\nTb Screen Test/ Questionnaire: Latent Tuberculosis Infection in Adults: Screening -- Asymptomatic adults at\nFasting Blood Glucose: Prediabetes and Type 2 Diabetes: Screening -- Asymptomatic adults aged 35 to 70\nSkin Exam: Skin Cancer Prevention: Behavioral Counseling -- Adults, Young adults, adolescents, children, a\nHeart Disease Questionnaire: Screen for CV risk and consider Statin Use for the Primary Prevention of Card\nAlcohol Questionnaire: Unhealthy Alcohol Use in Adolescents and Adults: Screening and Behavioral Counse\nDrug Abuse Questionnaire: Unhealthy Drug Use: Screening -- Adults age 13 years or older"
3
+ }
Health_files/ocr_files/fhir_data.pdf ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.3
2
+ %���� ReportLab Generated PDF document http://www.reportlab.com
3
+ 1 0 obj
4
+ <<
5
+ /F1 2 0 R
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ <<
10
+ /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
11
+ >>
12
+ endobj
13
+ 3 0 obj
14
+ <<
15
+ /Contents 7 0 R /MediaBox [ 0 0 612 792 ] /Parent 6 0 R /Resources <<
16
+ /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
17
+ >> /Rotate 0 /Trans <<
18
+
19
+ >>
20
+ /Type /Page
21
+ >>
22
+ endobj
23
+ 4 0 obj
24
+ <<
25
+ /PageMode /UseNone /Pages 6 0 R /Type /Catalog
26
+ >>
27
+ endobj
28
+ 5 0 obj
29
+ <<
30
+ /Author (anonymous) /CreationDate (D:20250807041202-04'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20250807041202-04'00') /Producer (ReportLab PDF Library - www.reportlab.com)
31
+ /Subject (unspecified) /Title (untitled) /Trapped /False
32
+ >>
33
+ endobj
34
+ 6 0 obj
35
+ <<
36
+ /Count 1 /Kids [ 3 0 R ] /Type /Pages
37
+ >>
38
+ endobj
39
+ 7 0 obj
40
+ <<
41
+ /Filter [ /ASCII85Decode /FlateDecode ] /Length 487
42
+ >>
43
+ stream
44
+ Gas2Fh.dl`&;BTE'Q[3;PU,'j*7FJ^$$@]H,S8ch_9>M]H\26Y[)"*ah5aNq8lB.%M_l?#k8\)<BZb)U=':&h$"'P]ZlR3k[$1(faPZJS*gM&`CVB)mntH/!iV5iTbV]'PH-\4K`&A7pe^rrbl-?jjE=*o;H7[@]4W6\-acoEZNsM>@GJMOon]mfM4qY$B^3$&I,T]TA@*3O`RMD:eMGs4g;kO(HKDPA9o\Z[b@"]CN,I4AH?e>&6]I#c_DS5Mb-fm^\SY724Qnq/94W<]9V(2DCRG#2epI.'K1e+a&ao'@#/\@H*7rldYG0&<g7Q-OWY$;J%%3iaa'`NZt/#j.&<oi2tpbnjt4#Z6[j<ODnBtu@[#/`VU-?)AA1b=#*Bo9*<6?d-R$kN'9`oNd^=-N<YhkOi'rgG^6_NmBE;qg:2'21RC^",P)S,]`lG%L(U=jpg.m:]X3^Q0E5DA(^Em0Ui]OIsQ8)34[t6HAEf~>endstream
45
+ endobj
46
+ xref
47
+ 0 8
48
+ 0000000000 65535 f
49
+ 0000000073 00000 n
50
+ 0000000104 00000 n
51
+ 0000000211 00000 n
52
+ 0000000404 00000 n
53
+ 0000000472 00000 n
54
+ 0000000768 00000 n
55
+ 0000000827 00000 n
56
+ trailer
57
+ <<
58
+ /ID
59
+ [<e43a1b16932766336b46c488f35c5ee6><e43a1b16932766336b46c488f35c5ee6>]
60
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
61
+
62
+ /Info 5 0 R
63
+ /Root 4 0 R
64
+ /Size 8
65
+ >>
66
+ startxref
67
+ 1404
68
+ %%EOF
Health_files/ocr_files/fhir_output.json ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "date_of_birth": "1980-02-29",
3
+ "sex": "M",
4
+ "allergies": [
5
+ "poison ivy",
6
+ "lactose intolerance"
7
+ ],
8
+ "past_medical_history": [
9
+ "Glucose intolerance",
10
+ "Ocular Migraine",
11
+ "IBS Mixed",
12
+ "Colonoscopy",
13
+ "Male pattern baldness"
14
+ ],
15
+ "medications": [
16
+ {
17
+ "name": "multivitamin",
18
+ "strength": "1 tab daily",
19
+ "instructions": ""
20
+ },
21
+ {
22
+ "name": "Acetaminophen (Oral Pill)",
23
+ "strength": "500 mg Cap",
24
+ "instructions": "as needed for pain"
25
+ },
26
+ {
27
+ "name": "Psyllium (Oral Pill)",
28
+ "strength": "400 mg Cap",
29
+ "instructions": "2 tab bid"
30
+ },
31
+ {
32
+ "name": "Minoxidil (Topical)",
33
+ "strength": "5% Foam",
34
+ "instructions": "apply bid"
35
+ },
36
+ {
37
+ "name": "Finasteride (Oral Pill)",
38
+ "strength": "1 mg Tab",
39
+ "instructions": "po daily"
40
+ }
41
+ ]
42
+ }
Health_files/ocr_files/record_2025-08-07 04-12-02.pdf ADDED
Binary file (2.89 kB). View file
 
Health_files/summary/HHI404.PDF ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:2d388be64696f28f72da8c3576cd036c38d281de0f43e695dd4f8feba36f7d97
3
+ size 250193
Health_files/summary/chart.json ADDED
The diff for this file is too large to render. See raw diff
 
Health_files/summary/chest.jpeg ADDED
Health_files/summary/darna_sample.DCM ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee1fcf71fecb6a8aee3b1219388cc7ded40a804056a7647674ba1a77b797ae8e
3
+ size 1702398
Health_files/summary/medical_records.db ADDED
Binary file (77.8 kB). View file
 
Health_files/summary/sample.xml ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <Patient xmlns="http://hl7.org/fhir">
4
+ <id value="example"/>
5
+ <!-- MRN assigned by ACME healthcare on 6-May 2001 -->
6
+ <text> <status value="generated"/> <div xmlns="http://www.w3.org/1999/xhtml"><p style="border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"><b> Jim </b> male, DoB: 1974-12-25 ( Medical record number: 12345 (use: USUAL, period: 2001-05-06
7
+ --&gt; (ongoing)))</p> <hr/> <table class="grid"><tr> <td style="background-color: #f3f5da" title="Record is active">Active:</td> <td> true</td> <td style="background-color: #f3f5da" title="Known status of Patient">Deceased:</td> <td colspan="3">false</td> </tr> <tr> <td style="background-color: #f3f5da" title="Alternate names (see the one above)">Alt Names:</td> <td colspan="3"><ul> <li> Darnahi AUS (OFFICIAL)</li> <li> Darnahi AUS (MAIDEN)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Ways to contact the Patient">Contact Details:</td> <td colspan="3"><ul> <li> -unknown-(HOME)</li> <li> ph: (03) 5555 6473(WORK)</li> <li> ph: (03) 3410 5613(MOBILE)</li> <li> ph: (03) 5555 8834(OLD)</li> <li> 534 Erewhon St PeasantVille, Rainbow, Vic 3999(HOME)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Nominated Contact: Next-of-Kin">Next-of-Kin:</td> <td colspan="3"><ul> <li> Bénédicte du Marché (female)</li> <li> 534 Erewhon St PleasantVille Vic 3999 (HOME)</li> <li> <a href="tel:+33(237)998327">+33 (237) 998327</a> </li> <li> Valid Period: 2012 --&gt; (ongoing)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Patient Links">Links:</td> <td colspan="3"><ul> <li> Managing Organization: <a href="organization-example-gastro.html">Organization/1</a> &quot;Gastroenterology&quot;</li> </ul> </td> </tr> </table> </div> </text> <identifier>
8
+ <use value="usual"/>
9
+ <type>
10
+ <coding>
11
+ <system value="http://terminology.hl7.org/CodeSystem/v2-0203"/>
12
+ <code value="MR"/>
13
+ </coding>
14
+ </type>
15
+ <system value="urn:oid:1.2.36.146.595.217.0.1"/>
16
+ <value value="12345"/>
17
+ <period>
18
+ <start value="2001-05-06"/>
19
+ </period>
20
+ <assigner>
21
+ <display value="Acme Healthcare"/>
22
+ </assigner>
23
+ </identifier>
24
+ <active value="true"/>
25
+ <!-- Peter James Chalmers, but called "Jim" -->
26
+ <name>
27
+ <use value="official"/>
28
+ <family value="Hi"/>
29
+ <given value="Darna"/>
30
+ <given value="Darna"/>
31
+ </name>
32
+ <name>
33
+ <use value="usual"/>
34
+ <given value="Da"/>
35
+ </name>
36
+ <name>
37
+ <!-- Maiden names apply for anyone whose name changes as a result of marriage - irrespective
38
+ of gender -->
39
+ <use value="maiden"/>
40
+ <family value="Hi"/>
41
+ <given value="Darna"/>
42
+ <given value="Darna"/>
43
+ <period>
44
+ <end value="2002"/>
45
+ </period>
46
+ </name>
47
+ <telecom>
48
+ <use value="home"/>
49
+ <!-- home communication details aren't known -->
50
+ </telecom>
51
+ <telecom>
52
+ <system value="phone"/>
53
+ <value value="(03) 5555 6473"/>
54
+ <use value="work"/>
55
+ <rank value="1"/>
56
+ </telecom>
57
+ <telecom>
58
+ <system value="phone"/>
59
+ <value value="(03) 3410 5613"/>
60
+ <use value="mobile"/>
61
+ <rank value="2"/>
62
+ </telecom>
63
+ <telecom>
64
+ <system value="phone"/>
65
+ <value value="(03) 5555 8834"/>
66
+ <use value="old"/>
67
+ <period>
68
+ <end value="2014"/>
69
+ </period>
70
+ </telecom>
71
+ <!-- use FHIR code system for male / female -->
72
+ <gender value="male"/>
73
+ <birthDate value="1974-12-25">
74
+ <extension url="http://hl7.org/fhir/StructureDefinition/patient-birthTime">
75
+ <valueDateTime value="1974-12-25T14:35:45-05:00"/>
76
+ </extension>
77
+ </birthDate>
78
+ <deceasedBoolean value="false"/>
79
+ <address>
80
+ <use value="home"/>
81
+ <type value="both"/>
82
+ <text value="534 Erewhon St PeasantVille, Rainbow, Vic 3999"/>
83
+ <line value="534 Erewhon St"/>
84
+ <city value="PleasantVille"/>
85
+ <district value="Rainbow"/>
86
+ <state value="Vic"/>
87
+ <postalCode value="3999"/>
88
+ <period>
89
+ <start value="1974-12-25"/>
90
+ </period>
91
+ </address>
92
+ <contact>
93
+ <relationship>
94
+ <coding>
95
+ <system value="http://terminology.hl7.org/CodeSystem/v2-0131"/>
96
+ <code value="N"/>
97
+ </coding>
98
+ </relationship>
99
+ <name>
100
+ <family value="du Marché">
101
+ <!-- the "du" part is a family name prefix (VV in iso 21090) -->
102
+ <extension url="http://hl7.org/fhir/StructureDefinition/humanname-own-prefix">
103
+ <valueString value="VV"/>
104
+ </extension>
105
+ </family>
106
+ <given value="Bénédicte"/>
107
+ </name>
108
+ <telecom>
109
+ <system value="phone"/>
110
+ <value value="+33 (237) 998327"/>
111
+ </telecom>
112
+ <address>
113
+ <use value="home"/>
114
+ <type value="both"/>
115
+ <line value="534 Erewhon St"/>
116
+ <city value="PleasantVille"/>
117
+ <district value="Rainbow"/>
118
+ <state value="Vic"/>
119
+ <postalCode value="3999"/>
120
+ <period>
121
+ <start value="1974-12-25"/>
122
+ </period>
123
+ </address>
124
+ <gender value="female"/>
125
+ <period>
126
+ <!-- The contact relationship started in 2012 -->
127
+ <start value="2012"/>
128
+ </period>
129
+ </contact>
130
+ <managingOrganization>
131
+ <reference value="Organization/1"/>
132
+ </managingOrganization>
133
+ </Patient>
Health_files/summary/summary.pdf ADDED
Binary file (24.3 kB). View file
 
Health_files/upload/Welcome.txt ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Welcome to the upload directory:
2
+ ##DARNA Healthy Intent v2- An open source intiative - self custody of your health data
3
+ ##Early stage. Beta and under development and isnt secure. Please take all steps to safeguard your data.
4
+
5
+ What is this project?
6
+ ======================
7
+ DARNA. HI v2.2 is a Forward looking project.
8
+
9
+ Key words: LINUX, python, Open Source.
10
+
11
+ Vision: This will be an open source, linux based personal Health coach which by the way allows self custody data.
12
+
13
+ We are getting ready for the day when powerful Language models would run locally on your device and you can ask it in a very Privacy centered questions very specific to your health . In Darnahi v2.2 a language model (llama 1B) is being used to process personal health data and provide answers to questions specific to an individual's health situation. Additionally, natural language preprocessing (NLP) techniques are being used, along with a retrieval augmented architecture (RAG) to process and understand the health data by the local LLM.
14
+
15
+ In future we hope to have a trained lightweight large language model (LLM) being employed to interpret the processed data and generate relevant health intents or responses tailored to an individual's queries about their health. To summarize Darnahi aims to leverage advanced language AI capabilities to give personalized health insights and recommendations by analyzing someone's specific health information and answering their questions in that context.
16
+
17
+ The core components are NLP for understanding natural language, a RAG model for retrieving and processing relevant data, and a local LLM that ties it together by mapping the data to the queries and providing contextualized health-related output.
18
+
19
+ This project is an Free and open-source software that helps bring together your health data that is currently saved in different places like electronic health records, fitness apps, and wearable devices. We wish to provide a way to aggregate your health data. One stop to bring all your health data together on your secure computer at home. When you visit a new doctor, you can choose to share your encrypted health data with them on demand through QR code, email, link etc. This way, you have full custody of your health data and can decide who to share it with and what to do with it.
20
+
21
+ WHY I CARE ABOUT THIS: I created this project because I had trouble moving my own health data when I switched healthcare providers. As someone who works in the healthcare space, I see that current EHR solutions make it difficult to port your data, even though there are regulatory requirements to do so. It's frustrating to see that some institutions still rely on fax and scan to move data around, which shows how outdated and hidden these data porting techniques are. Given this pain point, I also see an exciting oppportunity with LLM's. I look forward to the day when superior Language models will run on your device at home.
22
+
23
+ This is version 2.2 of the project, and I anticipate that there will be many more iterations and help from OSS community before it takes a good form. My goal is to make it easier for people to take ownership of their health data and store it in one place on their own computer. They can shoose to interact with local LLM or use their deidentified data to interact with powerful public LLM if they wish. Darnahi provides both- a way to interact with local LLM and also to deidentify their data seamlessly/ This way, they can decide who to share it with, how to use it and finally have more control over their own health.
24
+
25
+ License?
26
+ ========
27
+ This program is free software; you can redistribute it and/or modify it under the terms of the Darna modified GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You would of course have to credit Darnahi.
28
+
29
+
Health_files2/ocr_files/Darna_tesseract/USPTF_Intent.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ Please run Analyze module with Age / Sex to generate personalized recommendations.
2
+
3
+ The USPTF recommendations age/ sex will be listed in this document.
4
+
Health_files2/ocr_files/Darna_tesseract/darnahi_ocr.png ADDED

Git LFS Details

  • SHA256: b6b468abfc1d22be39a6c6e57c2b3663884f6b8aea2237405815b351e0986f9a
  • Pointer size: 131 Bytes
  • Size of remote file: 273 kB
Health_files2/ocr_files/Darna_tesseract/deidentified_records.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ Please run Analyze module after uploading health documents using the Upload module.
2
+ This file will then be populated with deidentified records.
3
+ Please inspect record to ensure deidentification. This is a beta version and might not be fully secure.
4
+ For example deidentified records could be shared without risking privacy issues.
5
+ Or deidentified records could be used in LLM's to generate personalized information.
Health_files2/ocr_files/Darna_tesseract/ocr_results.txt ADDED
@@ -0,0 +1,2 @@
 
 
 
1
+ This the Text scraped from health records.
2
+
Health_files2/ocr_files/Darna_tesseract/wordcloud_summary.json ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "darnahi": "Darnahi is open source intiative - self custody of your health data. It is a way to aggregate your health data. One stop to bring all your health data together on your secure computer at home. When you visit a new doctor, you can choose to share your health data with them on demand through email, link etc. This way, you have full custody of your health data and can decide who to share it with and what to do with it.",
3
+ "darnahi_xmr": "Why XMR? cryptocurrencies like Monero can play a role in enhancing users' security and privacy. Monero is a privacy-focused cryptocurrency that provides anonymity and obfuscation of transaction details. This can be particularly relevant in healthcare contexts.",
4
+ "darnahi_summary": "",
5
+ "darnahi_past_medical_history": "Viral infection ",
6
+ "darnahi_medications": "",
7
+ "darnahi_screening": ""
8
+ }
Health_files2/ocr_files/fhir_data.pdf ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ %PDF-1.3
2
+ %���� ReportLab Generated PDF document http://www.reportlab.com
3
+ 1 0 obj
4
+ <<
5
+ /F1 2 0 R
6
+ >>
7
+ endobj
8
+ 2 0 obj
9
+ <<
10
+ /BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
11
+ >>
12
+ endobj
13
+ 3 0 obj
14
+ <<
15
+ /Contents 7 0 R /MediaBox [ 0 0 612 792 ] /Parent 6 0 R /Resources <<
16
+ /Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
17
+ >> /Rotate 0 /Trans <<
18
+
19
+ >>
20
+ /Type /Page
21
+ >>
22
+ endobj
23
+ 4 0 obj
24
+ <<
25
+ /PageMode /UseNone /Pages 6 0 R /Type /Catalog
26
+ >>
27
+ endobj
28
+ 5 0 obj
29
+ <<
30
+ /Author (anonymous) /CreationDate (D:20241001064118-04'00') /Creator (ReportLab PDF Library - www.reportlab.com) /Keywords () /ModDate (D:20241001064118-04'00') /Producer (ReportLab PDF Library - www.reportlab.com)
31
+ /Subject (unspecified) /Title (untitled) /Trapped /False
32
+ >>
33
+ endobj
34
+ 6 0 obj
35
+ <<
36
+ /Count 1 /Kids [ 3 0 R ] /Type /Pages
37
+ >>
38
+ endobj
39
+ 7 0 obj
40
+ <<
41
+ /Filter [ /ASCII85Decode /FlateDecode ] /Length 221
42
+ >>
43
+ stream
44
+ GapXO:Ci%2%)q[_MKbO.s(hg*oOoErO1Q:89GfJ$^q`W#K^\=5Ou?&3lcX&]W!bN6bQ&H>6c-W*n,b/p8)Q=2H/L[Z9B-TROb+OR#*MW[6sGJ&mVG>;+RDM\N\Zr3$du):LPbJ2?)t-XWd!Nq3q/<G'h7:GPI7?2Dqj]llt1'VgJ%0h1t!dj27_F92<B53can,WSLnSC`cPI<5J];XG6C-seNZ4~>endstream
45
+ endobj
46
+ xref
47
+ 0 8
48
+ 0000000000 65535 f
49
+ 0000000073 00000 n
50
+ 0000000104 00000 n
51
+ 0000000211 00000 n
52
+ 0000000404 00000 n
53
+ 0000000472 00000 n
54
+ 0000000768 00000 n
55
+ 0000000827 00000 n
56
+ trailer
57
+ <<
58
+ /ID
59
+ [<799e03bd3f537a80f464187a2391e803><799e03bd3f537a80f464187a2391e803>]
60
+ % ReportLab generated PDF document -- digest (http://www.reportlab.com)
61
+
62
+ /Info 5 0 R
63
+ /Root 4 0 R
64
+ /Size 8
65
+ >>
66
+ startxref
67
+ 1138
68
+ %%EOF
Health_files2/ocr_files/fhir_output.json ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "date_of_birth": "1900-01-01",
3
+ "sex": "F",
4
+ "allergies": [],
5
+ "past_medical_history": [],
6
+ "medications": [
7
+ {
8
+ "name": "",
9
+ "strength": "",
10
+ "instructions": ""
11
+ }
12
+ ]
13
+ }
Health_files2/ocr_files/record_2024-10-01 06-41-08.pdf ADDED
Binary file (2.34 kB). View file
 
Health_files2/summary/chart.json ADDED
@@ -0,0 +1,991 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "lformsVersion": "34.0.0",
3
+ "PATH_DELIMITER": "/",
4
+ "code": "phr",
5
+ "codeList": [
6
+ {
7
+ "code": "phr",
8
+ "display": "Health Record"
9
+ }
10
+ ],
11
+ "identifier": null,
12
+ "name": " \u300c \u2726 \u2726 \u2726 DARNAHI \u270e \u2726 \u2726 \u2726 \u2726 \u300d ",
13
+ "template": "table",
14
+ "items": [
15
+ {
16
+ "header": true,
17
+ "dataType": "SECTION",
18
+ "question": "ABOUT ME",
19
+ "codeList": [
20
+ {
21
+ "system": "custom",
22
+ "code": "birthdate",
23
+ "display": "BIRTHDATE"
24
+ }
25
+ ],
26
+ "questionCode": "birthdate",
27
+ "questionCodeSystem": "custom",
28
+ "linkId": "679676784038",
29
+ "questionCardinality": {
30
+ "max": "1",
31
+ "min": "1"
32
+ },
33
+ "items": [
34
+ {
35
+ "dataType": "DT",
36
+ "question": "DATE OF BIRTH",
37
+ "questionCode": "593486088490",
38
+ "questionCodeSystem": "LinkId",
39
+ "linkId": "593486088490",
40
+ "questionCardinality": {
41
+ "max": "1",
42
+ "min": "1"
43
+ },
44
+ "codeList": [],
45
+ "answerCardinality": {
46
+ "min": "0",
47
+ "max": "1"
48
+ },
49
+ "value": "1900-01-01"
50
+ },
51
+ {
52
+ "dataType": "CWE",
53
+ "question": "BIOLOGICAL SEX",
54
+ "questionCode": "571347576477",
55
+ "questionCodeSystem": "LinkId",
56
+ "linkId": "571347576477",
57
+ "answerCardinality": {
58
+ "max": "1",
59
+ "min": "0"
60
+ },
61
+ "displayControl": {
62
+ "answerLayout": {
63
+ "type": "RADIO_CHECKBOX"
64
+ }
65
+ },
66
+ "answers": [
67
+ {
68
+ "text": "M"
69
+ },
70
+ {
71
+ "text": "F"
72
+ }
73
+ ],
74
+ "codeList": [],
75
+ "questionCardinality": {
76
+ "min": "1",
77
+ "max": "1"
78
+ },
79
+ "value": {
80
+ "text": "F"
81
+ }
82
+ }
83
+ ],
84
+ "displayControl": {
85
+ "questionLayout": "vertical"
86
+ },
87
+ "answerCardinality": {
88
+ "min": "0",
89
+ "max": "1"
90
+ }
91
+ },
92
+ {
93
+ "dataType": "ST",
94
+ "question": "ALLERGIES",
95
+ "questionCode": "4547915263358",
96
+ "questionCodeSystem": "LinkId",
97
+ "linkId": "4547915263358",
98
+ "questionCardinality": {
99
+ "max": "1",
100
+ "min": "1"
101
+ },
102
+ "items": [
103
+ {
104
+ "header": true,
105
+ "dataType": "SECTION",
106
+ "question": "Allergies and Other Dangerous Reactions",
107
+ "codeList": [
108
+ {
109
+ "system": "Custom",
110
+ "code": "allergies",
111
+ "display": "Allergies and Other Dangerous Reactions"
112
+ }
113
+ ],
114
+ "questionCode": "allergies",
115
+ "questionCodeSystem": "Custom",
116
+ "linkId": "/allergies",
117
+ "questionCardinality": {
118
+ "max": "*",
119
+ "min": "1"
120
+ },
121
+ "displayControl": {
122
+ "questionLayout": "horizontal"
123
+ },
124
+ "items": [
125
+ {
126
+ "dataType": "CWE",
127
+ "question": "Name",
128
+ "codeList": [
129
+ {
130
+ "system": "Custom",
131
+ "code": "allergy_name",
132
+ "display": "Name"
133
+ }
134
+ ],
135
+ "questionCode": "allergy_name",
136
+ "questionCodeSystem": "Custom",
137
+ "linkId": "/allergies/allergy_name",
138
+ "answerCardinality": {
139
+ "max": "1",
140
+ "min": "0"
141
+ },
142
+ "displayControl": {
143
+ "answerLayout": {
144
+ "type": "COMBO_BOX"
145
+ }
146
+ },
147
+ "answers": [
148
+ {
149
+ "code": "food",
150
+ "text": "Food allergies"
151
+ },
152
+ {
153
+ "code": "FOOD-2",
154
+ "text": "Chocolate"
155
+ },
156
+ {
157
+ "code": "FOOD-22",
158
+ "text": "Crab"
159
+ },
160
+ {
161
+ "code": "FOOD-4",
162
+ "text": "Egg"
163
+ },
164
+ {
165
+ "code": "FOOD-5",
166
+ "text": "Fish"
167
+ },
168
+ {
169
+ "code": "FOOD-7",
170
+ "text": "Gluten"
171
+ },
172
+ {
173
+ "code": "FOOD-19",
174
+ "text": "Milk"
175
+ },
176
+ {
177
+ "code": "FOOD-16",
178
+ "text": "Monosodium Glutamate (MSG)"
179
+ },
180
+ {
181
+ "code": "FOOD-9",
182
+ "text": "Peanut"
183
+ },
184
+ {
185
+ "code": "FOOD-10",
186
+ "text": "Pork"
187
+ },
188
+ {
189
+ "code": "FOOD-18",
190
+ "text": "Sesame"
191
+ },
192
+ {
193
+ "code": "FOOD-12",
194
+ "text": "Shellfish"
195
+ },
196
+ {
197
+ "code": "FOOD-21",
198
+ "text": "Shrimp"
199
+ },
200
+ {
201
+ "code": "FOOD-13",
202
+ "text": "Soy"
203
+ },
204
+ {
205
+ "code": "FOOD-14",
206
+ "text": "Tomatoes"
207
+ },
208
+ {
209
+ "code": "FOOD-17",
210
+ "text": "Tree Nuts"
211
+ },
212
+ {
213
+ "code": "FOOD-20",
214
+ "text": "Wheat"
215
+ },
216
+ {
217
+ "code": "FOOD-23",
218
+ "text": "Cochineal extract (Carmine) red dye"
219
+ },
220
+ {
221
+ "code": "FOOD-24",
222
+ "text": "FD&C Blue No. 1 dye"
223
+ },
224
+ {
225
+ "code": "FOOD-25",
226
+ "text": "FD&C Yellow No. 2 dye"
227
+ },
228
+ {
229
+ "code": "environmental",
230
+ "text": "Environmental allergies"
231
+ },
232
+ {
233
+ "code": "OTHR-18",
234
+ "text": "Cat"
235
+ },
236
+ {
237
+ "code": "OTHR-4",
238
+ "text": "Cockroach"
239
+ },
240
+ {
241
+ "code": "OTHR-5",
242
+ "text": "Cold Weather"
243
+ },
244
+ {
245
+ "code": "OTHR-17",
246
+ "text": "Dog"
247
+ },
248
+ {
249
+ "code": "OTHR-6",
250
+ "text": "Dust Mites"
251
+ },
252
+ {
253
+ "code": "OTHR-7",
254
+ "text": "Hay Fever"
255
+ },
256
+ {
257
+ "code": "OTHR-1",
258
+ "text": "Iodinated x-ray contrast"
259
+ },
260
+ {
261
+ "code": "OTHR-2",
262
+ "text": "Latex"
263
+ },
264
+ {
265
+ "code": "OTHR-8",
266
+ "text": "Mold"
267
+ },
268
+ {
269
+ "code": "OTHR-9",
270
+ "text": "Nickel"
271
+ },
272
+ {
273
+ "code": "OTHR-10",
274
+ "text": "Pet Dander"
275
+ },
276
+ {
277
+ "code": "OTHR-19",
278
+ "text": "Pollen"
279
+ },
280
+ {
281
+ "code": "OTHR-11",
282
+ "text": "Ragweed"
283
+ },
284
+ {
285
+ "code": "OTHR-12",
286
+ "text": "Semen"
287
+ },
288
+ {
289
+ "code": "OTHR-13",
290
+ "text": "Sun"
291
+ },
292
+ {
293
+ "code": "OTHR-3",
294
+ "text": "Wasp, hornet, bee sting"
295
+ },
296
+ {
297
+ "code": "medClass",
298
+ "text": "Medication class allergies"
299
+ },
300
+ {
301
+ "code": "DRUG-CLASS-1",
302
+ "text": "ACE Inhibitors"
303
+ },
304
+ {
305
+ "code": "DRUG-CLASS-2",
306
+ "text": "Aminoglycosides"
307
+ },
308
+ {
309
+ "code": "DRUG-CLASS-3",
310
+ "text": "Antihistamines"
311
+ },
312
+ {
313
+ "code": "DRUG-CLASS-4",
314
+ "text": "Benzodiazepines"
315
+ },
316
+ {
317
+ "code": "DRUG-CLASS-5",
318
+ "text": "Beta Blockers"
319
+ },
320
+ {
321
+ "code": "DRUG-CLASS-6",
322
+ "text": "Calcium Channel Blockers"
323
+ },
324
+ {
325
+ "code": "DRUG-CLASS-7",
326
+ "text": "Cephalosporins"
327
+ },
328
+ {
329
+ "code": "DRUG-CLASS-8",
330
+ "text": "Diuretics"
331
+ },
332
+ {
333
+ "code": "DRUG-CLASS-9",
334
+ "text": "H2 Blockers"
335
+ },
336
+ {
337
+ "code": "DRUG-CLASS-10",
338
+ "text": "Insulins"
339
+ },
340
+ {
341
+ "code": "DRUG-CLASS-11",
342
+ "text": "Iodine Containing Medications"
343
+ },
344
+ {
345
+ "code": "DRUG-CLASS-12",
346
+ "text": "Local Anesthetics"
347
+ },
348
+ {
349
+ "code": "DRUG-CLASS-13",
350
+ "text": "Macrolides (like Erythromycin)"
351
+ },
352
+ {
353
+ "code": "DRUG-CLASS-14",
354
+ "text": "Muscle Relaxants, Skeletal"
355
+ },
356
+ {
357
+ "code": "DRUG-CLASS-15",
358
+ "text": "Narcotic Analgesics"
359
+ },
360
+ {
361
+ "code": "DRUG-CLASS-16",
362
+ "text": "Nonsteroidal Anti Inflam. Agents (NSAID)"
363
+ },
364
+ {
365
+ "code": "DRUG-CLASS-24",
366
+ "text": "Penicillin and Derivatives"
367
+ },
368
+ {
369
+ "code": "DRUG-CLASS-17",
370
+ "text": "Phenothiazines"
371
+ },
372
+ {
373
+ "code": "DRUG-CLASS-18",
374
+ "text": "Proton Pump Inhibitors"
375
+ },
376
+ {
377
+ "code": "DRUG-CLASS-19",
378
+ "text": "Quinolone Antibiotics"
379
+ },
380
+ {
381
+ "code": "DRUG-CLASS-20",
382
+ "text": "Serotonin Re-Uptake Inhibitors"
383
+ },
384
+ {
385
+ "code": "DRUG-CLASS-21",
386
+ "text": "Statins"
387
+ },
388
+ {
389
+ "code": "DRUG-CLASS-22",
390
+ "text": "Sulfa Drugs"
391
+ },
392
+ {
393
+ "code": "DRUG-CLASS-23",
394
+ "text": "Tetracycline"
395
+ },
396
+ {
397
+ "code": "medication",
398
+ "text": "Medication allergies"
399
+ },
400
+ {
401
+ "code": "MED-57",
402
+ "text": "ALEVE (Naproxen)"
403
+ },
404
+ {
405
+ "code": "MED-2",
406
+ "text": "AMBIEN (Zolpedem)"
407
+ },
408
+ {
409
+ "code": "MED-97",
410
+ "text": "Amoxicillin"
411
+ },
412
+ {
413
+ "code": "MED-6",
414
+ "text": "Aspirin (ASA)"
415
+ },
416
+ {
417
+ "code": "MED-7",
418
+ "text": "ATIVAN (Lorazapam)"
419
+ },
420
+ {
421
+ "code": "MED-8",
422
+ "text": "ATROVENT (Ipartropium)"
423
+ },
424
+ {
425
+ "code": "MED-55",
426
+ "text": "AVINZA (Morphine)"
427
+ },
428
+ {
429
+ "code": "MED-9",
430
+ "text": "Bacitracin"
431
+ },
432
+ {
433
+ "code": "MED-10",
434
+ "text": "BACTRIM (Sulfamethoxazol/trimethaprim)"
435
+ },
436
+ {
437
+ "code": "MED-11",
438
+ "text": "BENADRYL (Diphenhydramine )"
439
+ },
440
+ {
441
+ "code": "MED-13",
442
+ "text": "BUMEX (Bumetanide)"
443
+ },
444
+ {
445
+ "code": "MED-16",
446
+ "text": "CARDIZEM (Diltizzam)"
447
+ },
448
+ {
449
+ "code": "MED-99",
450
+ "text": "CEFZIL (Cefprozil)"
451
+ },
452
+ {
453
+ "code": "MED-18",
454
+ "text": "CIPROFLOXACIN (Cipro)"
455
+ },
456
+ {
457
+ "code": "MED-19",
458
+ "text": "Codeine"
459
+ },
460
+ {
461
+ "code": "MED-20",
462
+ "text": "COLACE (Docusate Sodium)"
463
+ },
464
+ {
465
+ "code": "MED-21",
466
+ "text": "COMPAZINE (Prochlorperazine Maleate)"
467
+ },
468
+ {
469
+ "code": "MED-22",
470
+ "text": "COUMADIN (Warfarin)"
471
+ },
472
+ {
473
+ "code": "MED-23",
474
+ "text": "DALMANE (Flurazepam)"
475
+ },
476
+ {
477
+ "code": "MED-52",
478
+ "text": "DEMEROL (Meperidine)"
479
+ },
480
+ {
481
+ "code": "MED-88",
482
+ "text": "DEPAKOTE ER (Valproic Acid)"
483
+ },
484
+ {
485
+ "code": "MED-26",
486
+ "text": "DILANTIN (Phenytoin)"
487
+ },
488
+ {
489
+ "code": "MED-28",
490
+ "text": "DULCOLAX (Bisacodyl)"
491
+ },
492
+ {
493
+ "code": "MED-29",
494
+ "text": "E-MYCIN (Erythromycin)"
495
+ },
496
+ {
497
+ "code": "MED-30",
498
+ "text": "GASTROGRAFIN(Diatrizoate Meglumine)"
499
+ },
500
+ {
501
+ "code": "MED-31",
502
+ "text": "GLUCOPHAGE (Metformin)"
503
+ },
504
+ {
505
+ "code": "MED-32",
506
+ "text": "HALCION (Triazolam)"
507
+ },
508
+ {
509
+ "code": "MED-33",
510
+ "text": "HALDOL (Haloperidol)"
511
+ },
512
+ {
513
+ "code": "MED-35",
514
+ "text": "HUMALIN (human insulin)"
515
+ },
516
+ {
517
+ "code": "MED-37",
518
+ "text": "IMDUR (Isosorbide)"
519
+ },
520
+ {
521
+ "code": "MED-64",
522
+ "text": "ISONIAZID (Isoniazide)"
523
+ },
524
+ {
525
+ "code": "MED-41",
526
+ "text": "KAYEVELATE (Sodium Polystyrene Sulfonate)"
527
+ },
528
+ {
529
+ "code": "MED-42",
530
+ "text": "KLONOPIN (Clonazepam)"
531
+ },
532
+ {
533
+ "code": "MED-43",
534
+ "text": "Lactose"
535
+ },
536
+ {
537
+ "code": "MED-44",
538
+ "text": "LASIX (Furosemide)"
539
+ },
540
+ {
541
+ "code": "MED-45",
542
+ "text": "LEVAQUIN (Levofloxacin)"
543
+ },
544
+ {
545
+ "code": "MED-46",
546
+ "text": "LIBRIUM (Chlordiazepoxide)"
547
+ },
548
+ {
549
+ "code": "MED-47",
550
+ "text": "Lidocaine, Local"
551
+ },
552
+ {
553
+ "code": "MED-48",
554
+ "text": "LIPITOR (Atorvastatin)"
555
+ },
556
+ {
557
+ "code": "MED-49",
558
+ "text": "LOPRESSOR (Metroprolol)"
559
+ },
560
+ {
561
+ "code": "MED-50",
562
+ "text": "LOVENOX (Enoxaparin)"
563
+ },
564
+ {
565
+ "code": "MED-51",
566
+ "text": "MELLARIL (Thioridazine)"
567
+ },
568
+ {
569
+ "code": "MED-36",
570
+ "text": "MOTRIN/ADVIL (Ibuprofen)"
571
+ },
572
+ {
573
+ "code": "MED-59",
574
+ "text": "NORVASC (Amlodipine)"
575
+ },
576
+ {
577
+ "code": "MED-98",
578
+ "text": "OMNICEF (Cefdinir)"
579
+ },
580
+ {
581
+ "code": "MED-96",
582
+ "text": "Penicillin"
583
+ },
584
+ {
585
+ "code": "MED-61",
586
+ "text": "PEPCID (Famotidine)"
587
+ },
588
+ {
589
+ "code": "MED-62",
590
+ "text": "PERMITIL (Fluphenazine)"
591
+ },
592
+ {
593
+ "code": "MED-65",
594
+ "text": "PLAVIX (Clopidogrel)"
595
+ },
596
+ {
597
+ "code": "MED-67",
598
+ "text": "PREVACID (Lansoprazole)"
599
+ },
600
+ {
601
+ "code": "MED-68",
602
+ "text": "PROLIXIN (Fluphenazine)"
603
+ },
604
+ {
605
+ "code": "MED-70",
606
+ "text": "REGLAN (Metoclopramide)"
607
+ },
608
+ {
609
+ "code": "MED-71",
610
+ "text": "RESTORIL (Temazepam)"
611
+ },
612
+ {
613
+ "code": "MED-53",
614
+ "text": "ROBAXIN (Methocarbamol)"
615
+ },
616
+ {
617
+ "code": "MED-72",
618
+ "text": "SENOKOT (Senna)"
619
+ },
620
+ {
621
+ "code": "MED-73",
622
+ "text": "SERAX (Oxazepam)"
623
+ },
624
+ {
625
+ "code": "MED-74",
626
+ "text": "SERENTIL (Mesoridazine)"
627
+ },
628
+ {
629
+ "code": "MED-66",
630
+ "text": "SLOW-K (Potassium)"
631
+ },
632
+ {
633
+ "code": "MED-75",
634
+ "text": "SOLU MEDROL (Methylprednisolone )"
635
+ },
636
+ {
637
+ "code": "MED-77",
638
+ "text": "STELAZINE (Trifluoperazine)"
639
+ },
640
+ {
641
+ "code": "MED-79",
642
+ "text": "SYNTHROID (Thyroxin)"
643
+ },
644
+ {
645
+ "code": "MED-15",
646
+ "text": "TEGRETOL (Carbamazepine)"
647
+ },
648
+ {
649
+ "code": "MED-82",
650
+ "text": "THORAZINE (Chlorpromazine)"
651
+ },
652
+ {
653
+ "code": "MED-83",
654
+ "text": "TOPROL (Metoprolol)"
655
+ },
656
+ {
657
+ "code": "MED-84",
658
+ "text": "TRANXENE (Clorazepate)"
659
+ },
660
+ {
661
+ "code": "MED-85",
662
+ "text": "TRILAFON (Perphenazie)"
663
+ },
664
+ {
665
+ "code": "MED-86",
666
+ "text": "TYLENOL (Acetaminophen)"
667
+ },
668
+ {
669
+ "code": "MED-25",
670
+ "text": "VALIUM (Diastat)"
671
+ },
672
+ {
673
+ "code": "MED-87",
674
+ "text": "VALIUM (Diazepam)"
675
+ },
676
+ {
677
+ "code": "MED-89",
678
+ "text": "VASOTEC (Enalapril)"
679
+ },
680
+ {
681
+ "code": "MED-90",
682
+ "text": "VITAMIN K1 (Phytonadione)"
683
+ },
684
+ {
685
+ "code": "MED-91",
686
+ "text": "XANAX (Alprazolam)"
687
+ },
688
+ {
689
+ "code": "MED-92",
690
+ "text": "ZAROXOLYN (Metolazone)"
691
+ },
692
+ {
693
+ "code": "MED-93",
694
+ "text": "ZOLOFT (Sertraline)"
695
+ },
696
+ {
697
+ "code": "MED-94",
698
+ "text": "ZOSYN (Piperacillin/Tazobactam)"
699
+ },
700
+ {
701
+ "code": "MED-95",
702
+ "text": "ZYPREXA (Olanzapine)"
703
+ }
704
+ ],
705
+ "questionCardinality": {
706
+ "min": "1",
707
+ "max": "1"
708
+ }
709
+ },
710
+ {
711
+ "dataType": "TX",
712
+ "question": "Comment",
713
+ "codeList": [
714
+ {
715
+ "system": "Custom",
716
+ "code": "allergy_comment",
717
+ "display": "Comment"
718
+ }
719
+ ],
720
+ "questionCode": "allergy_comment",
721
+ "questionCodeSystem": "Custom",
722
+ "linkId": "/allergies/allergy_comment",
723
+ "questionCardinality": {
724
+ "max": "1",
725
+ "min": "1"
726
+ },
727
+ "answerCardinality": {
728
+ "min": "0",
729
+ "max": "1"
730
+ }
731
+ }
732
+ ],
733
+ "answerCardinality": {
734
+ "min": "0",
735
+ "max": "1"
736
+ }
737
+ }
738
+ ],
739
+ "codeList": [],
740
+ "answerCardinality": {
741
+ "min": "0",
742
+ "max": "1"
743
+ }
744
+ },
745
+ {
746
+ "header": true,
747
+ "dataType": "SECTION",
748
+ "question": "PAST MEDICAL HISTORY:",
749
+ "codeList": [
750
+ {
751
+ "system": "Custom",
752
+ "code": "conditions",
753
+ "display": "PAST MEDICAL HISTORY"
754
+ }
755
+ ],
756
+ "questionCode": "conditions",
757
+ "questionCodeSystem": "Custom",
758
+ "linkId": "/conditions",
759
+ "questionCardinality": {
760
+ "max": "*",
761
+ "min": "1"
762
+ },
763
+ "displayControl": {
764
+ "questionLayout": "horizontal"
765
+ },
766
+ "items": [
767
+ {
768
+ "externallyDefined": "https://clinicaltables.nlm.nih.gov/api/conditions/v3/search",
769
+ "dataType": "CWE",
770
+ "question": "PAST MEDICAL HISTORY",
771
+ "codeList": [
772
+ {
773
+ "system": "Custom",
774
+ "code": "condition",
775
+ "display": "PAST MEDICAL HISTORY"
776
+ }
777
+ ],
778
+ "questionCode": "condition",
779
+ "questionCodeSystem": "Custom",
780
+ "linkId": "/conditions/condition",
781
+ "answerCardinality": {
782
+ "max": "1",
783
+ "min": "0"
784
+ },
785
+ "displayControl": {
786
+ "answerLayout": {
787
+ "type": "COMBO_BOX"
788
+ }
789
+ },
790
+ "questionCardinality": {
791
+ "min": "1",
792
+ "max": "1"
793
+ }
794
+ },
795
+ {
796
+ "dataType": "DT",
797
+ "question": "Started",
798
+ "codeList": [
799
+ {
800
+ "system": "Custom",
801
+ "code": "cond_started",
802
+ "display": "Started"
803
+ }
804
+ ],
805
+ "questionCode": "cond_started",
806
+ "questionCodeSystem": "Custom",
807
+ "linkId": "/conditions/cond_started",
808
+ "questionCardinality": {
809
+ "max": "1",
810
+ "min": "1"
811
+ },
812
+ "answerCardinality": {
813
+ "min": "0",
814
+ "max": "1"
815
+ },
816
+ "value": "2022-01-04"
817
+ }
818
+ ],
819
+ "answerCardinality": {
820
+ "min": "0",
821
+ "max": "1"
822
+ }
823
+ },
824
+ {
825
+ "header": true,
826
+ "dataType": "SECTION",
827
+ "question": "MEDICATIONS:",
828
+ "obj_text": {
829
+ "extension": [
830
+ {
831
+ "url": "http://hl7.org/fhir/StructureDefinition/rendering-style",
832
+ "valueString": ".your-class-name {\n color: black; /* Text color */\n font-weight: bold; /* Bold text */\n font-size: 1.5em; /* H4 size approximation */\n background-color: orange; /* Background color */\n}"
833
+ }
834
+ ]
835
+ },
836
+ "questionCode": "/medications",
837
+ "questionCodeSystem": "LinkId",
838
+ "linkId": "/medications",
839
+ "questionCardinality": {
840
+ "max": "*",
841
+ "min": "1"
842
+ },
843
+ "displayControl": {
844
+ "questionLayout": "horizontal"
845
+ },
846
+ "items": [
847
+ {
848
+ "externallyDefined": "https://clinicaltables.nlm.nih.gov/api/rxterms/v3/search?ef=STRENGTHS_AND_FORMS,RXCUIS",
849
+ "dataType": "CWE",
850
+ "question": "MEDICATIONS",
851
+ "codeList": [
852
+ {
853
+ "system": "Custom",
854
+ "code": "med_name",
855
+ "display": "MEDICATIONS"
856
+ }
857
+ ],
858
+ "questionCode": "med_name",
859
+ "questionCodeSystem": "Custom",
860
+ "linkId": "/medications/med_name",
861
+ "answerCardinality": {
862
+ "max": "1",
863
+ "min": "0"
864
+ },
865
+ "displayControl": {
866
+ "answerLayout": {
867
+ "type": "COMBO_BOX"
868
+ }
869
+ },
870
+ "questionCardinality": {
871
+ "min": "1",
872
+ "max": "1"
873
+ }
874
+ },
875
+ {
876
+ "dataType": "CWE",
877
+ "question": "Strength",
878
+ "codeList": [
879
+ {
880
+ "system": "Custom",
881
+ "code": "med_strength",
882
+ "display": "Strength"
883
+ }
884
+ ],
885
+ "questionCode": "med_strength",
886
+ "questionCodeSystem": "Custom",
887
+ "linkId": "/medications/med_strength",
888
+ "answerCardinality": {
889
+ "max": "1",
890
+ "min": "0"
891
+ },
892
+ "displayControl": {
893
+ "answerLayout": {
894
+ "type": "COMBO_BOX"
895
+ }
896
+ },
897
+ "dataControl": [
898
+ {
899
+ "source": {
900
+ "sourceType": "INTERNAL",
901
+ "sourceLinkId": "/medications/med_name"
902
+ },
903
+ "construction": "ARRAY",
904
+ "dataFormat": {
905
+ "code": "value.data.RXCUIS",
906
+ "text": "value.data.STRENGTHS_AND_FORMS"
907
+ },
908
+ "onAttribute": "answers"
909
+ }
910
+ ],
911
+ "answers": [],
912
+ "questionCardinality": {
913
+ "min": "1",
914
+ "max": "1"
915
+ }
916
+ },
917
+ {
918
+ "dataType": "TX",
919
+ "question": "Instructions",
920
+ "codeList": [
921
+ {
922
+ "system": "Custom",
923
+ "code": "med_instructions",
924
+ "display": "Instructions"
925
+ }
926
+ ],
927
+ "questionCode": "med_instructions",
928
+ "questionCodeSystem": "Custom",
929
+ "linkId": "/medications/med_instructions",
930
+ "questionCardinality": {
931
+ "max": "1",
932
+ "min": "1"
933
+ },
934
+ "answerCardinality": {
935
+ "min": "0",
936
+ "max": "1"
937
+ }
938
+ },
939
+ {
940
+ "dataType": "DT",
941
+ "question": "Resupply",
942
+ "codeList": [
943
+ {
944
+ "system": "Custom",
945
+ "code": "med_resupply",
946
+ "display": "Resupply"
947
+ }
948
+ ],
949
+ "questionCode": "med_resupply",
950
+ "questionCodeSystem": "Custom",
951
+ "linkId": "/medications/med_resupply",
952
+ "questionCardinality": {
953
+ "max": "1",
954
+ "min": "1"
955
+ },
956
+ "answerCardinality": {
957
+ "min": "0",
958
+ "max": "1"
959
+ }
960
+ }
961
+ ],
962
+ "codeList": [],
963
+ "answerCardinality": {
964
+ "min": "0",
965
+ "max": "1"
966
+ }
967
+ }
968
+ ],
969
+ "templateOptions": {
970
+ "showQuestionCode": false,
971
+ "showCodingInstruction": false,
972
+ "allowMultipleEmptyRepeatingItems": false,
973
+ "allowHTMLInInstructions": false,
974
+ "displayControl": {
975
+ "questionLayout": "vertical"
976
+ },
977
+ "viewMode": "auto",
978
+ "defaultAnswerLayout": {
979
+ "answerLayout": {
980
+ "type": "COMBO_BOX",
981
+ "columns": "0"
982
+ }
983
+ },
984
+ "hideTreeLine": false,
985
+ "hideIndentation": false,
986
+ "hideRepetitionNumber": false,
987
+ "displayScoreWithAnswerText": true
988
+ },
989
+ "hasSavedData": true,
990
+ "fhirVersion": "R4"
991
+ }
Health_files2/summary/darna_sample.DCM ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:ee1fcf71fecb6a8aee3b1219388cc7ded40a804056a7647674ba1a77b797ae8e
3
+ size 1702398
Health_files2/summary/medical_records.db ADDED
Binary file (12.3 kB). View file
 
Health_files2/summary/record_2024-10-01 06-41-08.pdf ADDED
Binary file (2.34 kB). View file
 
Health_files2/summary/sample.xml ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+
3
+ <Patient xmlns="http://hl7.org/fhir">
4
+ <id value="example"/>
5
+ <!-- MRN assigned by ACME healthcare on 6-May 2001 -->
6
+ <text> <status value="generated"/> <div xmlns="http://www.w3.org/1999/xhtml"><p style="border: 1px #661aff solid; background-color: #e6e6ff; padding: 10px;"><b> Jim </b> male, DoB: 1974-12-25 ( Medical record number: 12345 (use: USUAL, period: 2001-05-06
7
+ --&gt; (ongoing)))</p> <hr/> <table class="grid"><tr> <td style="background-color: #f3f5da" title="Record is active">Active:</td> <td> true</td> <td style="background-color: #f3f5da" title="Known status of Patient">Deceased:</td> <td colspan="3">false</td> </tr> <tr> <td style="background-color: #f3f5da" title="Alternate names (see the one above)">Alt Names:</td> <td colspan="3"><ul> <li> Darnahi AUS (OFFICIAL)</li> <li> Darnahi AUS (MAIDEN)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Ways to contact the Patient">Contact Details:</td> <td colspan="3"><ul> <li> -unknown-(HOME)</li> <li> ph: (03) 5555 6473(WORK)</li> <li> ph: (03) 3410 5613(MOBILE)</li> <li> ph: (03) 5555 8834(OLD)</li> <li> 534 Erewhon St PeasantVille, Rainbow, Vic 3999(HOME)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Nominated Contact: Next-of-Kin">Next-of-Kin:</td> <td colspan="3"><ul> <li> Bénédicte du Marché (female)</li> <li> 534 Erewhon St PleasantVille Vic 3999 (HOME)</li> <li> <a href="tel:+33(237)998327">+33 (237) 998327</a> </li> <li> Valid Period: 2012 --&gt; (ongoing)</li> </ul> </td> </tr> <tr> <td style="background-color: #f3f5da" title="Patient Links">Links:</td> <td colspan="3"><ul> <li> Managing Organization: <a href="organization-example-gastro.html">Organization/1</a> &quot;Gastroenterology&quot;</li> </ul> </td> </tr> </table> </div> </text> <identifier>
8
+ <use value="usual"/>
9
+ <type>
10
+ <coding>
11
+ <system value="http://terminology.hl7.org/CodeSystem/v2-0203"/>
12
+ <code value="MR"/>
13
+ </coding>
14
+ </type>
15
+ <system value="urn:oid:1.2.36.146.595.217.0.1"/>
16
+ <value value="12345"/>
17
+ <period>
18
+ <start value="2001-05-06"/>
19
+ </period>
20
+ <assigner>
21
+ <display value="Acme Healthcare"/>
22
+ </assigner>
23
+ </identifier>
24
+ <active value="true"/>
25
+ <!-- Peter James Chalmers, but called "Jim" -->
26
+ <name>
27
+ <use value="official"/>
28
+ <family value="Hi"/>
29
+ <given value="Darna"/>
30
+ <given value="Darna"/>
31
+ </name>
32
+ <name>
33
+ <use value="usual"/>
34
+ <given value="Da"/>
35
+ </name>
36
+ <name>
37
+ <!-- Maiden names apply for anyone whose name changes as a result of marriage - irrespective
38
+ of gender -->
39
+ <use value="maiden"/>
40
+ <family value="Hi"/>
41
+ <given value="Darna"/>
42
+ <given value="Darna"/>
43
+ <period>
44
+ <end value="2002"/>
45
+ </period>
46
+ </name>
47
+ <telecom>
48
+ <use value="home"/>
49
+ <!-- home communication details aren't known -->
50
+ </telecom>
51
+ <telecom>
52
+ <system value="phone"/>
53
+ <value value="(03) 5555 6473"/>
54
+ <use value="work"/>
55
+ <rank value="1"/>
56
+ </telecom>
57
+ <telecom>
58
+ <system value="phone"/>
59
+ <value value="(03) 3410 5613"/>
60
+ <use value="mobile"/>
61
+ <rank value="2"/>
62
+ </telecom>
63
+ <telecom>
64
+ <system value="phone"/>
65
+ <value value="(03) 5555 8834"/>
66
+ <use value="old"/>
67
+ <period>
68
+ <end value="2014"/>
69
+ </period>
70
+ </telecom>
71
+ <!-- use FHIR code system for male / female -->
72
+ <gender value="male"/>
73
+ <birthDate value="1974-12-25">
74
+ <extension url="http://hl7.org/fhir/StructureDefinition/patient-birthTime">
75
+ <valueDateTime value="1974-12-25T14:35:45-05:00"/>
76
+ </extension>
77
+ </birthDate>
78
+ <deceasedBoolean value="false"/>
79
+ <address>
80
+ <use value="home"/>
81
+ <type value="both"/>
82
+ <text value="534 Erewhon St PeasantVille, Rainbow, Vic 3999"/>
83
+ <line value="534 Erewhon St"/>
84
+ <city value="PleasantVille"/>
85
+ <district value="Rainbow"/>
86
+ <state value="Vic"/>
87
+ <postalCode value="3999"/>
88
+ <period>
89
+ <start value="1974-12-25"/>
90
+ </period>
91
+ </address>
92
+ <contact>
93
+ <relationship>
94
+ <coding>
95
+ <system value="http://terminology.hl7.org/CodeSystem/v2-0131"/>
96
+ <code value="N"/>
97
+ </coding>
98
+ </relationship>
99
+ <name>
100
+ <family value="du Marché">
101
+ <!-- the "du" part is a family name prefix (VV in iso 21090) -->
102
+ <extension url="http://hl7.org/fhir/StructureDefinition/humanname-own-prefix">
103
+ <valueString value="VV"/>
104
+ </extension>
105
+ </family>
106
+ <given value="Bénédicte"/>
107
+ </name>
108
+ <telecom>
109
+ <system value="phone"/>
110
+ <value value="+33 (237) 998327"/>
111
+ </telecom>
112
+ <address>
113
+ <use value="home"/>
114
+ <type value="both"/>
115
+ <line value="534 Erewhon St"/>
116
+ <city value="PleasantVille"/>
117
+ <district value="Rainbow"/>
118
+ <state value="Vic"/>
119
+ <postalCode value="3999"/>
120
+ <period>
121
+ <start value="1974-12-25"/>
122
+ </period>
123
+ </address>
124
+ <gender value="female"/>
125
+ <period>
126
+ <!-- The contact relationship started in 2012 -->
127
+ <start value="2012"/>
128
+ </period>
129
+ </contact>
130
+ <managingOrganization>
131
+ <reference value="Organization/1"/>
132
+ </managingOrganization>
133
+ </Patient>
Health_files2/summary/summary.pdf ADDED
Binary file (24.3 kB). View file
 
Health_files2/upload/Welcome.txt ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Welcome to the upload directory:
2
+ ##DARNA Health Intelligence v2.3- An open source intiative - self custody of your health data
3
+ ##Early stage. Beta and under development and isnt secure. Please take all steps to safeguard your data.
4
+
5
+ What is this project?
6
+ ======================
7
+ DARNA. HI v2.3 is a Forward looking project.
8
+
9
+ Key words: Self Hosted, LINUX, python, Open Source.
10
+
11
+ Vision: This will be an open source, linux based personal Health coach which by the way allows self custody data.
12
+
13
+ We are getting ready for the day when powerful Language models would run locally on your device and you can ask it in a very Privacy centered questions very specific to your health . In Darnahi v2.3 a language model (llama 1B) is being used to process personal health data and provide answers to questions specific to an individual's health situation. Additionally, natural language preprocessing (NLP) techniques are being used, along with a retrieval augmented architecture (RAG) to process and understand the health data by the local LLM.
14
+
15
+ In future we hope to have a trained lightweight large language model (LLM) being employed to interpret the processed data and generate relevant health intents or responses tailored to an individual's queries about their health. To summarize Darnahi aims to leverage advanced language AI capabilities to give personalized health insights and recommendations by analyzing someone's specific health information and answering their questions in that context.
16
+
17
+ The core components are NLP for understanding natural language, a RAG model for retrieving and processing relevant data, and a local LLM that ties it together by mapping the data to the queries and providing contextualized health-related output.
18
+
19
+ This project is an Free and open-source software that helps bring together your health data that is currently saved in different places like electronic health records, fitness apps, and wearable devices. We wish to provide a way to aggregate your health data. One stop to bring all your health data together on your secure computer at home. When you visit a new doctor, you can choose to share your encrypted health data with them on demand through QR code, email, link etc. This way, you have full custody of your health data and can decide who to share it with and what to do with it.
20
+
21
+ License?
22
+ ========
23
+ This program is free software; you can redistribute it and/or modify it under the terms of the Darna modified GNU General Public License as published by the Free Software Foundation. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You would of course have to credit Darnahi.
24
+
25
+
README.md CHANGED
@@ -1,8 +1,8 @@
1
  ---
2
- title: Health Server
3
- emoji: 🌖
4
- colorFrom: blue
5
- colorTo: green
6
  sdk: docker
7
  pinned: false
8
  license: other
 
1
  ---
2
+ title: Darnahi
3
+ emoji: ﮩـﮩﮩ٨ـ🫀ﮩ٨ـﮩﮩ٨ـ
4
+ colorFrom: purple
5
+ colorTo: gray
6
  sdk: docker
7
  pinned: false
8
  license: other
analyze.py ADDED
@@ -0,0 +1,987 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ #/* DARNA.HI
3
+ # * Copyright (c) 2023 Seapoe1809 <https://github.com/seapoe1809>
4
+ # * Copyright (c) 2023 pnmeka <https://github.com/pnmeka>
5
+ # *
6
+ # *
7
+ # * This program is free software: you can redistribute it and/or modify
8
+ # * it under the terms of the GNU General Public License as published by
9
+ # * the Free Software Foundation, either version 3 of the License, or
10
+ # * (at your option) any later version.
11
+ # *
12
+ # * This program is distributed in the hope that it will be useful,
13
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # * GNU General Public License for more details.
16
+ # *
17
+ # * You should have received a copy of the GNU General Public License
18
+ # * along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ import pytesseract
20
+ from pdf2image import convert_from_path
21
+ import os, subprocess
22
+ from variables import variables
23
+ from variables import variables2
24
+ import re
25
+ from PIL import Image, ImageFile
26
+ from datetime import datetime
27
+ import json
28
+ import fitz # PyMuPDF
29
+ import chromadb
30
+ from tqdm import tqdm
31
+
32
+ #from install_module.Analyze.pdf_sectionreader import *
33
+ #from install_module.Analyze.nlp_process import *
34
+
35
+
36
+ ImageFile.LOAD_TRUNCATED_IMAGES = True
37
+
38
+ HS_path = os.getcwd()
39
+
40
+ print(HS_path)
41
+ folderpath = os.environ.get('FOLDERPATH')
42
+ print("folderpath is", folderpath)
43
+
44
+
45
+ if folderpath:
46
+ ocr_files = f"{folderpath}/ocr_files"
47
+ else:
48
+ print("Session FOLDERPATH environment variable not set.")
49
+
50
+ APP_dir = f"{HS_path}/install_module"
51
+ ocr_files = f"{folderpath}/ocr_files"
52
+ upload_dir = f"{folderpath}/upload"
53
+ ip_address = variables.ip_address
54
+ age = variables2.age
55
+ sex = variables2.sex
56
+ try:
57
+ formatted_ignore_words = variables2.ignore_words if hasattr(variables2, 'ignore_words') else None
58
+ except NameError:
59
+ formatted_ignore_words = None
60
+
61
+
62
+ # Path to the Tesseract OCR executable (change this if necessary)
63
+ pytesseract.pytesseract.tesseract_cmd = '/usr/bin/tesseract'
64
+
65
+ ocr_files_dir = f'{ocr_files}/'
66
+
67
+ output_dir = os.path.join(ocr_files_dir, 'Darna_tesseract')
68
+ os.makedirs(output_dir, exist_ok=True)
69
+
70
+ # Define the patterns to identify and deidentify
71
+ # remove anything after keyword
72
+ KEYWORDS_REGEX = r'(?i)(?:Name|DOB|Date of birth|Birth|Address|Phone|PATIENT|Patient|MRN|Medical Record Number|APT|House|Street|ST|zip|pin):.*?(\n|$)'
73
+
74
+ # remove specific words
75
+ IGNORE_REGEX = rf'(?i)(?<!\bNO\b[-.,])(?:NO\b[-.]|[Nn][Oo]\b[-.,]|{formatted_ignore_words})'
76
+
77
+ KEYWORDS_REPLACE = r'\1REDACT'
78
+ # NAME_REGEX = r'\b(?!(?:NO\b|NO\b[-.]|[Nn][Oo]\b[-.,]))(?:[A-Z][a-z]+\s){1,2}(?:[A-Z][a-z]+)(?<!\b[A-Z]{2}\b)\b'
79
+
80
+ DOB_REGEX = r'\b(?!(?:NO\b|NO\b[-.]|[Nn][Oo]\b[-.,]))(?:0[1-9]|1[0-2])-(?:0[1-9]|[1-2]\d|3[0-1])-\d{4}\b'
81
+ SSN_REGEX = r'\b(?!(?:NO\b|NO\b[-.]|[Nn][Oo]\b[-.,]))(\d{3})-(\d{4})\b'
82
+ EMAIL_REGEX = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b'
83
+ ZIP_REGEX = r'\b(?!(?:NO\b|NOb[-.]|[Nn][Oo]\b[-.,]))([A-Z]{2}) (\d{5})\b'
84
+
85
+ def perform_ocr(image_path):
86
+ # Implementation of the perform_ocr function
87
+ try:
88
+ # Perform OCR using Tesseract
89
+ text = pytesseract.image_to_string(image_path)
90
+ return text
91
+ except pytesseract.TesseractError as e:
92
+ print(f"Error processing image: {image_path}")
93
+ print(f"Error message: {str(e)}")
94
+ return None
95
+
96
+ def convert_pdf_to_images(file_path):
97
+ # Implementation of the convert_pdf_to_images function
98
+ try:
99
+ # Convert PDF to images using pdf2image library
100
+ images = convert_from_path(file_path)
101
+ return images
102
+ except Exception as e:
103
+ print(f"Error converting PDF to images: {file_path}")
104
+ print(f"Error message: {str(e)}")
105
+ return None
106
+
107
+
108
+ def process_ocr_files(directory, age):
109
+ output_file = os.path.join(directory, 'ocr_results.txt') # Assuming you meant to define `directory` here.
110
+ with open(output_file, 'w') as f:
111
+ for root, dirs, files in os.walk(directory):
112
+ # Skip any paths that include the 'tesseract' directory
113
+ if 'tesseract' in root.split(os.sep):
114
+ continue
115
+
116
+ for file_name in files:
117
+ # Skip hidden files and non-image/non-PDF files explicitly
118
+ if file_name.startswith('.') or not file_name.lower().endswith(('.pdf', '.jpg', '.jpeg', '.png')):
119
+ continue
120
+
121
+ file_path = os.path.join(root, file_name)
122
+ if os.path.isfile(file_path):
123
+ if file_name.lower().endswith('.pdf'):
124
+ images = convert_pdf_to_images(file_path)
125
+ if images is not None:
126
+ for i, image in enumerate(images):
127
+ text = perform_ocr(image)
128
+ if text:
129
+ f.write(f"File: {file_name}, Page: {i+1}\n")
130
+ f.write(text)
131
+ f.write('\n\n')
132
+ image.close()
133
+ else:
134
+ # Assuming perform_ocr can handle image files directly
135
+ text = perform_ocr(file_path)
136
+ if text:
137
+ f.write(f"File: {file_name}\n")
138
+ f.write(text)
139
+ f.write('\n\n')
140
+
141
+ print('OCR completed. Results saved in', output_file)
142
+
143
+
144
+ def add_deidentification_tags(text):
145
+ return f'Deidentified Entry | {datetime.now().strftime("%m/%d/%Y")}\n{text}'
146
+
147
+ def generate_fake_text(match):
148
+ return re.sub(KEYWORDS_REGEX, KEYWORDS_REPLACE, match.group())
149
+
150
+ def redact_zip_and_words(match):
151
+ words = match.group(1)
152
+ zip_code = match.group(2)
153
+ redacted_words = 'XX ' * min(4, len(words.split()))
154
+ redacted_zip = re.sub(r'\b\d{5}\b', '11111', zip_code)
155
+ return redacted_words + redacted_zip
156
+
157
+ def deidentify_records(ocr_files, formatted_ignore_words):
158
+ try:
159
+ os.makedirs(os.path.dirname(f'{ocr_files}/ocr_results.txt'), exist_ok=True)
160
+ try:
161
+ with open(f'{ocr_files}/ocr_results.txt') as f:
162
+ text = f.read()
163
+ except FileNotFoundError:
164
+ with open(f'{ocr_files}/ocr_results.txt', 'w') as f:
165
+ pass
166
+ text = ""
167
+
168
+ # remove specific words
169
+ IGNORE_REGEX = rf'(?i)(?<!\bNO\b[-.,])(?:NO\b[-.]|[Nn][Oo]\b[-.,]|{deidentify_words})'
170
+
171
+
172
+ redacted = re.sub(KEYWORDS_REGEX, generate_fake_text, text, flags=re.IGNORECASE)
173
+ redacted = re.sub(IGNORE_REGEX, '', redacted)
174
+ redacted = re.sub(DOB_REGEX, '', redacted)
175
+ redacted = re.sub(SSN_REGEX, '', redacted)
176
+ redacted = re.sub(EMAIL_REGEX, '', redacted)
177
+ redacted = re.sub(ZIP_REGEX, redact_zip_and_words, redacted)
178
+
179
+ tagged = add_deidentification_tags(redacted)
180
+
181
+ with open(f'{ocr_files}/Darna_tesseract/deidentified_records.txt', 'w') as f:
182
+ f.write(tagged)
183
+ print("Deidentified records printed with user input")
184
+ except Exception as e:
185
+ return f"Error in deidentification process: {str(e)}"
186
+
187
+
188
+ def collate_images(input_dir, output_dir):
189
+ images = []
190
+ for root, dirs, files in os.walk(input_dir):
191
+ # Skip processing files in the '<tesseract>' subdirectory
192
+ if os.path.basename(root) == 'Darna_tesseract':
193
+ continue
194
+
195
+ for file in files:
196
+ # Skip all .txt files
197
+ if file.lower().endswith('.txt'):
198
+ continue
199
+
200
+ file_path = os.path.join(root, file)
201
+ try:
202
+ if file.lower().endswith(('.jpg', '.jpeg', '.png', '.gif')):
203
+ img = Image.open(file_path)
204
+ if img.size[0] > 0 and img.size[1] > 0: # Check if the image is not empty
205
+ images.append(img)
206
+ img.close()
207
+ elif file.lower().endswith(('.pdf', '.PDF')):
208
+ pdf_images = convert_pdf_to_images(file_path)
209
+ if pdf_images is not None:
210
+ for pdf_img in pdf_images:
211
+ if pdf_img.size[0] > 0 and pdf_img.size[1] > 0: # Check if the image is not empty
212
+ images.append(pdf_img)
213
+ # No need to close PIL Images created from bytes
214
+ except Exception as e:
215
+ print(f"Error processing image: {file_path}")
216
+ print(f"Error message: {str(e)}")
217
+ continue
218
+
219
+ def get_recommendations(age=None, sex=None, ancestry=None, pack_years=None, smoking=None, quit_within_past_15_years=None, overweight_or_obesity=None, cardiovascular_risk=None, cardiovascular_risk_7_5_to_10=None, rh_d_negative=None, pregnant=None, new_mother=None, substance_abuse_risk=None, skin_type=None):
220
+ recommendations = []
221
+ # Set default values when not specified
222
+ if ancestry is None:
223
+ ancestry = "not None"
224
+ if pack_years is None:
225
+ pack_years = 5
226
+ if smoking is None:
227
+ smoking = "not None"
228
+ if quit_within_past_15_years is None:
229
+ quit_within_past_15_years = "not None"
230
+ if overweight_or_obesity is None:
231
+ overweight_or_obesity = "not None"
232
+ if cardiovascular_risk is None:
233
+ cardiovascular_risk = "not None"
234
+ if rh_d_negative is None:
235
+ rh_d_negative = "not None"
236
+ if cardiovascular_risk_7_5_to_10 is None:
237
+ cardiovascular_risk_7_5_to_10 = "not None"
238
+ if substance_abuse_risk is None:
239
+ substance_abuse_risk = "not None"
240
+ if skin_type is None:
241
+ skin_type = "not None"
242
+
243
+ # B - Recommended (39)
244
+ if (sex == 'female') and (age is not None) and (age >= 21 and age <= 65):
245
+ recommendations.append("Pap Smear: Cervical Cancer: Screening -- Women aged 21 to 65 years")
246
+ if age is not None and (age >= 50 and age <= 75):
247
+ recommendations.append("Colonoscopy: Colorectal Cancer: Screening -- Adults aged 50 to 75 years")
248
+ if age is not None and (age >= 18):
249
+ recommendations.append("BP: Blood pressure screening in office screening -- Adults aged 18 years and above")
250
+ if sex == 'female' and age >= 45:
251
+ recommendations.append("Coronary Risk: Screening women aged 45 and older for lipid disorders if they are at increased risk for coronary heart disease.")
252
+ if sex == 'male' and age >= 35:
253
+ recommendations.append("Fasting Lipid: Screening Men aged 35 and older for lipid disorders with fasting lipid profile.")
254
+ if sex == 'female' and (ancestry is not None):
255
+ recommendations.append("BRCA: BRCA-Related Cancer: Risk Assessment, Genetic Counseling, and Genetic Testing -- Women with a personal or family history of breast, ovarian, tubal, or peritoneal cancer or an ancestry associated with BRCA1/2 gene mutation")
256
+ if sex == 'female' and age >= 35:
257
+ recommendations.append("Breast Cancer: Medication Use to Reduce Risk -- Women at increased risk for breast cancer aged 35 years or older")
258
+ if (sex == 'female') and age is not None and (age >= 50 and age <= 74):
259
+ recommendations.append("Mammogram: Breast Cancer: Screening -- Women aged 50 to 74 years")
260
+ if (sex == 'female' or (new_mother is not None and new_mother)):
261
+ recommendations.append("Breastfeeding: Primary Care Interventions -- Pregnant women, new mothers, and their children")
262
+ if sex == 'female':
263
+ recommendations.append("Sti screen: Chlamydia and Gonorrhea: Screening -- Sexually active women, including pregnant persons")
264
+ if age is not None and (age >= 45 and age <= 49):
265
+ recommendations.append("Colonoscopy: Colorectal Cancer: Screening -- Adults aged 45 to 49 years")
266
+ if age is not None and (age >= 8 and age <= 18):
267
+ recommendations.append("Anxiety Questionnaire: Anxiety in Children and Adolescents: Screening -- Children and adolescents aged 8 to 18 years")
268
+ if (sex == 'pregnant' or (pregnant is not None and pregnant)):
269
+ recommendations.append("Aspirin for High Risk: Aspirin Use to Prevent Preeclampsia and Related Morbidity and Mortality: Preventive Medication -- Pregnant persons at high risk for preeclampsia")
270
+ if sex == 'pregnant':
271
+ recommendations.append("Urinalysis: Asymptomatic Bacteriuria in Adults: Screening -- Pregnant persons")
272
+ if sex == 'male' and (ancestry is not None):
273
+ recommendations.append("Brca Gene Test: BRCA-Related Cancer: If screen positive, risk Assessment, Genetic Counseling, and Genetic Testing -- Men with a personal or family history of breast, ovarian, tubal, or peritoneal cancer or an ancestry associated with BRCA1/2 gene mutation")
274
+ if sex == 'male' and age >= 65 and (pack_years is not None and pack_years > 0):
275
+ recommendations.append("Ultrasound Doppler Abdomen: Abdominal Aortic Aneurysm: Screening -- Men aged 65 to 75 years who have ever smoked")
276
+ if age is not None and (age >= 12 and age <= 18):
277
+ recommendations.append("Depression Screen Questionnaire: Depression and Suicide Risk in Children and Adolescents: Screening -- Adolescents aged 12 to 18 years")
278
+ if age is not None and (age >= 65):
279
+ recommendations.append("Falls Screen Questionnaire: Falls Prevention in Community-Dwelling Older Adults: Interventions -- Adults 65 years or older")
280
+ if (sex == 'pregnant' or (pregnant is not None and pregnant)) and (age is not None and (age >= 24)):
281
+ recommendations.append("Fasting Blood Glucose: Gestational Diabetes: Screening -- Asymptomatic pregnant persons at 24 weeks of gestation or after")
282
+ if overweight_or_obesity is not None:
283
+ recommendations.append("Bmi screen: If elevated BMI consider Healthy Diet and Physical Activity for Cardiovascular Disease Prevention in Adults With Cardiovascular Risk Factors: Behavioral Counseling Interventions -- Adults with cardiovascular disease risk factors")
284
+ if (sex == 'pregnant' or (pregnant is not None and pregnant)):
285
+ recommendations.append("Weight Trend: Healthy Weight and Weight Gain In Pregnancy: Behavioral Counseling Interventions -- Pregnant persons")
286
+ if sex == 'female' and (age is not None and (age >= 18)):
287
+ recommendations.append("Hepatitis B Blood Test: Hepatitis B Virus Infection in Adolescents and Adults: Screening -- Adolescents and adults at increased risk for infection")
288
+ if sex == 'male' and (age is not None and (age >= 18 and age <= 79)):
289
+ recommendations.append("Hepatitis C Blood Test: Hepatitis C Virus Infection in Adolescents and Adults: Screening -- Adults aged 18 to 79 years")
290
+ if sex == 'female' and (age is not None and (age >= 14)):
291
+ recommendations.append("Violence Questionnaire screen: Intimate Partner Violence, Elder Abuse, and Abuse of Vulnerable Adults: Screening -- Women of reproductive age")
292
+ if age is not None and (age >= 6 and age <= 60):
293
+ recommendations.append("Tb Screen Test/ Questionnaire: Latent Tuberculosis Infection in Adults: Screening -- Asymptomatic adults at increased risk of latent tuberculosis infection (LTBI)")
294
+ if (sex == 'male' or (sex == 'female' and (pregnant is not None and pregnant))) and (age is not None and (age >= 50 and age <= 80) and (pack_years is not None) and (smoking is not None)):
295
+ recommendations.append("Ct Chest: Lung Cancer screening if you smoked more that 20 pack years: Screening -- Adults aged 50 to 80 years who have a 20 pack-year smoking history and currently smoke or have quit within the past 15 years")
296
+ if age is not None and (age >= 6 and age <= 18):
297
+ recommendations.append("Bmi Screen: Obesity in Children and Adolescents: Screening -- Children and adolescents 6 years and older")
298
+ if sex == 'female' and (age is not None and (age < 65)):
299
+ recommendations.append("Dexa Bone Test: Osteoporosis to Prevent Fractures: Screening -- Postmenopausal women younger than 65 years at increased risk of osteoporosis")
300
+ if sex == 'female' and (age is not None and (age >= 65)):
301
+ recommendations.append("Dexa Bone Test: Osteoporosis to Prevent Fractures: Screening -- Women 65 years and older")
302
+ if (sex == 'pregnant' or (pregnant is not None and pregnant) or (new_mother is not None)):
303
+ recommendations.append("Depression Questionnaire: Perinatal Depression: Preventive Interventions -- Pregnant and postpartum persons")
304
+ if age is not None and (age >= 35 and age <= 70):
305
+ recommendations.append("Fasting Blood Glucose: Prediabetes and Type 2 Diabetes: Screening -- Asymptomatic adults aged 35 to 70 years who have overweight or obesity")
306
+ if (sex == 'pregnant' or (pregnant is not None and pregnant)):
307
+ recommendations.append("Bp, Questionnaire and Urine test: Preeclampsia: Screening -- Pregnant woman")
308
+ if age is not None and (age < 5):
309
+ recommendations.append("Oral Exam: Prevention of Dental Caries in Children Younger Than 5 Years: Screening and Interventions -- Children younger than 5 years")
310
+ if (sex == 'female' or (pregnant is not None and pregnant)) or (new_mother is not None):
311
+ recommendations.append("Oral Exam: Prevention of Dental Caries in Children Younger Than 5 Years: Screening and Interventions -- Children younger than 5 years")
312
+ if (sex == 'pregnant' or (pregnant is not None and pregnant)) and (rh_d_negative is not None):
313
+ recommendations.append("Rh Blood Test: Rh(D) Incompatibility especially with Rh negative: Screening -- Unsensitized Rh(D)-negative pregnant women")
314
+ if sex == 'male' or (sex == 'female' and (pregnant is not None and pregnant) or (new_mother is not None and new_mother)):
315
+ recommendations.append("Depression Questionnaire: Screening for Depression in Adults -- General adult population")
316
+ if sex == 'male' or (sex == 'female' and (pregnant is not None and pregnant)) or (new_mother is not None):
317
+ recommendations.append("Sti Screen: Sexually Transmitted Infections: Behavioral Counseling -- Sexually active adolescents and adults at increased risk")
318
+ if (age is not None and (age >= 25)) or (new_mother is not None) or (sex == 'male' and (substance_abuse_risk is not None)):
319
+ recommendations.append("Skin Exam: Skin Cancer Prevention: Behavioral Counseling -- Adults, Young adults, adolescents, children, and parents of young children")
320
+ if (age is not None and (age >= 40 and age <= 75)) and (cardiovascular_risk is not None) and (cardiovascular_risk_7_5_to_10 is not None):
321
+ recommendations.append("Heart Disease Questionnaire: Screen for CV risk and consider Statin Use for the Primary Prevention of Cardiovascular Disease in Adults: Preventive Medication -- Adults aged 40 to 75 years who have 1 or more cardiovascular risk factors and an estimated 10-year cardiovascular disease (CVD) risk of 10% or greater")
322
+ if sex == 'female' and (pregnant is not None and pregnant) and (ancestry is not None and ancestry == 'BRCA1/2 gene mutation'):
323
+ recommendations.append("Family History and Brca Test: BRCA-Related Cancer: Risk Assessment, Genetic Counseling, and Genetic Testing -- Women with a personal or family history of breast, ovarian, tubal, or peritoneal cancer or an ancestry associated with BRCA1/2 gene mutation")
324
+ if (age is not None and (age >= 6 and age <= 18)) or (sex == 'pregnant' or (pregnant is not None and pregnant)):
325
+ recommendations.append("Tobacco Questionnaire: Tobacco Use in Children and Adolescents: Primary Care Interventions -- School-aged children and adolescents who have not started to use tobacco")
326
+ if age is not None and (age >= 18) and (substance_abuse_risk is not None):
327
+ recommendations.append("Alcohol Questionnaire: Unhealthy Alcohol Use in Adolescents and Adults: Screening and Behavioral Counseling Interventions -- Adults 18 years or older, including pregnant women")
328
+ if age is not None and (age >= 13):
329
+ recommendations.append("Drug Abuse Questionnaire: Unhealthy Drug Use: Screening -- Adults age 13 years or older")
330
+ if age is not None and (age > 2 and age < 24) and skin_type is not None:
331
+ recommendations.append("Skin Exam: Skin Cancer: Counseling -- Fair-skinned individuals aged 6 months to 24 years with a family history of skin cancer or personal history of skin cancer, or who are at increased risk of skin cancer")
332
+
333
+ return recommendations
334
+
335
+
336
+ def generate_recommendations(age=None, sex=None):
337
+ age = f"{age}"
338
+ try:
339
+ age = int(age)
340
+ except ValueError:
341
+ print("Invalid age value. Age must be a valid integer.")
342
+
343
+ sex = f"{sex}"
344
+
345
+ recommendations = get_recommendations(age, sex)
346
+ # Adding subheading
347
+ subheading = f"The USPTF recommendations for {age}/{sex} are:"
348
+ subheading = f"RECOMMENDATIONS:"
349
+ recommendations_with_subheading = [subheading] + recommendations
350
+
351
+ with open(f'{ocr_files}/Darna_tesseract/USPTF_Intent.txt', 'w') as file:
352
+ file.write('\n\n\n'.join(recommendations_with_subheading))
353
+ doc = fitz.open() # Create a new PDF
354
+ page = doc.new_page()
355
+ text = "\n\n\n".join(recommendations_with_subheading)
356
+ page.insert_text((72, 72), text)
357
+ doc.save(f'{ocr_files}/USPTF.pdf') # Save the PDF
358
+ doc.close()
359
+
360
+ #extract data from the updated fhir file
361
+
362
+ def extract_lforms_data(json_data):
363
+ if isinstance(json_data, str):
364
+ data = json.loads(json_data)
365
+ else:
366
+ data = json_data
367
+
368
+ extracted_info = {
369
+ "date_of_birth": None,
370
+ "sex": None,
371
+ "allergies": [],
372
+ "past_medical_history": [],
373
+ "medications": []
374
+ }
375
+
376
+ for item in data.get("items", []):
377
+ if item.get("question") == "ABOUT ME":
378
+ for subitem in item.get("items", []):
379
+ if subitem.get("question") == "DATE OF BIRTH":
380
+ extracted_info["date_of_birth"] = subitem.get("value")
381
+ elif subitem.get("question") == "BIOLOGICAL SEX":
382
+ extracted_info["sex"] = subitem.get("value", {}).get("text")
383
+
384
+ elif item.get("question") == "ALLERGIES":
385
+ for allergy_item in item.get("items", []):
386
+ if allergy_item.get("question") == "Allergies and Other Dangerous Reactions":
387
+ for subitem in allergy_item.get("items", []):
388
+ if subitem.get("question") == "Name" and "value" in subitem:
389
+ extracted_info["allergies"].append(subitem["value"]["text"])
390
+
391
+ elif item.get("question") == "PAST MEDICAL HISTORY:":
392
+ for condition_item in item.get("items", []):
393
+ if condition_item.get("question") == "PAST MEDICAL HISTORY" and "value" in condition_item:
394
+ condition = extract_condition(condition_item)
395
+ if condition:
396
+ extracted_info["past_medical_history"].append(condition)
397
+
398
+ elif item.get("question") == "MEDICATIONS:":
399
+ medication = {}
400
+ for med_item in item.get("items", []):
401
+ if med_item.get("question") == "MEDICATIONS":
402
+ medication["name"] = extract_med_value(med_item)
403
+ elif med_item.get("question") == "Strength":
404
+ medication["strength"] = extract_med_value(med_item)
405
+ elif med_item.get("question") == "Instructions":
406
+ medication["instructions"] = extract_med_value(med_item)
407
+ if medication:
408
+ extracted_info["medications"].append(medication)
409
+
410
+ return extracted_info
411
+
412
+
413
+ def extract_condition(condition_item):
414
+ if isinstance(condition_item.get("value"), dict):
415
+ return condition_item["value"].get("text", "")
416
+ elif isinstance(condition_item.get("value"), str):
417
+ return condition_item["value"]
418
+ return ""
419
+
420
+ def extract_med_value(med_item):
421
+ if "value" not in med_item:
422
+ return ""
423
+ value = med_item["value"]
424
+ if isinstance(value, str):
425
+ return value
426
+ elif isinstance(value, dict):
427
+ return value.get("text", "")
428
+ return ""
429
+
430
+
431
+ #######
432
+ ###nlp_process.py functions
433
+
434
+
435
+ import json
436
+ import nltk
437
+ import re, os
438
+ from wordcloud import WordCloud
439
+ import matplotlib.pyplot as plt
440
+ from nltk.corpus import stopwords
441
+ from nltk.tokenize import word_tokenize
442
+
443
+
444
+
445
+ # Ensure NLTK components are downloaded
446
+ #nltk.download('punkt')
447
+ #nltk.download('stopwords')
448
+
449
+ #convert text to lowercase and remove fillers
450
+ def normalize_text(text):
451
+ # Convert text to lowercase and remove ':' and '-'
452
+ return re.sub('[: -]', '', text.lower())
453
+
454
+ def condense_summary_to_tokens(text, token_limit=300):
455
+ tokens = word_tokenize(text)
456
+ # Select the first 'token_limit' tokens
457
+ limited_tokens = tokens[:token_limit]
458
+ # Reconstruct the text from these tokens
459
+ condensed_text = ' '.join(limited_tokens)
460
+ return condensed_text
461
+
462
+ #write all to a json summary file
463
+ def wordcloud_summary(keys, texts, directory):
464
+ output_file = f'{directory}/wordcloud_summary.json'
465
+ wordcloud_dir = f'{directory}/wordclouds'
466
+
467
+ try:
468
+ with open(output_file, 'r', encoding='utf-8') as file:
469
+ existing_data = json.load(file)
470
+ except FileNotFoundError:
471
+ existing_data = {}
472
+
473
+ # Ensure the directories exist
474
+ os.makedirs(os.path.dirname(output_file), exist_ok=True)
475
+ os.makedirs(wordcloud_dir, exist_ok=True)
476
+
477
+ for i, key in enumerate(keys):
478
+ if i < len(texts):
479
+ text = texts[i]
480
+ # Check if the text contains any words
481
+ if text.strip():
482
+ existing_data[key] = text
483
+
484
+ # Attempt to generate word cloud
485
+ try:
486
+ # Split the text into words
487
+ words = text.split()
488
+
489
+ # Check if there are enough words
490
+ if len(words) > 1:
491
+ wordcloud = WordCloud(width=800, height=400, background_color='white').generate(text)
492
+
493
+ # Save the word cloud
494
+ plt.figure(figsize=(10, 5))
495
+ plt.imshow(wordcloud, interpolation='bilinear')
496
+ plt.axis('off')
497
+ plt.title(f'Word Cloud for {key}')
498
+ plt.savefig(f'{wordcloud_dir}/{key}_wordcloud.png')
499
+ plt.close()
500
+
501
+ print(f"Generated word cloud for key: {key}")
502
+ else:
503
+ print(f"Not enough words to generate word cloud for key: {key}")
504
+ except Exception as e:
505
+ print(f"Error generating word cloud for key {key}: {str(e)}")
506
+ else:
507
+ print(f"Skipping empty text for key: {key}")
508
+ else:
509
+ print(f"No text available for key: {key}")
510
+
511
+ with open(output_file, 'w', encoding='utf-8') as file:
512
+ json.dump(existing_data, file, indent=4, ensure_ascii=False)
513
+
514
+
515
+ #generate list of meds from the files
516
+ def load_text_from_json_meds(json_file_path, keys):
517
+ normalized_keys = [normalize_text(key) for key in keys]
518
+ with open(json_file_path, 'r') as file:
519
+ data = json.load(file)
520
+ text = []
521
+ for json_key, value in data.items():
522
+ normalized_json_key = normalize_text(json_key)
523
+ if any(normalized_key in normalized_json_key for normalized_key in normalized_keys):
524
+ if isinstance(value, str):
525
+ text.append(value)
526
+ elif isinstance(value, list):
527
+ text.extend(str(item) for item in value if item)
528
+ elif isinstance(value, dict):
529
+ text.extend(str(item) for item in value.values() if item)
530
+ else:
531
+ text.append(str(value))
532
+
533
+ combined_text = ' '.join(text)
534
+ combined_text = condense_summary_to_tokens(combined_text, 300)
535
+ return combined_text
536
+
537
+ #generate a list of past medical history from the files
538
+
539
+ def load_text_from_json_pmh(json_file_path, keys):
540
+ normalized_keys = [normalize_text(key) for key in keys]
541
+ with open(json_file_path, 'r') as file:
542
+ data = json.load(file)
543
+ text = []
544
+ for json_key, value in data.items():
545
+ normalized_json_key = normalize_text(json_key)
546
+ if any(normalized_key in normalized_json_key for normalized_key in normalized_keys):
547
+ if isinstance(value, str):
548
+ text.append(value)
549
+ elif isinstance(value, list):
550
+ text.extend(str(item) for item in value if item)
551
+ elif isinstance(value, dict):
552
+ text.extend(str(item) for item in value.values() if item)
553
+ else:
554
+ text.append(str(value))
555
+
556
+ combined_text = ' '.join(text)
557
+ combined_text = condense_summary_to_tokens(combined_text, 300)
558
+ return combined_text
559
+
560
+ #generate a list of screening items from the USPTF file
561
+ def load_text_from_json_screening(json_file_path, keys):
562
+ normalized_keys = [normalize_text(key) for key in keys]
563
+ with open(json_file_path, 'r') as file:
564
+ data = json.load(file)
565
+ text = []
566
+ for json_key, value in data.items():
567
+ normalized_json_key = normalize_text(json_key)
568
+ if any(normalized_key in normalized_json_key for normalized_key in normalized_keys):
569
+ text.append(value)
570
+ combined_text_screening=' '.join(text)
571
+ #print (combined_text_screening)
572
+
573
+ return combined_text_screening
574
+
575
+ def load_text_from_json_summary(json_file_path, keys):
576
+ normalized_keys = [normalize_text(key) for key in keys]
577
+ with open(json_file_path, 'r') as file:
578
+ data = json.load(file)
579
+ text = []
580
+ for json_key, value in data.items():
581
+ normalized_json_key = normalize_text(json_key)
582
+ if any(normalized_key in normalized_json_key for normalized_key in normalized_keys):
583
+ if isinstance(value, str):
584
+ text.append(value)
585
+ elif isinstance(value, list):
586
+ text.extend(str(item) for item in value if item)
587
+ elif isinstance(value, dict):
588
+ text.extend(str(item) for item in value.values() if item)
589
+ else:
590
+ text.append(str(value))
591
+
592
+ combined_text = ' '.join(text)
593
+ combined_text = condense_summary_to_tokens(combined_text, 300)
594
+ return combined_text
595
+
596
+ #iterate json files in directory and call function above
597
+ def process_directory_summary(directory, keys):
598
+ combined_texts = []
599
+ for filename in os.listdir(directory):
600
+ if filename.endswith('.json'):
601
+ file_path = os.path.join(directory, filename)
602
+ print(file_path)
603
+ combined_text = load_text_from_json_summary(file_path, keys)
604
+ if combined_text: # Only add non-empty strings
605
+ combined_texts.append(combined_text)
606
+
607
+
608
+ # Combine all texts into one
609
+ final_combined_text = ' '.join(combined_texts)
610
+ return final_combined_text
611
+
612
+ #iterate json files in directory and summarize meds
613
+ def process_directory_meds(directory, keys):
614
+ combined_texts = []
615
+ for filename in os.listdir(directory):
616
+ if filename.endswith('.json'):
617
+ file_path = os.path.join(directory, filename)
618
+ print(file_path)
619
+ combined_text = load_text_from_json_meds(file_path, keys)
620
+ combined_texts.append(combined_text)
621
+
622
+
623
+ # Combine all texts into one
624
+ final_combined_text = ' '.join(combined_texts)
625
+ return final_combined_text
626
+
627
+ #iterate json files in directory and summarize past medical
628
+ def process_directory_pmh(directory, keys):
629
+ combined_texts = []
630
+ for filename in os.listdir(directory):
631
+ if filename.endswith('.json'):
632
+ file_path = os.path.join(directory, filename)
633
+ print(file_path)
634
+ combined_text = load_text_from_json_pmh(file_path, keys)
635
+ combined_texts.append(combined_text)
636
+
637
+
638
+ # Combine all texts into one
639
+ final_combined_text = ' '.join(combined_texts)
640
+ return final_combined_text
641
+
642
+ def preprocess_and_create_wordcloud(text, directory):
643
+ # Tokenize and remove stopwords
644
+ stop_words = set(stopwords.words('english'))
645
+ words = word_tokenize(text)
646
+ filtered_words = [word for word in words if word.isalnum() and word.lower() not in stop_words]
647
+
648
+ # Check if there are any words left after filtering
649
+ if not filtered_words:
650
+ print("No words left after preprocessing. Skipping word cloud creation.")
651
+ return
652
+
653
+ processed_text = ' '.join(filtered_words)
654
+
655
+ # Create and display the word cloud
656
+ wordcloud = WordCloud(width=800, height=800, background_color='white').generate(processed_text)
657
+ plt.figure(figsize=(8, 8), facecolor=None)
658
+ plt.imshow(wordcloud)
659
+ plt.axis("off")
660
+ plt.tight_layout(pad=0)
661
+ plt.tight_layout(pad = 0)
662
+
663
+ # Display the word cloud
664
+ #plt.show()
665
+
666
+ # Save the word cloud image
667
+ plt.savefig(f'{directory}darnahi_ocr.png')
668
+
669
+ #############
670
+
671
+ pattern = r"\d+\..+?(\d{4};\d+\(\d+\):\d+–\d+\. DOI: .+?\.|.+?ed\., .+?: .+?; \d{4}\. \d+–\d+\.)"
672
+
673
+ class Document:
674
+ def __init__(self, page_content, metadata):
675
+ self.page_content = page_content
676
+ self.metadata = metadata
677
+
678
+ def process_pdf(file_path, chunk_size=350):
679
+ try:
680
+ doc = fitz.open(file_path)
681
+ full_text = ""
682
+ for page in doc:
683
+ text_blocks = page.get_text("dict")["blocks"]
684
+ for block in text_blocks:
685
+ if 'text' in block:
686
+ text = block['text'].strip()
687
+ if text:
688
+ full_text += text + "\n"
689
+ chunks = [full_text[i:i+chunk_size] for i in range(0, len(full_text), chunk_size)]
690
+ return chunks
691
+ except Exception as e:
692
+ print(f"An error occurred: {str(e)}")
693
+ return []
694
+
695
+ def process_json(input_file):
696
+ try:
697
+ with open(input_file, 'r', encoding='utf-8') as file:
698
+ existing_data = json.load(file)
699
+ except FileNotFoundError:
700
+ print("File not found.")
701
+ return []
702
+ semantic_snippets = []
703
+ for heading, content in existing_data.items():
704
+ metadata = {'heading': heading, 'file': input_file}
705
+ doc = Document(page_content=content, metadata=metadata)
706
+ semantic_snippets.append(doc)
707
+ return semantic_snippets
708
+
709
+ def process_files(directory):
710
+ all_semantic_snippets = []
711
+ for filename in os.listdir(directory):
712
+ file_path = os.path.join(directory, filename)
713
+ if filename.endswith('.pdf'):
714
+ snippets = process_pdf(file_path)
715
+ all_semantic_snippets.extend(snippets)
716
+ elif filename.endswith('.json'):
717
+ semantic_snippets = process_json(file_path)
718
+ all_semantic_snippets.extend(semantic_snippets)
719
+ return all_semantic_snippets
720
+
721
+ def chromadb_embed(directory, collection_name="documents_collection"):
722
+ persist_directory = os.path.join(directory, 'Darna_tesseract', 'chroma_storage')
723
+ os.makedirs(persist_directory, exist_ok=True)
724
+ all_semantic_snippets = str(process_files(directory))
725
+ client = chromadb.PersistentClient(path=persist_directory)
726
+ collection = client.get_or_create_collection(name=collection_name)
727
+ count = collection.count()
728
+ print(f"Collection already contains {count} documents")
729
+ ids = [str(i) for i in range(count, count + len(all_semantic_snippets))]
730
+ for i in tqdm(range(0, len(all_semantic_snippets), 100), desc="Adding documents"):
731
+ batch_snippets = all_semantic_snippets[i:i+100]
732
+ batch_metadatas = []
733
+ for snippet in batch_snippets:
734
+ metadata = {"filename": "summary", "heading": "summary_heading"} if not isinstance(snippet, Document) else snippet.metadata
735
+ batch_metadatas.append(metadata)
736
+ collection.add(ids=ids[i:i+100], documents=[s if isinstance(s, str) else s.page_content for s in batch_snippets], metadatas=batch_metadatas)
737
+ new_count = collection.count()
738
+ print(f"Added {new_count - count} documents")
739
+
740
+ #######################################
741
+ #########pdf_sectionreader.py
742
+ import os
743
+ import fitz
744
+ import pandas as pd
745
+ import json
746
+ from unidecode import unidecode
747
+
748
+ global_heading_content_dict = {} # Global dictionary to accumulate data
749
+
750
+ def process_pdf_files(directory):
751
+ for filename in os.listdir(directory):
752
+ if filename.endswith('.pdf'):
753
+ file_path = os.path.join(directory, filename)
754
+ with fitz.open(file_path) as doc:
755
+ print(f"Processing {filename}...")
756
+ extract_and_tag_text(doc)
757
+
758
+ # Generate and save output after processing all files
759
+ generate_output(global_heading_content_dict, directory)
760
+
761
+ def extract_and_tag_text(doc):
762
+ block_dict, page_num = {}, 1
763
+ for page in doc:
764
+ file_dict = page.get_text('dict')
765
+ block = file_dict['blocks']
766
+ block_dict[page_num] = block
767
+ page_num += 1
768
+
769
+ rows = []
770
+ for page_num, blocks in block_dict.items():
771
+ for block in blocks:
772
+ if block['type'] == 0:
773
+ for line in block['lines']:
774
+ for span in line['spans']:
775
+ xmin, ymin, xmax, ymax = list(span['bbox'])
776
+ font_size = span['size']
777
+ text = unidecode(span['text'])
778
+ span_font = span['font']
779
+ is_upper = text.isupper()
780
+ is_bold = "bold" in span_font.lower()
781
+
782
+ if text.strip() != "":
783
+ rows.append((xmin, ymin, xmax, ymax, text, is_upper, is_bold, span_font, font_size))
784
+
785
+ span_df = pd.DataFrame(rows, columns=['xmin', 'ymin', 'xmax', 'ymax', 'text', 'is_upper', 'is_bold', 'span_font', 'font_size'])
786
+ common_font_size = span_df['font_size'].mode().iloc[0]
787
+ span_df['tag'] = span_df.apply(assign_tag, axis=1, common_font_size=common_font_size)
788
+
789
+ update_global_dict(span_df)
790
+
791
+ def assign_tag(row, common_font_size):
792
+ if any(char.isdigit() for char in row['text']):
793
+ return 'p'
794
+ elif row['font_size'] > common_font_size and row['is_bold'] and row['is_upper']:
795
+ return 'h1'
796
+ elif row['is_bold'] or row['is_upper'] or row['font_size'] > common_font_size:
797
+ return 'h2'
798
+ else:
799
+ return 'p'
800
+
801
+ def update_global_dict(span_df):
802
+ tmp = []
803
+ current_heading = None
804
+
805
+ for index, span_row in span_df.iterrows():
806
+ text, tag = span_row.text.strip(), span_row.tag
807
+ if 'h' in tag:
808
+ if current_heading is not None:
809
+ existing_text = global_heading_content_dict.get(current_heading, "")
810
+ global_heading_content_dict[current_heading] = existing_text + '\n'.join(tmp).strip()
811
+ current_heading = text
812
+ tmp = []
813
+ else:
814
+ tmp.append(text)
815
+
816
+ if current_heading is not None:
817
+ existing_text = global_heading_content_dict.get(current_heading, "")
818
+ global_heading_content_dict[current_heading] = existing_text + '\n'.join(tmp).strip()
819
+
820
+
821
+
822
+ def generate_output(heading_content_dict, directory):
823
+ text_df = pd.DataFrame(list(heading_content_dict.items()), columns=['heading', 'content'])
824
+ #text_df.to_excel(f'{directory}/combined_output.xlsx', index=False, engine='openpyxl')
825
+
826
+ json_data = json.dumps(heading_content_dict, indent=4, ensure_ascii=False)
827
+ with open(f'{directory}/Darna_tesseract/combined_output.json', 'w', encoding='utf-8') as f:
828
+ f.write(json_data)
829
+ with open(f'{directory}/combined_output.json', 'w', encoding='utf-8') as f:
830
+ f.write(json_data)
831
+
832
+
833
+
834
+
835
+
836
+
837
+ ###########################################
838
+
839
+ #write files to pdf
840
+ def write_text_to_pdf(directory, text):
841
+ doc = fitz.open() # Create a new PDF
842
+ page = doc.new_page() # Add a new page
843
+ page.insert_text((72, 72), text) # Position (x, y) and text
844
+ doc.save(f'{directory}/fhir_data.pdf') # Save the PDF
845
+ doc.close()
846
+ def run_analyzer(age, sex, ocr_files, formatted_ignore_words):
847
+ try:
848
+ # Process OCR files with provided input
849
+ print("Processing OCR files")
850
+ process_ocr_files(ocr_files, age)
851
+
852
+ # Create collated file
853
+ collate_images(ocr_files, f"{ocr_files}/Darna_tesseract")
854
+
855
+ # Deidentify records
856
+ print("Deidentifying records")
857
+ deidentify_records(ocr_files, formatted_ignore_words)
858
+
859
+
860
+ # Generate recommendations with provided age and sex
861
+ print("Generating recommendations")
862
+ recommendations = generate_recommendations(age=age, sex=sex)
863
+
864
+ # Extract data from FHIR file and create PDF
865
+ directory = ocr_files
866
+ #folderpath is global directory
867
+
868
+ with open(f'{folderpath}/summary/chart.json', 'r') as file:
869
+ json_data = json.load(file)
870
+
871
+ extracted_info = extract_lforms_data(json.dumps(json_data))
872
+ print(extracted_info)
873
+
874
+ json_output = json.dumps(extracted_info, indent=4)
875
+ write_text_to_pdf(directory, str(extracted_info))
876
+
877
+ final_directory = f'{directory}/Darna_tesseract/'
878
+
879
+ # Process PDF files
880
+ process_pdf_files(directory)
881
+
882
+ # Write the JSON output to a file
883
+ with open(f'{directory}/fhir_output.json', 'w', encoding='utf-8') as f:
884
+ f.write(json_output)
885
+
886
+ # NLP Processing for summary, past medical history, medications, and screening
887
+ json_file_path = f'{directory}/combined_output.json'
888
+ keys_pmh = ['PMH', 'medical', 'past medical history', 'surgical', 'past']
889
+ keys_meds = ['medications', 'MEDICATIONS:', 'medicine', 'meds']
890
+ keys_summary = ['HPI', 'history', 'summary']
891
+ keys_screening = ['RECS', 'RECOMMENDATIONS']
892
+
893
+ # Process text data and create word clouds
894
+ text_summary = process_directory_summary(directory, keys_summary)
895
+ preprocess_and_create_wordcloud(text_summary, final_directory)
896
+
897
+ text_meds = process_directory_meds(directory, keys_meds)
898
+ text_screening = load_text_from_json_screening(json_file_path, keys_screening)
899
+ text_pmh = process_directory_pmh(directory, keys_pmh)
900
+
901
+ # Write processed texts to JSON
902
+ keys = ("darnahi_summary", "darnahi_past_medical_history", "darnahi_medications", "darnahi_screening")
903
+ texts = (text_summary, text_pmh, text_meds, text_screening)
904
+ wordcloud_summary(keys, texts, final_directory)
905
+
906
+ # CHROMA embedding
907
+ chromadb_embed(directory)
908
+
909
+ # Cleanup OCR files, but leave Darna_tesseract files
910
+ subprocess.run(f'find {directory} -maxdepth 1 -type f -exec rm {{}} +', shell=True)
911
+
912
+ except Exception as e:
913
+ print(f"Error during processing: {e}")
914
+
915
+ ##CALL ANALYZER
916
+ run_analyzer(age, sex, ocr_files, formatted_ignore_words)
917
+ """
918
+ # Process OCR files with provided input
919
+ print("process ocr files")
920
+ process_ocr_files(ocr_files, age)
921
+
922
+ #doesnt work
923
+ #create collated file
924
+ collate_images(ocr_files, f"{ocr_files}/Darna_tesseract")
925
+
926
+
927
+ # Deidentify records
928
+ print("debug deidentify records")
929
+ deidentify_records()
930
+
931
+
932
+ # Generate recommendations with provided age and sex
933
+ print("debug generate records")
934
+ recommendations = generate_recommendations(age=age, sex=sex)
935
+
936
+ #extract data from fhir file and make pdf
937
+ directory = ocr_files
938
+
939
+ with open(f'{folderpath}/summary/chart.json', 'r') as file:
940
+ json_data = json.load(file)
941
+ # Extract information using function above from fhir document and write to pdf and json file
942
+ extracted_info = extract_lforms_data(json.dumps(json_data))
943
+ print(extracted_info)
944
+ #extracted_info = extract_info(json_data)
945
+ json_output = json.dumps(extracted_info, indent=4)
946
+ #extracted_info = extract_info(json_data)
947
+ write_text_to_pdf(directory, str(extracted_info))
948
+ final_directory= f'{directory}/Darna_tesseract/'
949
+
950
+ #calls the CALL_FILE pdf_sectionreader
951
+ process_pdf_files(directory)
952
+
953
+ # Write the JSON output to a file and pdf file (2 lines above)
954
+ with open(f'{directory}/fhir_output.json', 'w', encoding='utf-8') as f:
955
+ f.write(json_output)
956
+
957
+
958
+ #CALL FILE NLP_PROCESS
959
+ # Usage nlp_process
960
+ json_file_path = f'{directory}/combined_output.json'
961
+ #json_file_path = 'processed_data2.json'
962
+ #keys_summary = ['HPI', 'History of presenting illness', 'History of', 'summary']
963
+ keys_pmh = ['PMH', 'medical', 'past medical history', 'surgical', 'past'] #extracts past medical history
964
+ keys_meds = ['medications', 'MEDICATIONS:', 'medicine', 'meds'] #extracts medications
965
+ keys_summary = ['HPI', 'history', 'summary']
966
+ keys_screening= ['RECS', 'RECOMMENDATIONS']
967
+
968
+ #call functions and write to wordcloud and creat wordcloud.png file
969
+ text_summary = process_directory_summary(directory, keys_summary)
970
+ #creates wordcloud of uploaded files
971
+ preprocess_and_create_wordcloud(text_summary, final_directory)
972
+
973
+ text_meds = process_directory_meds(directory, keys_meds)#saves to medications in json
974
+ text_screening = load_text_from_json_screening(json_file_path, keys_screening)#saves to screening in json
975
+
976
+ text_pmh = process_directory_pmh(directory, keys_pmh)#saves to past history in json
977
+ #write to json using "keys":"texts"
978
+ keys= ("darnahi_summary", "darnahi_past_medical_history", "darnahi_medications", "darnahi_screening")
979
+ texts= (text_summary, text_pmh, text_meds, text_screening)
980
+ wordcloud_summary(keys, texts, final_directory)
981
+
982
+ #CHROMA MINER # Adjust this path to your directory
983
+ chromadb_embed(directory)
984
+
985
+ #remove files from ocr_files- cleanup but leave Darna_tesseract files
986
+ subprocess.run(f'find {directory} -maxdepth 1 -type f -exec rm {{}} +', shell=True)
987
+ """
app.py ADDED
@@ -0,0 +1,1025 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ##Sets up the flask server for viewing locally at {ip_address}:3001
2
+ #/* DARNA.HI
3
+ # * Copyright (c) 2023 Seapoe1809 <https://github.com/seapoe1809>
4
+ # * Copyright (c) 2023 pnmeka <https://github.com/pnmeka>
5
+ # *
6
+ # *
7
+ # * This program is free software: you can redistribute it and/or modify
8
+ # * it under the terms of the GNU General Public License as published by
9
+ # * the Free Software Foundation, either version 3 of the License, or
10
+ # * (at your option) any later version.
11
+ # *
12
+ # * This program is distributed in the hope that it will be useful,
13
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
+ # * GNU General Public License for more details.
16
+ # *
17
+ # * You should have received a copy of the GNU General Public License
18
+ # * along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ # */
20
+ from flask import Flask, render_template, send_file, send_from_directory, session, request, redirect, jsonify, url_for, Response, flash
21
+ from flask_login import LoginManager, UserMixin, login_user, logout_user, login_required, current_user
22
+ #from flask_bcrypt import Bcrypt
23
+ #from flask_sqlalchemy import SQLAlchemy
24
+
25
+ # Standard library
26
+ import os
27
+ import sqlite3
28
+ import json
29
+ import subprocess
30
+ from subprocess import run, CalledProcessError
31
+ import getpass
32
+ import webbrowser
33
+ from datetime import datetime, timedelta
34
+ from pathlib import Path
35
+ from functools import wraps
36
+ from urllib.parse import quote, unquote
37
+ import io
38
+
39
+ # Third-party packages
40
+ import requests
41
+ import qrcode
42
+ import pyzipper
43
+ import numpy as np
44
+ import matplotlib.pyplot as plt
45
+ from pdf2image import convert_from_path
46
+ from cryptography.fernet import Fernet
47
+
48
+ # DICOM handling
49
+ import pydicom
50
+ from pydicom.pixel_data_handlers.util import apply_voi_lut
51
+ import numpy as np
52
+ import matplotlib.pyplot as plt
53
+ import variables.variables as variables
54
+
55
+
56
+ ##UPDATE ZIP PASSWORD HERE
57
+ create_zip_password = "2023"
58
+
59
+ app = Flask(__name__)
60
+ HS_path= os.getcwd()
61
+ """
62
+ #app.config.update(
63
+ #SESSION_COOKIE_SECURE=True,
64
+ #SESSION_COOKIE_SAMESITE='None',
65
+ #)
66
+ # Initialize Flask extensions and Flask_login
67
+ bcrypt = Bcrypt(app)
68
+ login_manager = LoginManager()
69
+ login_manager.init_app(app)
70
+ login_manager.login_view = 'login'
71
+
72
+ #generates a app.secret_key that is variable and encrypted
73
+ key = Fernet.generate_key()
74
+ cipher_suite = Fernet(key)
75
+ app.secret_key = cipher_suite.encrypt(os.getcwd().encode())
76
+
77
+ # Configure the SQLAlchemy part to use SQLite database
78
+ from flask_sqlalchemy import SQLAlchemy
79
+
80
+ # Replace 'your_password' and 'your_database_name' with actual values
81
+ app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://seapoe1809:darnausers@seapoe1809.mysql.pythonanywhere-services.com/seapoe1809$users'
82
+ app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False # To suppress a warning
83
+
84
+ #app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
85
+ db = SQLAlchemy(app)
86
+
87
+ # Define User model, access
88
+ class User(db.Model, UserMixin):
89
+ id = db.Column(db.Integer, primary_key=True)
90
+ username = db.Column(db.String(80), unique=True)
91
+ password = db.Column(db.String(120))
92
+
93
+ def get_paths(self):
94
+ base_path = os.getcwd()
95
+ if self.username == 'ADMIN':
96
+ folder_name = 'Health_files'
97
+ elif self.username == 'USER1':
98
+ folder_name = 'Health_files2'
99
+ else:
100
+ folder_name = f'Health_files_{self.username}' # fallback
101
+
102
+ folderpath = os.path.join(base_path, folder_name)
103
+ APP_dir = os.path.join(base_path, 'install_module')
104
+ ocr_files = os.path.join(folderpath, 'ocr_files')
105
+ upload_dir = os.path.join(folderpath, 'upload')
106
+ summary_dir = os.path.join(folderpath, 'summary')
107
+
108
+ return {
109
+ 'HS_path': base_path,
110
+ 'folderpath': folderpath,
111
+ 'APP_dir': APP_dir,
112
+ 'ocr_files': ocr_files,
113
+ 'upload_dir': upload_dir,
114
+ 'summary_dir': summary_dir
115
+
116
+ }
117
+
118
+
119
+
120
+ @login_manager.user_loader
121
+ def load_user(user_id):
122
+ return db.session.get(User, int(user_id))
123
+ """
124
+ #importing variables from variables.py
125
+ # Label current dir and parent dir
126
+ HS_path = os.getcwd()
127
+ ip_address = variables.ip_address
128
+ APP_dir = f"{HS_path}/install_module"
129
+
130
+ # Configure static folder path
131
+ app.static_folder = 'static'
132
+ ########### TEST
133
+
134
+ app.secret_key = 'your_secret_key'
135
+
136
+ # Simulating users in memory for testing
137
+ from werkzeug.security import generate_password_hash, check_password_hash
138
+ users = {
139
+ 'ADMIN': generate_password_hash('health'),
140
+ 'USER1': generate_password_hash('wellness')
141
+ }
142
+
143
+ # Initialize Flask extensions and Flask-Login
144
+ login_manager = LoginManager()
145
+ login_manager.init_app(app)
146
+ login_manager.login_view = 'login'
147
+
148
+ # Simulating users in memory for testing
149
+ users = {
150
+ 'ADMIN': generate_password_hash('health'),
151
+ 'USER1': generate_password_hash('wellness')
152
+ }
153
+
154
+ # User class for Flask-Login
155
+ class User(UserMixin):
156
+ def __init__(self, username):
157
+ self.id = username # Use the username as the user ID
158
+
159
+ def get_paths(self):
160
+
161
+ base_path= os.getcwd()
162
+ HS_path= os.getcwd()
163
+
164
+ # Assign folder name based on the username
165
+ if self.id == 'ADMIN': # Use self.id since it's assigned in the constructor
166
+ folder_name = 'Health_files'
167
+ elif self.id == 'USER1':
168
+ folder_name = 'Health_files2'
169
+ else:
170
+ folder_name = f'Health_files_{self.id}' # fallback for other usernames
171
+
172
+ # Define paths
173
+
174
+ folderpath = os.path.join(base_path, folder_name)
175
+ APP_dir = os.path.join(base_path, 'install_module')
176
+ ocr_files = os.path.join(folderpath, 'ocr_files')
177
+ upload_dir = os.path.join(folderpath, 'upload')
178
+ summary_dir = os.path.join(folderpath, 'summary')
179
+
180
+
181
+ # Return paths as a dictionary
182
+ return {
183
+ 'HS_path': base_path,
184
+ 'folderpath': folderpath,
185
+ 'APP_dir': APP_dir,
186
+ 'ocr_files': ocr_files,
187
+ 'upload_dir': upload_dir,
188
+ 'summary_dir': summary_dir
189
+ }
190
+
191
+
192
+ @login_manager.user_loader
193
+ def load_user(username):
194
+ if username in users:
195
+ return User(username) # Return User object if username exists
196
+ return None
197
+
198
+ @app.route('/login', methods=['GET', 'POST'])
199
+ def login():
200
+ error_message = None
201
+ if request.method == 'POST':
202
+ username = request.form['username']
203
+ password = request.form['password']
204
+
205
+ # Check if user exists and password is correct
206
+ if username in users and check_password_hash(users[username], password):
207
+ user = User(username) # Create a user object
208
+ login_user(user) # Log the user in
209
+
210
+ # Assign folder paths based on the user
211
+ if username == "ADMIN":
212
+ session['folderpath'] = f"{os.getcwd()}/Health_files"
213
+ elif username == "USER1":
214
+ session['folderpath'] = f"{os.getcwd()}/Health_files2"
215
+
216
+ return redirect(url_for('protected'))
217
+ else:
218
+ error_message = "Password ⚠️"
219
+
220
+ return render_template('login.html', error_message=error_message)
221
+ """
222
+ #@app.before_first_request
223
+ def create_users():
224
+ db.create_all()
225
+ #admin = User(username='ADMIN', password='health')
226
+ admin = User(username='ADMIN', password=bcrypt.generate_password_hash('health').decode('utf-8'))
227
+ #admin = User(username='USER1', password='wellness')
228
+ user1 = User(username='USER1', password=bcrypt.generate_password_hash('wellness').decode('utf-8'))
229
+ db.session.add(admin)
230
+ db.session.add(user1)
231
+ db.session.commit()
232
+
233
+ @app.route('/login', methods=['GET', 'POST'])
234
+ def login():
235
+ error_message = None
236
+ if request.method == 'POST':
237
+ username = request.form['username']
238
+ password = request.form['password']
239
+ user = User.query.filter_by(username=username).first()
240
+
241
+ if user and bcrypt.check_password_hash(user.password, password):
242
+ login_user(user)
243
+
244
+ if username == "ADMIN":
245
+ session['folderpath'] = f"{os.getcwd()}/Health_files"
246
+ elif username == "USER1":
247
+ session['folderpath'] = f"{os.getcwd()}/Health_files2"
248
+
249
+ return redirect(url_for('protected'))
250
+ else:
251
+ error_message = "Password ⚠️"
252
+
253
+ return render_template('login.html', error_message=error_message)
254
+ """
255
+ @app.route('/protected')
256
+ @login_required
257
+ def protected():
258
+ return redirect(url_for('home'))
259
+
260
+ @login_manager.unauthorized_handler
261
+ def unauthorized():
262
+ return render_template('login.html')
263
+
264
+ @app.route('/logout')
265
+ @login_required
266
+ def logout():
267
+ logout_user()
268
+ return redirect('/login')
269
+
270
+ @app.route('/shutdown')
271
+ @login_required
272
+ def shutdown():
273
+ messages = []
274
+
275
+ # Attempt to gracefully shut down the mainapp.py process
276
+ try:
277
+ subprocess.run(["pkill", "-f", "python.*darnabot.py"], check=True)
278
+ messages.append("mainapp.py shut down successfully.")
279
+ subprocess.run(["pkill", "-f", "python.*darna.py"], check=True)
280
+ messages.append("app2.py shut down successfully.")
281
+ except subprocess.CalledProcessError as e:
282
+ messages.append(f"Failed to shut down processes: {e}")
283
+ # List of ports to shut down processes on
284
+ ports = [7860, 3012]
285
+
286
+ for port in ports:
287
+ try:
288
+ pid = subprocess.check_output(["lsof", "-t", "-i:{}".format(port)]).decode().strip()
289
+ if pid:
290
+ # Splitting PIDs in case multiple PIDs are found
291
+ pids = pid.split('\n')
292
+ for pid in pids:
293
+ subprocess.run(["kill", pid], check=True)
294
+ messages.append(f"Process on port {port} shut down successfully.")
295
+ else:
296
+ messages.append(f"No process found on port {port}.")
297
+ except subprocess.CalledProcessError:
298
+ try:
299
+ # If the first kill fails, attempt a forceful kill
300
+ for pid in pids:
301
+ subprocess.run(["kill", "-9", pid], check=True)
302
+ messages.append(f"Process on port {port} force-killed successfully.")
303
+ except subprocess.CalledProcessError as e:
304
+ messages.append(f"Forceful kill failed on port {port}: {e}")
305
+
306
+ return ' '.join(messages)
307
+
308
+
309
+ @app.route('/')
310
+ def home():
311
+ if current_user.is_authenticated:
312
+ # User is logged in
313
+ return render_template('index.html')
314
+ else:
315
+ # User is not logged in
316
+ return redirect(url_for('login'))
317
+
318
+
319
+ #making links for folder directory and files
320
+ @app.route('/folder')
321
+ @login_required
322
+ def folder_index():
323
+
324
+ folder_path = session.get('folderpath')
325
+ if folder_path is None:
326
+ return "No folder path set in the session, please log in again.", 400
327
+
328
+ files = []
329
+ files = os.listdir(folder_path)
330
+
331
+ file_links = []
332
+
333
+ for filename in files:
334
+ file_path = os.path.join(folder_path, filename)
335
+ is_directory = os.path.isdir(file_path)
336
+
337
+ if is_directory:
338
+ file_links.append({'filename': filename, 'path': f'/folder/{filename}', 'is_folder': True})
339
+ else:
340
+ file_links.append({'filename': filename, 'path': f'/{filename}', 'is_folder': False})
341
+
342
+ return render_template('folder_index.html', files=file_links)
343
+
344
+ #serving files from folder directory
345
+ @app.route('/<path:filename>')
346
+ @login_required
347
+ def serve_file(filename):
348
+ if not current_user.is_authenticated:
349
+ return redirect('/login')
350
+
351
+ folderpath = session.get('folderpath')
352
+ decoded_filename = unquote(filename)
353
+ return send_from_directory(folderpath, decoded_filename, as_attachment=False)
354
+
355
+ #making file links in subdirectory
356
+ @app.route('/folder/<path:subfolder>')
357
+ @login_required
358
+ def subfolder_index(subfolder):
359
+
360
+ folderpath = session.get('folderpath')
361
+ folder_path = os.path.join(folderpath, subfolder)
362
+
363
+ files = []
364
+ if os.path.exists(folder_path):
365
+ files = os.listdir(folder_path)
366
+
367
+ file_links = []
368
+
369
+ for filename in files:
370
+ file_path = os.path.join(folder_path, filename)
371
+ is_directory = os.path.isdir(file_path)
372
+
373
+ if is_directory:
374
+ file_links.append({'filename': filename, 'path': f'/folder/{subfolder}/{filename}', 'is_folder': True})
375
+ else:
376
+ file_links.append({'filename': filename, 'path': f'/folder/{subfolder}/{filename}', 'is_folder': False})
377
+
378
+ return render_template('folder_index.html', files=file_links)
379
+
380
+
381
+ @app.route('/folder/<path:subfolder>/<path:nested_subfolder>/<path:filename>')
382
+ @app.route('/folder/<path:subfolder>/<path:filename>')
383
+ @login_required
384
+ def serve_file_or_subfolder(subfolder, filename, nested_subfolder=''):
385
+
386
+ folderpath = session.get('folderpath')
387
+ folder_path = os.path.join(folderpath, subfolder, nested_subfolder)
388
+
389
+ decoded_filename = unquote(filename)
390
+
391
+ if os.path.isdir(os.path.join(folder_path, decoded_filename)):
392
+ # Render subfolder index
393
+ files = os.listdir(os.path.join(folder_path, decoded_filename))
394
+ file_links = []
395
+
396
+ for file in files:
397
+ file_path = os.path.join(folder_path, decoded_filename, file)
398
+ is_directory = os.path.isdir(file_path)
399
+
400
+ if is_directory:
401
+ file_links.append({'filename': file, 'path': f'/folder/{subfolder}/{nested_subfolder}/{decoded_filename}/{file}', 'is_folder': True})
402
+ else:
403
+ file_links.append({'filename': file, 'path': f'/folder/{subfolder}/{nested_subfolder}/{decoded_filename}/{file}', 'is_folder': False})
404
+
405
+ return render_template('folder_index.html', files=file_links)
406
+ else:
407
+ # Serve file
408
+ return send_from_directory(folder_path, decoded_filename, as_attachment=False)
409
+
410
+
411
+
412
+ @app.route('/zip_summary', methods=['POST'])
413
+ @login_required
414
+ def zip_summary():
415
+ folderpath = session.get('folderpath')
416
+ folder = folderpath
417
+ zip_password = f'{create_zip_password}'.encode('utf-8') # PASSWORD TO YOUR CHOICE HERE FOR ZIP ENCRYPT
418
+ folder_to_zip = f'{folder}/summary'
419
+ zip_filename = f'{folder}/my_summary.zip'
420
+
421
+ try:
422
+ """
423
+ with pyzipper.AESZipFile(zip_filename,
424
+ 'w',
425
+ compression=pyzipper.ZIP_DEFLATED,
426
+ encryption=pyzipper.WZ_AES) as zipf:
427
+ zipf.setpassword(zip_password)
428
+ for root, _, files in os.walk(folder_to_zip):
429
+ for file in files:
430
+ zipf.write(os.path.join(root, file),
431
+ os.path.relpath(os.path.join(root, file),
432
+ folder_to_zip))
433
+ """
434
+ return render_template('success.html', message="ZIP file created and encrypted.")
435
+ except Exception as e:
436
+ return render_template('error.html', message=str(e))
437
+
438
+ #The Following 3 sections are removed in ver 2.2 as sudo in app is security risk
439
+
440
+
441
+ #the following are to chart your medications and past medical history in fhir format
442
+ @app.route('/chart')
443
+ @login_required
444
+ def chart():
445
+ chart_json_url = url_for('custom_static', filename='chart.json')
446
+ #vitals_json_url = url_for('custom_static', filename='vitals.json')
447
+ return render_template('chart.html', chart_json_url=chart_json_url)
448
+
449
+
450
+
451
+ @app.route('/save-edits', methods=['POST'])
452
+ @login_required
453
+ def save_edits():
454
+ folderpath = session.get('folderpath', '')
455
+ destination_dir3 = os.path.join(folderpath, 'summary')
456
+ file_path = os.path.join(destination_dir3, 'chart.json')
457
+ updatedData = request.json # Get the updated data from the request
458
+ try:
459
+ with open(file_path, 'w') as jsonFile:
460
+ json.dump(updatedData, jsonFile, indent=4)
461
+ return jsonify({"status": "success", "message": "Data saved successfully"})
462
+ except Exception as e:
463
+ return jsonify({"status": "error", "message": str(e)}), 500
464
+
465
+ #Portal access viewer in Sky
466
+ @app.route('/pabv')
467
+ def pabv():
468
+ if current_user.is_authenticated:
469
+ # User is logged in
470
+ return render_template('pabv.html')
471
+ else:
472
+ # User is not logged in
473
+ return redirect(url_for('login'))
474
+
475
+ #Launches the darnabot with user ID
476
+ @app.route('/gradio_user')
477
+ @login_required
478
+ def gradio_user():
479
+ user_id = current_user.id # Get the user's ID
480
+ current_ip = request.host.split(':')[0] # Extract the IP
481
+ return redirect(f"http://{current_ip}:3012?user={user_id}")
482
+
483
+
484
+
485
+ # Keep the original custom_static function
486
+ @app.route('/custom_static/<filename>')
487
+ @login_required
488
+ def custom_static(filename):
489
+ folderpath = session.get('folderpath', '')
490
+ directory = os.path.join(folderpath, 'summary')
491
+ return send_from_directory(directory, filename)
492
+ #AI TAG ASSISTANCE
493
+ def get_db_connection(db_path):
494
+ """Create a database connection to medical_records.db"""
495
+ conn = sqlite3.connect(db_path)
496
+ conn.row_factory = sqlite3.Row
497
+ return conn
498
+
499
+ def format_file_size(size_in_bytes):
500
+ """Convert bytes to human readable format"""
501
+ for unit in ['B', 'KB', 'MB', 'GB']:
502
+ if size_in_bytes < 1024.0:
503
+ return f"{size_in_bytes:.1f} {unit}"
504
+ size_in_bytes /= 1024.0
505
+ return f"{size_in_bytes:.1f} TB"
506
+
507
+ def format_date(date_string):
508
+ """Format date string to readable format"""
509
+ try:
510
+ dt = datetime.fromisoformat(date_string)
511
+ return dt.strftime("%B %d, %Y at %I:%M %p")
512
+ except:
513
+ return date_string
514
+
515
+ def format_json_metadata(json_string):
516
+ """Format JSON string for display"""
517
+ try:
518
+ if json_string:
519
+ data = json.loads(json_string)
520
+ return json.dumps(data, indent=2)
521
+ return None
522
+ except:
523
+ return json_string
524
+
525
+ def search_files(search_term, folderpath):
526
+ """Search files across all fields with folder path filtering"""
527
+ # Get the summary directory path
528
+ db_path = f"{folderpath}/medical_records.db"
529
+ print("db_path", db_path)
530
+
531
+ # Check if database exists
532
+ if not os.path.exists(db_path):
533
+ raise FileNotFoundError(f"Database not found at {db_path}")
534
+
535
+ conn = get_db_connection(db_path)
536
+ cursor = conn.cursor()
537
+
538
+ # First, let's check what paths are actually in the database
539
+ # (You can remove this debug code later)
540
+ cursor.execute("SELECT DISTINCT file_path FROM files LIMIT 5")
541
+ sample_paths = cursor.fetchall()
542
+ print("Sample paths in DB:", sample_paths)
543
+
544
+ if not search_term:
545
+ # Return all files if no search term
546
+ # Remove the file_path filter since we're already using the correct database
547
+ query = """
548
+ SELECT
549
+ filename,
550
+ file_path,
551
+ file_size,
552
+ file_type,
553
+ upload_date,
554
+ embedded_metadata,
555
+ ai_metadata
556
+ FROM files
557
+ WHERE filename != '.db'
558
+ AND filename != 'chart.json'
559
+ AND filename != 'medical_records.db'
560
+ ORDER BY upload_date DESC
561
+ """
562
+ cursor.execute(query)
563
+ else:
564
+ # Search across all relevant fields
565
+ query = '''
566
+ SELECT
567
+ filename,
568
+ file_path,
569
+ file_size,
570
+ file_type,
571
+ upload_date,
572
+ embedded_metadata,
573
+ ai_metadata
574
+ FROM files
575
+ WHERE (filename LIKE ?
576
+ OR embedded_metadata LIKE ?
577
+ OR ai_metadata LIKE ?)
578
+ AND filename != '.db'
579
+ AND filename != 'chart.json'
580
+ AND filename != 'medical_records.db'
581
+ ORDER BY upload_date DESC
582
+ '''
583
+ search_pattern = f"%{search_term}%"
584
+ cursor.execute(query, [search_pattern, search_pattern, search_pattern])
585
+
586
+ files = cursor.fetchall()
587
+ conn.close()
588
+
589
+ # Convert to list of dictionaries and add formatted fields
590
+ file_list = []
591
+ for file in files:
592
+ file_dict = dict(file)
593
+
594
+ # Add formatted versions
595
+ file_dict['file_size_formatted'] = format_file_size(file_dict.get('file_size', 0))
596
+ file_dict['upload_date_formatted'] = format_date(file_dict.get('upload_date', ''))
597
+ file_dict['embedded_metadata_formatted'] = format_json_metadata(file_dict.get('embedded_metadata', ''))
598
+ file_dict['ai_metadata_formatted'] = format_json_metadata(file_dict.get('ai_metadata', ''))
599
+
600
+ file_list.append(file_dict)
601
+
602
+ return file_list
603
+
604
+ def categorize_files_from_db(files):
605
+ """Categorize files from database based on their extensions"""
606
+ categories = {
607
+ 'pdf_files': [],
608
+ 'xml_files': [],
609
+ 'dicom_files': [],
610
+ 'image_files': [],
611
+ 'other_files': []
612
+ }
613
+
614
+ # Define file extensions for each category
615
+ pdf_extensions = {'.pdf'}
616
+ xml_extensions = {'.xml'}
617
+ dicom_extensions = {'.dcm', '.dicom'}
618
+ image_extensions = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp'}
619
+
620
+ for file in files:
621
+ filename = file['filename'].lower()
622
+ extension = Path(filename).suffix.lower()
623
+
624
+ if extension in pdf_extensions:
625
+ categories['pdf_files'].append(file)
626
+ elif extension in xml_extensions:
627
+ categories['xml_files'].append(file)
628
+ elif extension in dicom_extensions:
629
+ categories['dicom_files'].append(file)
630
+ elif extension in image_extensions:
631
+ categories['image_files'].append(file)
632
+ else:
633
+ categories['other_files'].append(file)
634
+
635
+ return categories
636
+
637
+ # View dicom files in Health files - MAIN SUMMARY ROUTE
638
+ @app.route('/summary/', methods=['GET'])
639
+ @login_required
640
+ def dicom_files():
641
+ """Main summary page with search functionality"""
642
+ folderpath = session.get('folderpath', '')
643
+ directory = os.path.join(folderpath, 'summary')
644
+
645
+ # Get search term from query parameters
646
+ search_term = request.args.get('search', '').strip()
647
+
648
+ # Try database first, then fallback to filesystem
649
+ use_database = False
650
+ total_results = 0
651
+
652
+ try:
653
+ # Check if database exists and try to use it
654
+ db_path = f"{directory}/medical_records.db"
655
+ if os.path.exists(db_path):
656
+ # Try to get files from database
657
+ if search_term:
658
+ files = search_files(search_term, directory)
659
+
660
+ else:
661
+ files = search_files(None, directory) # Get all files
662
+
663
+ # Categorize files from database
664
+ categories = categorize_files_from_db(files)
665
+
666
+ # Calculate total results
667
+ total_results = len(files)
668
+ use_database = True
669
+
670
+ # Extract the categorized files
671
+ pdf_files = categories['pdf_files']
672
+ xml_files = categories['xml_files']
673
+ dicom_files = categories['dicom_files']
674
+ image_files = categories['image_files']
675
+ other_files = categories['other_files']
676
+ else:
677
+ raise FileNotFoundError("Database not found, using filesystem")
678
+
679
+ except Exception as e:
680
+ # Fallback to filesystem if database fails
681
+ print(f"Database error, falling back to filesystem: {str(e)}")
682
+
683
+ # Lists to store file names (original method)
684
+ pdf_files = []
685
+ xml_files = []
686
+ dicom_files = []
687
+ image_files = []
688
+ other_files = []
689
+
690
+ # Scan directory and categorize files by extension
691
+ if os.path.exists(directory):
692
+ for f in os.listdir(directory):
693
+ # Skip database files
694
+ if f in ['medical_records.db', '.db', 'chart.json']:
695
+ continue
696
+
697
+ # Create file dict for compatibility with template
698
+ file_dict = {'filename': f}
699
+
700
+ if f.lower().endswith(('.pdf', '.PDF')):
701
+ pdf_files.append(file_dict)
702
+ elif f.lower().endswith(('.xml', '.XML')):
703
+ xml_files.append(file_dict)
704
+ elif f.lower().endswith(('.dcm', '.dicom', '.DCM')):
705
+ dicom_files.append(file_dict)
706
+ elif f.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp')):
707
+ image_files.append(file_dict)
708
+ else:
709
+ # Other files
710
+ other_files.append(file_dict)
711
+
712
+ use_database = False
713
+ search_term = None
714
+ total_results = len(pdf_files) + len(xml_files) + len(dicom_files) + len(image_files) + len(other_files)
715
+
716
+ # Debug prints (like original)
717
+ print(f"PDF files: {len(pdf_files)}")
718
+ print(f"DICOM files: {len(dicom_files)}")
719
+ print(f"Using database: {use_database}")
720
+
721
+ # Pass the lists to the template
722
+ return render_template('summary.html',
723
+ pdf_files=pdf_files,
724
+ xml_files=xml_files,
725
+ dicom_files=dicom_files,
726
+ image_files=image_files,
727
+ other_files=other_files,
728
+ search_term=search_term,
729
+ total_results=total_results,
730
+ use_database=use_database)
731
+
732
+ @app.route('/summary/<filename>')
733
+ @login_required
734
+ def display_file(filename):
735
+ """Display individual file"""
736
+ folderpath = session.get('folderpath', '')
737
+ directory = os.path.join(folderpath, 'summary')
738
+ file_path = os.path.join(directory, filename)
739
+
740
+ # Handle DICOM files
741
+ if filename.lower().endswith(('.dcm', '.dicom', '.DCM')):
742
+ # Load the DICOM file to compute max_slice
743
+ dicom_data = pydicom.dcmread(file_path)
744
+
745
+ # Ensure it's a multi-dimensional dataset to calculate max_slice
746
+ if dicom_data.pixel_array.ndim > 2:
747
+ max_slice = dicom_data.pixel_array.shape[0] - 1
748
+ else:
749
+ max_slice = 0 # Handle single-slice (2D) DICOM images as well
750
+ return render_template('view_dicom.html', filename=filename, max_slice=max_slice)
751
+
752
+ # Directly serve PDF files
753
+ if filename.lower().endswith('.pdf'):
754
+ return send_from_directory(directory, filename, mimetype='application/pdf')
755
+
756
+ # Directly serve XML files
757
+ elif filename.lower().endswith('.xml'):
758
+ return send_from_directory(directory, filename, mimetype='application/xml')
759
+
760
+ # Serve image files
761
+ elif filename.lower().endswith(('.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.webp')):
762
+ # Determine appropriate mimetype
763
+ extension = Path(filename).suffix.lower()
764
+ mimetypes = {
765
+ '.jpg': 'image/jpeg',
766
+ '.jpeg': 'image/jpeg',
767
+ '.png': 'image/png',
768
+ '.gif': 'image/gif',
769
+ '.bmp': 'image/bmp',
770
+ '.tiff': 'image/tiff',
771
+ '.svg': 'image/svg+xml',
772
+ '.webp': 'image/webp'
773
+ }
774
+ return send_from_directory(directory, filename, mimetype=mimetypes.get(extension, 'application/octet-stream'))
775
+
776
+ # Fallback for unsupported file types
777
+ return 'Unsupported file type', 404
778
+
779
+
780
+
781
+
782
+ @app.route('/dicom/slice/<filename>/<int:slice_index>')
783
+ @login_required
784
+ def serve_dicom_slice(filename, slice_index):
785
+ folderpath = session.get('folderpath', '')
786
+ directory = os.path.join(folderpath, 'summary')
787
+ dicom_file_path = os.path.join(directory, filename)
788
+
789
+ dicom_data = pydicom.dcmread(dicom_file_path)
790
+ image_3d = apply_voi_lut(dicom_data.pixel_array, dicom_data)
791
+
792
+ # Selecting the requested slice
793
+ try:
794
+ image_slice = image_3d[slice_index]
795
+ except IndexError:
796
+ return "Slice index out of range", 400
797
+
798
+ # Normalize and convert to uint8
799
+ image_slice = np.interp(image_slice, (image_slice.min(), image_slice.max()), (0, 255))
800
+ image_slice = image_slice.astype(np.uint8)
801
+
802
+ # Convert to PNG
803
+ buf = io.BytesIO()
804
+ plt.imsave(buf, image_slice, cmap='gray', format='png')
805
+ buf.seek(0)
806
+
807
+ return send_file(buf, mimetype='image/png')
808
+
809
+
810
+ @app.route('/upload', methods=['GET', 'POST'])
811
+ @login_required
812
+ def upload_file():
813
+ folderpath = session.get('folderpath', '')
814
+ destination_dir1 = os.path.join(folderpath, 'ocr_files')
815
+ upload_dir = os.path.join(folderpath, 'upload')
816
+ destination_dir3 = os.path.join(folderpath, 'summary')
817
+
818
+ if request.method == 'POST':
819
+ file_type = request.form.get('Type')
820
+ file = request.files.get('File')
821
+
822
+ if file:
823
+ filename = file.filename
824
+ file_path1 = os.path.join(destination_dir1 if file_type == 'HL_File' else folderpath, filename)
825
+ file.save(file_path1) # Save the file temporarily
826
+
827
+ try:
828
+ # Run clamscan on the uploaded file
829
+ result = subprocess.run(['clamscan', '-r', file_path1], capture_output=True, text=True, check=True)
830
+
831
+ if "Infected files: 0" in result.stdout:
832
+ if file_type == 'HL_File':
833
+ file_path2 = os.path.join(destination_dir3, filename)
834
+ # Use subprocess to copy the file
835
+ if os.name == 'posix': # Unix-based system
836
+ subprocess.run(['cp', file_path1, file_path2], check=True)
837
+ elif os.name == 'nt': # Windows
838
+ subprocess.run(['copy', file_path1, file_path2], shell=True, check=True)
839
+ return render_template('success.html')
840
+ else:
841
+ os.remove(file_path1) # Remove the infected file
842
+ return render_template('upload.html', message='File is infected!')
843
+
844
+ except subprocess.CalledProcessError as e:
845
+ print(f"An error occurred during file operation: {e}")
846
+ os.remove(file_path1) # Ensure the source file is removed in case of error
847
+ return render_template('upload.html', message='An error occurred during file processing!')
848
+
849
+ except Exception as e:
850
+ print(f"An error occurred: {e}")
851
+ if file_type == 'HL_File':
852
+ file_path2 = os.path.join(destination_dir3, filename)
853
+ # Use subprocess to copy the file
854
+ if os.name == 'posix': # Unix-based system
855
+ subprocess.run(['cp', file_path1, file_path2], check=True)
856
+ elif os.name == 'nt': # Windows
857
+ subprocess.run(['copy', file_path1, file_path2], shell=True, check=True)
858
+ return render_template('success.html') # Proceed if ClamAV scan is not performed
859
+
860
+ return render_template('upload.html')
861
+
862
+ @app.route('/connect_nc')
863
+ @login_required
864
+ def connect_nc():
865
+ url = request.remote_addr
866
+ client_ip = request.remote_addr
867
+ if url == '0.0.0.0':
868
+ url = f"http://{ip_address}:7860"
869
+ else:
870
+ url = f"http://{url}:7860"
871
+ print(url)
872
+ qr = qrcode.QRCode()
873
+ qr.add_data(url)
874
+ qr.make()
875
+ image = qr.make_image()
876
+ image.save(f'{HS_path}/static/qrcode.png')
877
+ return render_template('connect_nc.html')
878
+
879
+ #update password in connect_nc
880
+ @app.route('/register', methods=['GET', 'POST'])
881
+ @login_required
882
+ def register():
883
+ if request.method == 'POST':
884
+ # Get the new password from the form
885
+ new_password = request.form['password']
886
+
887
+ # Hash the new password
888
+ hashed_new_password = bcrypt.generate_password_hash(new_password).decode('utf-8')
889
+
890
+ # Look up the current user in your database
891
+ user = User.query.filter_by(username=current_user.username).first()
892
+
893
+ if user:
894
+ # Update the password in the database
895
+ user.password = hashed_new_password
896
+ db.session.commit()
897
+
898
+ flash('Password change successful. You can now log in.')
899
+ return redirect(url_for('login'))
900
+ else:
901
+ flash('Error: User not found.')
902
+ return redirect(url_for('register'))
903
+
904
+ return render_template('register.html')
905
+
906
+
907
+ @app.route('/pi')
908
+ @login_required
909
+ def pi():
910
+ with open(f'/{HS_path}/install_module/templates/index2.html', 'r') as f:
911
+ content = f.read()
912
+ return Response(content, content_type='text/html')
913
+
914
+ """
915
+ @app.route('/analyze', methods=['GET', 'POST'])
916
+ @login_required
917
+ def analyze():
918
+
919
+ if request.method == 'POST':
920
+ age = request.form['age']
921
+ sex = request.form['sex']
922
+ ignore_words = request.form['ignore-words']
923
+ print(f"Age: {age}")
924
+ print(f"Sex: {sex}")
925
+ print(f"Ignore Words: {ignore_words}")
926
+ formatted_ignore_words = ignore_words.replace(' ', '|')
927
+
928
+ content = f"age = '{age}'\nsex = '{sex}'\nignore_words = '{formatted_ignore_words}'\n"
929
+ file_path = f"{HS_path}/variables/variables2.py"
930
+
931
+ try:
932
+ with open(file_path, 'w') as file:
933
+ file.write(content)
934
+ except Exception as e:
935
+ print(f"Error writing to variables2.py: {str(e)}")
936
+ return str(e)
937
+
938
+ # Run the analyze script asynchronously using nohup and pass session cookie
939
+ folderpath = session.get('folderpath')
940
+ env_vars = os.environ.copy()
941
+ env_vars['FOLDERPATH'] = folderpath
942
+ command = f'python3 {HS_path}/analyze.py'
943
+ #command = f'nohup python3 {HS_path}/analyze.py > /dev/null 2>&1 &'
944
+ #subprocess.Popen(command, shell=True, env=env_vars)
945
+ print("Process time is 3 minutes")
946
+ try:
947
+ subprocess.Popen(command, shell=True, env=env_vars)
948
+ except Exception as e:
949
+ print(f"Error running analyze.py: {str(e)}")
950
+ return str(e)
951
+
952
+ return render_template('success.html', message="Process time is 3 minutes")
953
+
954
+ return render_template('analyze.html', submitted=True)
955
+
956
+ """
957
+
958
+ @app.route('/install_module/<path:filename>')
959
+ @login_required
960
+ def serve_install_module(filename):
961
+ return send_from_directory('install_module', filename)
962
+
963
+ @app.route('/install')
964
+ @login_required
965
+ def install():
966
+ print("Inside /install")
967
+ app_name_file = request.args.get('app_name_file') + '.js'
968
+ app_folder = request.args.get('app_folder')
969
+ session['app_name_file']= app_name_file
970
+ app_name = session.get('app_name_file', '').replace('.js', '')
971
+
972
+ print(f"Session variables: {session}")
973
+
974
+ # Additional logic to process the app_name_file and app_folder if needed
975
+ return render_template('install.html', app_name_file=app_name_file, app_folder=app_folder)
976
+
977
+
978
+ @app.route('/execute_script', methods=['GET', 'POST'])
979
+ @login_required
980
+ # executes the install script
981
+ def execute_script():
982
+ try:
983
+ print("Inside /execute_script")
984
+
985
+ # List of allowed app names
986
+ allowed_app_names = ['Ibs_Module', 'Immunization_Tracker', 'Weight_Tracker', 'Tailscale', 'Dock', 'Strep_Module', 'Anxiety_Module']
987
+
988
+ app_name = session.get('app_name_file', '').replace('.js', '')
989
+ if not app_name:
990
+ return jsonify(success=False, error="Missing app_name")
991
+
992
+ # Validate app_name
993
+ if app_name not in allowed_app_names:
994
+ return jsonify(success=False, error=f"Invalid app_name. Allowed values are: {', '.join(allowed_app_names)}")
995
+
996
+ app_name_file = session['app_name_file']
997
+ # import ip_address from variables and pass to env_var to install app_name
998
+ url = f"http://{ip_address}"
999
+ env_vars = os.environ.copy()
1000
+ env_vars['URL'] = url
1001
+ #cmd = ['python3', f'install_module/{app_name}/{app_name}.py']
1002
+ print(cmd)
1003
+ proc = subprocess.Popen(cmd, env=env_vars, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
1004
+ stdout, stderr = proc.communicate()
1005
+ if proc.returncode == 0:
1006
+ return jsonify(success=True, message="Please Refresh App")
1007
+ else:
1008
+ print(f'Subprocess output: {stdout}')
1009
+ print(f'Subprocess error: {stderr}')
1010
+ return jsonify(success=False, error="Non-zero return code")
1011
+ print(f"Session variables: {session}")
1012
+ except Exception as e:
1013
+ return jsonify(success=False, error=str(e))
1014
+
1015
+
1016
+ @app.errorhandler(404)
1017
+ @login_required
1018
+ def page_not_found(error):
1019
+ print("Error 404 Encountered")
1020
+ return render_template('errors.html', error_message='Page not found'), 404
1021
+
1022
+
1023
+ if __name__== '__main__':
1024
+ app.run('0.0.0.0', port=7860)
1025
+
darnabot/darnabot.log ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Traceback (most recent call last):
2
+ File "/media/meka/extradrive11/Health_server/darnabot/darnabot.py", line 972, in <module>
3
+ demo.launch(server_name='0.0.0.0', server_port=3012, share=False)
4
+ File "/media/meka/extradrive11/Health_server/darnabot/llmvenv/lib/python3.10/site-packages/gradio/blocks.py", line 2372, in launch
5
+ ) = http_server.start_server(
6
+ File "/media/meka/extradrive11/Health_server/darnabot/llmvenv/lib/python3.10/site-packages/gradio/http_server.py", line 154, in start_server
7
+ raise OSError(
8
+ OSError: Cannot find empty port in range: 3012-3012. You can specify a different port by setting the GRADIO_SERVER_PORT environment variable or passing the `server_port` parameter to `launch()`.
9
+ /media/meka/extradrive11/Health_server/darnabot/llmvenv/lib/python3.10/site-packages/gradio/analytics.py:106: UserWarning: IMPORTANT: You are using gradio version 4.44.0, however version 4.44.1 is available, please upgrade.
10
+ --------
11
+ warnings.warn(
darnabot/darnabot.py ADDED
@@ -0,0 +1,976 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #/* DARNA.HI
2
+ # * Copyright (c) 2023 Seapoe1809 <https://github.com/seapoe1809>
3
+ # * Copyright (c) 2023 pnmeka <https://github.com/pnmeka>
4
+ # *
5
+ # *
6
+ # * This program is free software: you can redistribute it and/or modify
7
+ # * it under the terms of the GNU General Public License as published by
8
+ # * the Free Software Foundation, either version 3 of the License, or
9
+ # * (at your option) any later version.
10
+ # *
11
+ # * This program is distributed in the hope that it will be useful,
12
+ # * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # * GNU General Public License for more details.
15
+ # *
16
+ # * You should have received a copy of the GNU General Public License
17
+ # * along with this program. If not, see <http://www.gnu.org/licenses/>.
18
+ # */
19
+ #uses chromaminer to chunk and embed and then uses function to extract relevant component
20
+ import os, subprocess
21
+ import re
22
+ import json
23
+ import random
24
+ import requests
25
+ import gradio as gr
26
+ import chromadb
27
+ import sqlite3
28
+ import base64
29
+ from io import BytesIO
30
+ from datetime import datetime
31
+ from fpdf import FPDF
32
+ import threading
33
+ from threading import local
34
+ from reportlab.pdfgen import canvas
35
+ from reportlab.lib.pagesizes import letter
36
+ import tempfile
37
+ from PIL import Image
38
+ import io
39
+ from ollama import AsyncClient
40
+ import asyncio
41
+ ####NEW
42
+ #install pytesseract
43
+ #install pdf2image pip install reportlab PyPDF2 nltk wordcloud unidecode
44
+ #pdfplumber ollama
45
+
46
+ #from transformers import pipeline
47
+ #set model
48
+ model="gemma3:4b"
49
+ directory = ""
50
+ folderpath= ""
51
+ basic_info=""
52
+
53
+
54
+
55
+
56
+ conversation_memory = []
57
+
58
+ async def chat(messages):
59
+
60
+ async for part in await AsyncClient().chat(model=f'{model}', messages=messages, stream=True):
61
+ chunk=part['message']['content']
62
+ yield chunk
63
+
64
+
65
+ #this truncates the words for use by Chroma to build context
66
+ def truncate_words(documents):
67
+ truncated_documents = []
68
+ for doc in documents:
69
+ doc=str(doc)
70
+ words = doc.split()[:300] # Truncate to 300 words
71
+ truncated_documents.append(' '.join(words))
72
+ return truncated_documents
73
+
74
+ def generate_context_and_sources(
75
+ query: str,
76
+ collection_name: str = "documents_collection",
77
+ persist_directory: str = "chroma_storage"
78
+ ) -> (str, str):
79
+ print(persist_directory)
80
+ context, sources = "No data available", "No sources available."
81
+ try:
82
+ # Check if persist_directory exists; if not
83
+ if not os.path.exists(persist_directory):
84
+ print(f"Directory '{persist_directory}' does not exist. Skipping.")
85
+ return context, sources
86
+
87
+ chroma_client = chromadb.PersistentClient(path=persist_directory)
88
+ collection = chroma_client.get_collection(name=collection_name)
89
+
90
+ results = collection.query(query_texts=[query], n_results=3, include=["documents", "metadatas"])
91
+ sources = "\n".join(
92
+ [
93
+ f"{result.get('filename', 'Unknown filename')}: batch {result.get('batch_number', 'Unknown batch')}"
94
+ for result in results["metadatas"][0] # type: ignore
95
+ ]
96
+ )
97
+ truncated_documents = truncate_words(results["documents"])
98
+ context = "".join(truncated_documents)
99
+
100
+ except Exception as e:
101
+ print(f"Error accessing collection or processing query: {e}")
102
+ return context, sources
103
+
104
+ #set global directory
105
+ def set_user_directory(request: gr.Request):
106
+ global directory
107
+ referer= request.headers.get('referer')
108
+ if "user=1" in referer:
109
+ # Admin user
110
+ directory = "../Health_files/ocr_files/Darna_tesseract/"
111
+ elif "user=2" in referer:
112
+ # Non-admin user
113
+ directory = "../Health_files2/ocr_files/Darna_tesseract/"
114
+ else:
115
+ # Handle unexpected user types
116
+ directory = "/"
117
+ print(f"Current ocr directory: {directory}")
118
+
119
+ def set_user_health_files_directory(request: gr.Request):
120
+ global folderpath
121
+ referer= request.headers.get('referer')
122
+ if "user=1" in referer:
123
+ # Admin user
124
+ folderpath = "../Health_files/"
125
+ elif "user=2" in referer:
126
+ # Non-admin user
127
+ folderpath = "../Health_files2/"
128
+ else:
129
+ # Handle unexpected user types
130
+ folderpath = "/"
131
+ print(f"Current folderpath: {folderpath}")
132
+
133
+
134
+ #function to ananlyze the input query using re and make some assessment on where to get context
135
+ def analyze_query(query, directory):
136
+ #pattern for keyword
137
+ darna_pattern = r'(?:darnahi|darna|server|hello)\s*[:=]?\s*'
138
+ darna_match = re.search(darna_pattern, query, re.IGNORECASE)
139
+ darna_value = darna_match.group().strip() if darna_match else None
140
+
141
+ med_pattern = r'(?:meds|medication|medications|medicine|medicine|drug|drugs)\s*[:=]?\s*'
142
+ med_match = re.search(med_pattern, query, re.IGNORECASE)
143
+ med_value = med_match.group().strip() if med_match else None
144
+
145
+ summary_pattern = r'(?:medical|clinical|advice|advise|weight|diet)\s*[:=]?\s*'
146
+ summary_match = re.search(summary_pattern, query, re.IGNORECASE)
147
+ summary_value = summary_match.group().strip() if summary_match else None
148
+
149
+ past_medical_history_pattern = r'(?:history|procedure|procedures|surgery|pastmedical|pmh|past-medical|past-history)\s*[:=]?\s*'
150
+ past_medical_history_match = re.search(past_medical_history_pattern, query, re.IGNORECASE)
151
+ past_medical_history_value = past_medical_history_match.group().strip() if past_medical_history_match else None
152
+
153
+ xmr_pattern = r'(?:monero|xmr|crypto|cryptocurrency|privacy|XMR|MOnero)\s*[:=]?\s*'
154
+ xmr_match = re.search(xmr_pattern, query, re.IGNORECASE)
155
+ xmr_value = xmr_match.group().strip() if xmr_match else None
156
+
157
+ json_file_path= f'{directory}/wordcloud_summary.json'
158
+
159
+ try:
160
+ with open(json_file_path, 'r', encoding='utf-8') as file:
161
+ existing_data = json.load(file)
162
+ except FileNotFoundError:
163
+ existing_data = {}
164
+
165
+ result = ""
166
+ if darna_value is not None:
167
+ print(darna_value)
168
+ key='darnahi'
169
+ result += existing_data.get(key, " ")[:200]
170
+ if med_value is not None:
171
+ print(med_value)
172
+ key = 'darnahi_medications'
173
+ result += existing_data.get(key, " ")[:500]
174
+ if summary_value is not None:
175
+ print(summary_value)
176
+ key = 'darnahi_summary'
177
+ result += existing_data.get(key, "No data found for 'summary' key.")[:350]
178
+ if past_medical_history_value is not None:
179
+ print(past_medical_history_value)
180
+ key = 'darnahi_past_medical_history'
181
+ result += existing_data.get(key, " ")[:500]
182
+ if xmr_value is not None:
183
+ print(xmr_value)
184
+ key = 'darnahi_xmr'
185
+ result += existing_data.get(key, " ")[:200]
186
+
187
+ # Check if no pattern matched
188
+ if not (darna_match or med_match or summary_match or xmr_match or past_medical_history_match):
189
+
190
+ collection_name="documents_collection"
191
+ context, sources = generate_context_and_sources(query, collection_name, os.path.join(directory, 'chroma_storage'))
192
+
193
+ print(context, sources)
194
+ result = context[:150]
195
+ if result is None:
196
+ result={''}
197
+
198
+ print(result)
199
+ return result
200
+
201
+
202
+
203
+ #generate a chat function using the query and context
204
+ async def my_function(query, request: gr.Request, chat_history):
205
+ #pass userID
206
+ global conversation_memory
207
+ history ="<history>\n".join(conversation_memory)
208
+ if len(history) > 300:
209
+ history = history[-400:]
210
+
211
+ print(history)
212
+ referer= request.headers.get('referer')
213
+ if "user=2" in referer:
214
+ #non admin user
215
+ directory="../Health_files2/ocr_files/Darna_tesseract/"
216
+ print(directory)
217
+ elif "user=1" in referer:
218
+ #admin
219
+ directory="../Health_files/ocr_files/Darna_tesseract/"
220
+ print(directory)
221
+ else:
222
+ directory="/"
223
+ print("default dir")
224
+
225
+ #chroma rag
226
+ context=analyze_query(query, directory)
227
+ context=f'<context>{context}</context>'
228
+
229
+ messages = [
230
+ {"role": "user", "content": "You are Darnabot. End with a followup"},
231
+ {"role": "assistant", "content": "I am 'Darnabot', AI health assistant with domain expertise. How can I help?"},
232
+ {"role": "user", "content": f"'Darnabot' answer query: {query} using context: {context}. Also here is history of previous conversation with user but ignore if not relevant to query: {history}"},
233
+ ]
234
+
235
+
236
+
237
+ full_response=""
238
+ async for content in chat(messages):
239
+ full_response += content
240
+ yield chat_history + [(query, full_response)]
241
+
242
+
243
+ conversation_memory.append(f"<history> {full_response}</history>")
244
+ conversation_memory = conversation_memory[-4:]
245
+
246
+ def clear_conversation():
247
+ global conversation_memory
248
+ conversation_memory = []
249
+ gr.ClearButton([msg, chatbot])
250
+ return "", None
251
+
252
+ ################################
253
+ """
254
+ #run ai to analyze records
255
+ #from analyze import *
256
+ import logging
257
+ import json
258
+ import subprocess
259
+ from typing import List, Tuple
260
+
261
+ def stepwise_error_handling_analyze(deidentify_words, folderpath: str, ocr_files: str, age: int, sex: str) -> List[Tuple[str, str]]:
262
+ logging.basicConfig(filename='error_log.txt', level=logging.ERROR,
263
+ format='%(asctime)s - %(levelname)s - %(message)s')
264
+
265
+ steps = [
266
+ ("Extract and write LForms data", lambda: extract_and_write_lforms_data(folderpath)),
267
+ ("Process OCR files", lambda: process_ocr_files(ocr_files)),
268
+ ("Collate images", lambda: collate_images(ocr_files, f"{ocr_files}/Darna_tesseract")),
269
+ ("Deidentify records", deidentify_records(ocr_files, deidentify_words)),
270
+ ("Generate recommendations", lambda: generate_recommendations(folderpath, age=age, sex=sex)),
271
+ ("Process PDF files", lambda: process_pdf_files(ocr_files)),
272
+ ("Process directory summary", lambda: process_directory_summary(ocr_files, ['HPI', 'history', 'summary'])),
273
+ ("Create wordcloud", lambda: preprocess_and_create_wordcloud(process_directory_summary(ocr_files, ['HPI', 'history', 'summary']), f'{ocr_files}/Darna_tesseract/')),
274
+ ("Process directory meds", lambda: process_directory_meds(ocr_files, ['medications', 'MEDICATIONS:', 'medicine', 'meds'])),
275
+ ("Load screening text", lambda: load_text_from_json_screening(f'{ocr_files}/Darna_tesseract/combined_output.json', ['RECS', 'RECOMMENDATIONS'])),
276
+ ("Process directory PMH", lambda: process_directory_pmh(ocr_files, ['PMH', 'medical', 'past medical history', 'surgical', 'past'])),
277
+ ("Generate wordcloud summary", lambda: wordcloud_summary(
278
+ ("darnahi_summary", "darnahi_past_medical_history", "darnahi_medications", "darnahi_screening"),
279
+ (process_directory_summary(ocr_files, ['HPI', 'history', 'summary']),
280
+ process_directory_pmh(ocr_files, ['PMH', 'medical', 'past medical history', 'surgical', 'past']),
281
+ process_directory_meds(ocr_files, ['medications', 'MEDICATIONS:', 'medicine', 'meds']),
282
+ load_text_from_json_screening(f'{ocr_files}/combined_output.json', ['RECS', 'RECOMMENDATIONS'])),
283
+ f'{ocr_files}/Darna_tesseract/'
284
+ )),
285
+ #("Chromadb embed", lambda: chromadb_embed(ocr_files)),
286
+ #("Clean up directory", lambda: subprocess.run(f'find {ocr_files} -maxdepth 1 -type f -exec rm {{}} +', shell=True))
287
+ ]
288
+
289
+ results = []
290
+
291
+ for step_name, step_function in steps:
292
+ try:
293
+ step_function()
294
+ results.append((step_name, "Success"))
295
+ except Exception as e:
296
+ error_message = f"Error in {step_name}: {str(e)}"
297
+ logging.error(error_message)
298
+ results.append((step_name, f"Error: {str(e)}"))
299
+
300
+ return results
301
+
302
+ def extract_and_write_lforms_data(folderpath: str):
303
+ with open(f'{folderpath}/summary/chart.json', 'r') as file:
304
+ json_data = json.load(file)
305
+
306
+ extracted_info = extract_lforms_data(json.dumps(json_data))
307
+ json_output = json.dumps(extracted_info, indent=4)
308
+ write_text_to_pdf(folderpath, str(extracted_info))
309
+ with open(f'{folderpath}/ocr_files/fhir_output.json', 'w', encoding='utf-8') as f:
310
+ f.write(json_output)
311
+
312
+ """
313
+
314
+ def extract_lforms_data(json_data):
315
+ if isinstance(json_data, str):
316
+ data = json.loads(json_data)
317
+ else:
318
+ data = json_data
319
+
320
+ extracted_info = {
321
+ "date_of_birth": None,
322
+ "sex": None,
323
+ "allergies": [],
324
+ "past_medical_history": [],
325
+ "medications": []
326
+ }
327
+
328
+ for item in data.get("items", []):
329
+ if item.get("question") == "ABOUT ME":
330
+ for subitem in item.get("items", []):
331
+ if subitem.get("question") == "DATE OF BIRTH":
332
+ extracted_info["date_of_birth"] = subitem.get("value")
333
+ elif subitem.get("question") == "BIOLOGICAL SEX":
334
+ extracted_info["sex"] = subitem.get("value", {}).get("text")
335
+
336
+ elif item.get("question") == "ALLERGIES":
337
+ for allergy_item in item.get("items", []):
338
+ if allergy_item.get("question") == "Allergies and Other Dangerous Reactions":
339
+ for subitem in allergy_item.get("items", []):
340
+ if subitem.get("question") == "Name" and "value" in subitem:
341
+ value = subitem["value"]
342
+ if isinstance(value, dict):
343
+ allergy_text = value.get("text")
344
+ else:
345
+ allergy_text = value
346
+ if allergy_text:
347
+ extracted_info["allergies"].append(allergy_text)
348
+
349
+ elif item.get("question") == "PAST MEDICAL HISTORY:":
350
+ for condition_item in item.get("items", []):
351
+ if condition_item.get("question") == "PAST MEDICAL HISTORY" and "value" in condition_item:
352
+ condition = extract_condition(condition_item)
353
+ if condition:
354
+ extracted_info["past_medical_history"].append(condition)
355
+
356
+ elif item.get("question") == "MEDICATIONS:":
357
+ medication = {}
358
+ for med_item in item.get("items", []):
359
+ if med_item.get("question") == "MEDICATIONS":
360
+ medication["name"] = extract_med_value(med_item)
361
+ elif med_item.get("question") == "Strength":
362
+ medication["strength"] = extract_med_value(med_item)
363
+ elif med_item.get("question") == "Instructions":
364
+ medication["instructions"] = extract_med_value(med_item)
365
+ if medication:
366
+ extracted_info["medications"].append(medication)
367
+
368
+ return extracted_info
369
+
370
+
371
+ def extract_condition(condition_item):
372
+ if isinstance(condition_item.get("value"), dict):
373
+ return condition_item["value"].get("text", "")
374
+ elif isinstance(condition_item.get("value"), str):
375
+ return condition_item["value"]
376
+ return ""
377
+
378
+ def extract_med_value(med_item):
379
+ if "value" not in med_item:
380
+ return ""
381
+ value = med_item["value"]
382
+ if isinstance(value, str):
383
+ return value
384
+ elif isinstance(value, dict):
385
+ return value.get("text", "")
386
+ return ""
387
+
388
+
389
+ ##run analyze located in ../dir
390
+ def analyze(request: gr.Request, deidentify_words):
391
+ set_user_health_files_directory(request)
392
+ if not folderpath:
393
+ print("folderpath value is empty. Skipping.")
394
+ return
395
+ # Set up environment variables
396
+ env_vars = os.environ.copy()
397
+ env_vars['FOLDERPATH'] = folderpath
398
+ if deidentify_words:
399
+ content = f"\nignore_words = '{deidentify_words}'\n"
400
+ file_path_variables2 = "../variables/variables2.py"
401
+ try:
402
+ with open(file_path_variables2, 'a') as file:
403
+ file.write(content)
404
+ print(f"Successfully appended deidentify_words to {file_path_variables2}")
405
+ except IOError as e:
406
+ error_message = f"IOError writing to variables2.py: {str(e)}"
407
+ print(error_message)
408
+ return error_message
409
+ except Exception as e:
410
+ error_message = f"Unexpected error writing to variables2.py: {str(e)}"
411
+ print(error_message)
412
+ return error_message
413
+ # Get the absolute path to the current script's directory
414
+ current_dir = os.path.dirname(os.path.abspath(__file__))
415
+
416
+ # Set up the paths
417
+ venv_dir = os.path.abspath(os.path.join(current_dir, '..', 'darnavenv'))
418
+ venv_python = os.path.join(venv_dir, 'bin', 'python3.10')
419
+ analyze_script = os.path.abspath(os.path.join(current_dir, '..', 'analyze.py'))
420
+
421
+ command = [venv_python, analyze_script]
422
+
423
+ try:
424
+ result = subprocess.run(command, env=env_vars, check=True, text=True, capture_output=True)
425
+ print("Running Analyzer output:", result.stdout)
426
+ return "🟢 Analysis completed successfully"
427
+ except subprocess.CalledProcessError as e:
428
+ print("Error running analyze.py:", e)
429
+ print("Error output:", e.stderr)
430
+
431
+
432
+ ##fetch age/sex in analyze module
433
+ def fetch_age_sex(request: gr.Request):
434
+ set_user_health_files_directory(request)
435
+ if not folderpath:
436
+ print("Directory value is empty. Skipping.")
437
+ return None, None, gr.update(visible=False), gr.update(visible=False)
438
+
439
+ ocr_files = f"{folderpath}/ocr_files"
440
+ try:
441
+ with open(f'{folderpath}/summary/chart.json', 'r') as file:
442
+ json_data = json.load(file)
443
+
444
+ extracted_info = extract_lforms_data(json.dumps(json_data))
445
+ sex = extracted_info.get('sex', None)
446
+ dob_str = extracted_info.get('date_of_birth', None)
447
+
448
+ age = None
449
+ if dob_str is not None:
450
+ try:
451
+ dob = datetime.strptime(dob_str, '%Y-%m-%d')
452
+ today = datetime.now()
453
+ age = today.year - dob.year
454
+
455
+ # Adjust age if birthday hasn't occurred this year
456
+ if (today.month, today.day) < (dob.month, dob.day):
457
+ age -= 1
458
+ except ValueError as e:
459
+ print(f"Error parsing date: {e}")
460
+
461
+ # Check if both age and sex are not None
462
+ if age is not None and sex is not None:
463
+ content = f"age = '{age}'\nsex = '{sex}'\n"
464
+ file_path_variables2 = f"../variables/variables2.py"
465
+ try:
466
+ with open(file_path_variables2, 'w') as file:
467
+ file.write(content)
468
+ except Exception as e:
469
+ print(f"Error writing to variables2.py: {str(e)}")
470
+ return None, None, gr.update(visible=False), gr.update(visible=False)
471
+
472
+ return f"Age: {age}\n Sex: {sex}\n", "🟢 Ready to analyze", gr.update(visible=True), gr.update(visible=True)
473
+ else:
474
+ return None, "🔴 Please update your age and sex in Darnahi Chartit", gr.update(visible=False), gr.update(visible=False)
475
+
476
+ except Exception as e:
477
+ return None, f"An error occurred: {str(e)}", gr.update(visible=False), gr.update(visible=False)
478
+
479
+
480
+
481
+
482
+
483
+ ####AI File server
484
+ def list_files(directory):
485
+ files = []
486
+ try:
487
+ # List files in the main directory
488
+ files.extend([f for f in os.listdir(directory) if os.path.isfile(os.path.join(directory, f))])
489
+
490
+ # List files in the AI wordcloud subdirectory
491
+ wordcloud_dir = os.path.join(directory, "wordclouds")
492
+ if os.path.isdir(wordcloud_dir):
493
+ wordcloud_files = [os.path.join("wordclouds", f) for f in os.listdir(wordcloud_dir) if os.path.isfile(os.path.join(wordcloud_dir, f))]
494
+ files.extend(wordcloud_files)
495
+
496
+ return files
497
+ except OSError as e:
498
+ #print(f"Pick a directory to list {directory}: {e}")
499
+ return []
500
+
501
+ def display_file(filename):
502
+ if not filename or isinstance(filename, gr.components.Dropdown):
503
+ return None, None
504
+
505
+ try:
506
+ file_path = os.path.join(directory, filename)
507
+ if os.path.exists(file_path):
508
+ if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.gif')):
509
+ return None, file_path
510
+ else:
511
+ with open(file_path, 'r') as file:
512
+ content = file.read()
513
+ return content, None
514
+ else:
515
+ print(f"File not found: {file_path}")
516
+ return None, None
517
+ except Exception as e:
518
+ print(f"Error displaying file {filename}: {e}")
519
+ return None, None
520
+
521
+ def refresh_file_list(request: gr.Request):
522
+ #checks for RAG dir and also refreshes list of files
523
+ set_user_directory(request)
524
+
525
+ file_choices = list_files(directory)
526
+
527
+ if os.path.isdir(os.path.join(directory, "chroma_storage")):
528
+ status = "🟢 RAG database successfully setup for Darnabot User"
529
+ else:
530
+ status = "🔴 RAG database needs to be set up for Darnabot User"
531
+
532
+ return gr.Dropdown(choices=file_choices), status
533
+
534
+ def update_display(filename):
535
+ if isinstance(filename, gr.components.Dropdown):
536
+ filename = filename.value
537
+ if not filename:
538
+ return gr.update(value="No file selected", visible=True), gr.update(value=None, visible=False)
539
+
540
+ content, image_path = display_file(filename)
541
+ if image_path:
542
+ return gr.update(value=None, visible=False), gr.update(value=image_path, visible=True)
543
+ elif content is not None:
544
+ return gr.update(value=content, visible=True), gr.update(value=None, visible=False)
545
+ else:
546
+ return gr.update(value="Error displaying file", visible=True), gr.update(value=None, visible=False)
547
+
548
+
549
+ ##SYMPTOM LOGGER
550
+
551
+ # Create a thread-local storage
552
+ local = threading.local()
553
+
554
+ # Function to get and connect to relevant database connection for current thread
555
+ def get_db():
556
+ if folderpath is None:
557
+ print("folderpath value is empty. Skipping. Please connect to your Darnahi Account.")
558
+ return None
559
+
560
+ try:
561
+ db_path = f"{folderpath}/summary/medical_records.db"
562
+ conn = sqlite3.connect(db_path)
563
+ return conn
564
+ except sqlite3.Error as e:
565
+ print(f"An error occurred while connecting to the database: {e}")
566
+ return None
567
+
568
+
569
+ def close_db():
570
+ if hasattr(local, "db") and local.db is not None:
571
+ local.db.close()
572
+ local.db = None
573
+
574
+ # Initialize the database
575
+ def init_db(request: gr.Request):
576
+
577
+ if folderpath is None:
578
+ print("folderpath value is empty. Skipping. Please connect to your Darnahi Account.")
579
+ return
580
+
581
+ global get_basic
582
+ get_basic(folderpath)
583
+
584
+ db = get_db()
585
+ if db is None:
586
+ return
587
+
588
+ try:
589
+ with db:
590
+ cursor = db.cursor()
591
+ cursor.execute('''
592
+ CREATE TABLE IF NOT EXISTS records (
593
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
594
+ date TEXT,
595
+ age INTEGER,
596
+ sex TEXT,
597
+ symptom TEXT,
598
+ past_medical_history TEXT,
599
+ medications TEXT,
600
+ image BLOB,
601
+ comment TEXT
602
+ )
603
+ ''')
604
+ print("Database initialized successfully.")
605
+ except sqlite3.Error as e:
606
+ print(f"An error occurred while initializing the database: {e}")
607
+ finally:
608
+ if db:
609
+ db.close()
610
+
611
+
612
+ def get_basic(folderpath):
613
+ # This function gets chartit summary
614
+ with open(f'{folderpath}/summary/chart.json', 'r') as file:
615
+ json_data = json.load(file)
616
+
617
+ extracted_info = extract_lforms_data(json.dumps(json_data))
618
+ json_output = json.dumps(extracted_info, indent=4)
619
+ write_text_to_pdf(folderpath, str(extracted_info))
620
+ with open(f'{folderpath}/ocr_files/fhir_output.json', 'w', encoding='utf-8') as f:
621
+ f.write(json_output)
622
+ return extracted_info
623
+
624
+ #duplicate as AI module but seems to relevant to keep
625
+ def calculate_age(dob):
626
+ if dob is not None:
627
+ today = datetime.now()
628
+ born = datetime.strptime(dob, "%Y-%m-%d")
629
+
630
+ return today.year - born.year - ((today.month, today.day) < (born.month, born.day))
631
+ return "Please update Chartit in you account"
632
+
633
+
634
+
635
+ #create PDF with container and margins
636
+ class PDF(FPDF):
637
+ def header(self):
638
+ self.set_font('Arial', 'B', 12)
639
+ self.cell(0, 10, 'Medical Record', 0, 1, 'C')
640
+ self.ln(10)
641
+
642
+ def footer(self):
643
+ self.set_y(-15)
644
+ self.set_font('Arial', 'I', 8)
645
+ self.cell(0, 10, f'Page {self.page_no()}/{{nb}}', 0, 0, 'C')
646
+
647
+ def create_pdf(record, image_data):
648
+ pdf = PDF()
649
+ pdf.alias_nb_pages()
650
+ pdf.add_page()
651
+ pdf.set_font("Arial", size=12)
652
+ pdf.set_auto_page_break(auto=True, margin=15)
653
+
654
+ # Set margin so that the comments dont go past margin
655
+ pdf.set_left_margin(10)
656
+
657
+ for key, value in record.items():
658
+ if key != 'image' and key != 'comment':
659
+ pdf.cell(0, 10, txt=f"{key}: {value}", ln=True)
660
+
661
+ pdf.ln(10)
662
+ pdf.set_font("Arial", 'B', size=12)
663
+ pdf.cell(0, 10, txt="Comment:", ln=True)
664
+ pdf.set_font("Arial", size=12)
665
+ pdf.multi_cell(0, 10, txt=record['comment'])
666
+
667
+ if image_data:
668
+ try:
669
+ image_bytes = base64.b64decode(image_data)
670
+
671
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
672
+ temp_file.write(image_bytes)
673
+ temp_file_path = temp_file.name
674
+
675
+ pdf.add_page()
676
+ pdf.image(temp_file_path, x=10, y=30, w=190)
677
+
678
+ os.unlink(temp_file_path)
679
+ except Exception as e:
680
+ pdf.ln(10)
681
+ pdf.cell(0, 10, txt=f"Error processing image: {e}", ln=True)
682
+
683
+ summary_dir = os.path.join(folderpath, "summary")
684
+ ocr_dir = os.path.join(folderpath, "ocr_files")
685
+
686
+
687
+ filename = os.path.join(summary_dir, f"record_{record['date'].replace(':', '-')}.pdf")
688
+ filename2 = os.path.join(ocr_dir, f"record_{record['date'].replace(':', '-')}.pdf")
689
+
690
+ pdf.output(filename)
691
+ pdf.output(filename2)
692
+ return filename, filename2
693
+
694
+ def write_text_to_pdf(directory, text):
695
+ pdf_buffer = BytesIO()
696
+ c = canvas.Canvas(pdf_buffer, pagesize=letter)
697
+ text_object = c.beginText(72, 750) # Start 1 inch from top
698
+ for line in text.split('\n'):
699
+ text_object.textLine(line)
700
+ c.drawText(text_object)
701
+ c.save()
702
+
703
+ # Save the PDF
704
+ with open(f'{directory}/ocr_files/fhir_data.pdf', 'wb') as f:
705
+ f.write(pdf_buffer.getvalue())
706
+
707
+ def submit_record(symptom, outputd, comment, file):
708
+ basic_info = get_basic(folderpath)
709
+ age = calculate_age(basic_info['date_of_birth'])
710
+ final_comment = outputd if outputd is not None else (comment if comment is not None else "")
711
+
712
+ record = {
713
+ 'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
714
+ 'age': age,
715
+ 'sex': basic_info['sex'],
716
+ 'symptom': symptom,
717
+ 'past_medical_history': json.dumps(basic_info['past_medical_history']),
718
+ 'medications': json.dumps(basic_info['medications']),
719
+ 'comment': final_comment
720
+ }
721
+
722
+ image_data = None
723
+ if file:
724
+ try:
725
+ # Read /encode file as base64
726
+ with open(file.name, "rb") as image_file:
727
+ image_data = base64.b64encode(image_file.read()).decode('utf-8')
728
+ except Exception as e:
729
+ return f"🔴 Error processing image: {e}"
730
+
731
+ with get_db() as conn:
732
+ cursor = conn.cursor()
733
+ cursor.execute('''
734
+ INSERT INTO records (date, age, sex, symptom, past_medical_history, medications, image, comment)
735
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?)
736
+ ''', (record['date'], record['age'], record['sex'], record['symptom'], record['past_medical_history'], record['medications'], image_data, final_comment))
737
+ conn.commit()
738
+
739
+ pdf_filename = create_pdf(record, image_data)
740
+
741
+ return f"🟢 Record submitted successfully. {pdf_filename}"
742
+
743
+ def fetch_records():
744
+ with get_db() as conn:
745
+ cursor = conn.cursor()
746
+ cursor.execute("SELECT id, date, symptom FROM records ORDER BY date DESC")
747
+ records = cursor.fetchall()
748
+ if not records:
749
+ return gr.Dropdown(choices=["No records available"], value="No records available")
750
+ choices = [f"{r[0]} - {r[1]} - {r[2]}" for r in records]
751
+ return gr.Dropdown(choices=choices, value=choices[0])
752
+
753
+ def display_record(selected_record):
754
+ if not selected_record or selected_record == "No records available":
755
+ return "Please select a record to display", None
756
+
757
+ record_id = int(selected_record.split(' - ')[0])
758
+ with get_db() as conn:
759
+ cursor = conn.cursor()
760
+ cursor.execute("SELECT * FROM records WHERE id = ?", (record_id,))
761
+ record = cursor.fetchone()
762
+
763
+ if not record:
764
+ return "Record not found", None
765
+
766
+ columns = ['id', 'date', 'age', 'sex', 'symptom', 'past_medical_history', 'medications', 'image', 'comment']
767
+ record_dict = {columns[i]: record[i] for i in range(len(columns))}
768
+
769
+ display_text = "\n".join([f"{k}: {v}" for k, v in record_dict.items() if k != 'image'])
770
+
771
+ if record_dict['image']:
772
+ try:
773
+ image_data = base64.b64decode(record_dict['image'])
774
+ img = Image.open(io.BytesIO(image_data))
775
+ with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as temp_file:
776
+ img.save(temp_file.name, 'PNG')
777
+ temp_file_path = temp_file.name
778
+ return display_text, temp_file_path
779
+ except Exception as e:
780
+ return f"{display_text}\n\nError displaying image: {e}", None
781
+ else:
782
+ return display_text, None
783
+
784
+ #toggle visibility and connect to relevant DB
785
+ def toggle_visibility(choice, request: gr.Request):
786
+ set_user_health_files_directory(request)
787
+ close_db()
788
+ init_db(request)
789
+
790
+ if choice == "new":
791
+ return gr.Row.update(visible=True), gr.Row.update(visible=False)
792
+ else:
793
+ return gr.Row.update(visible=False), gr.Row.update(visible=True)
794
+
795
+ #Using ai to write a note
796
+ class HealthMotivator:
797
+ async def get_motivation(self, symptom_info):
798
+ messages = [
799
+ {"role": "system", "content": "You are Darnabot, medical transcriber. Write a brief note with input and suggested first aid management only. Suggest doctor if complicated."},
800
+ {"role": "user", "content": f"Generate a brief note input: {symptom_info} only. Do not make up information."},
801
+ ]
802
+
803
+ try:
804
+ OLLAMA_HOST = os.environ.get('OLLAMA_HOST', 'http://localhost:11434')
805
+ async for part in await AsyncClient(host=OLLAMA_HOST).chat(model=f'{model}', messages=messages, stream=True):
806
+ yield part['message']['content']
807
+ except Exception as e:
808
+ yield f"Remember to take care of your health. Please see links below! Also download {model} from ollama. (Error: {str(e)})"
809
+
810
+
811
+
812
+ motivator = HealthMotivator()
813
+
814
+ async def symptom_note(symptom, symptom_info):
815
+ basic_info = get_basic(folderpath)
816
+ age = calculate_age(basic_info['date_of_birth'])
817
+ symptom_info = {
818
+ 'date': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
819
+ 'age': age,
820
+ 'sex': basic_info['sex'],
821
+ 'symptom': symptom,
822
+ 'past_medical_history': json.dumps(basic_info['past_medical_history']),
823
+ 'medications': json.dumps(basic_info['medications']),
824
+ 'comment': symptom_info
825
+ }
826
+
827
+ motivation = "See a doctor for Advice. This is only information. "
828
+ async for chunk in motivator.get_motivation(symptom_info):
829
+ motivation += chunk
830
+ yield motivation
831
+
832
+ #######################GRADIO UI
833
+
834
+ with gr.Blocks(theme='Taithrah/Minimal', css= "footer{display:none !important}") as demo:
835
+ with open('motivation.json', 'r') as file:
836
+ proverbs = json.load(file)
837
+ random_key = random.choice(list(proverbs.keys()))
838
+ proverb = proverbs[random_key]
839
+ gr.Markdown(f"""<div style='text-align: center; font-size: 1rem;'>
840
+ <i>{proverb}</i>
841
+ </div>
842
+ """)
843
+ with gr.Tab("DARNABOT"):
844
+ chatbot = gr.Chatbot(label="DARNAHI CONCIERGE 🛎️")
845
+ msg = gr.Textbox(label="Ask DARNABOT:", placeholder="How can I help?")
846
+ with gr.Row():
847
+ btn1 = gr.Button("Ask")
848
+ Clear = gr.ClearButton([msg, chatbot])
849
+
850
+
851
+
852
+ btn1.click(my_function, inputs=[msg, chatbot], outputs=[chatbot])
853
+ Clear.click(clear_conversation, outputs=[msg, chatbot])
854
+
855
+
856
+ with gr.Tab("RUN AI"):
857
+
858
+ gr.Markdown("## This section will run AI tools (~5min) on your medical records and do the following\n 1. Calculate Age using Darnahi Chartit Data\n 2. Scan through your previously uploaded records once\n 3. Run Image recognition and AI vision on it once to generate AI metadata\n 4. Generate Age and Sex based Recommendations using USPTF recommendations\n 5. Create summaries from your uploaded records that you can explore or download from file server tab\n 6. Create Wordclouds\n 7. Create structured and Unstructured data/ RAG for Darnabot to use so as to tailor its answers using your uploaded chunked data. \n\n")
859
+ with gr.Row():
860
+ fetch_button = gr.Button("Fetch Age and Sex")
861
+ with gr.Column(visible=False) as analysis_column:
862
+ deidentify_words = gr.Textbox(label="Enter information to deidentify", placeholder="names|email|address|phone")
863
+ analyze_button = gr.Button("RUN AI")
864
+
865
+ output1 = gr.Textbox(label="Age and Sex")
866
+ output2 = gr.Textbox(label="Alert")
867
+
868
+ fetch_button.click(
869
+ fn=fetch_age_sex,
870
+ inputs=[],
871
+ outputs=[output1, output2, analysis_column, analyze_button]
872
+ )
873
+
874
+ analyze_button.click(
875
+ fn=analyze,
876
+ inputs=[deidentify_words],
877
+ outputs=[output2]
878
+ )
879
+
880
+ with gr.Accordion(label="EXPLORE AI FILES)", open=False):
881
+
882
+ with gr.Row():
883
+ with gr.Row():
884
+ file_list = gr.Dropdown(label="Select a file", choices=list_files(directory))
885
+ refresh_button = gr.Button("Refresh List")
886
+ status_text = gr.Textbox(label="Database Status", interactive=False)
887
+
888
+ with gr.Row():
889
+ display_area = gr.Textbox(label="Explore Content", visible=True)
890
+ display_area2 = gr.Image(label="Image", visible=True)
891
+
892
+
893
+ file_list.change(
894
+ fn=update_display,
895
+ inputs=[file_list],
896
+ outputs=[display_area, display_area2]
897
+ )
898
+
899
+ refresh_button.click(
900
+ fn=refresh_file_list,
901
+ inputs=[],
902
+ outputs=[file_list, status_text]
903
+ )
904
+ with gr.Accordion(label="OTHER INFORMATIONAL LINKS)", open=False):
905
+
906
+ gr.HTML("""
907
+ <iframe src="https://www.uspreventiveservicestaskforce.org/webview/#!/" width="100%" height="580px"></iframe>
908
+ """)
909
+
910
+ gr.Markdown("## Are you up to date on Immunizations?\n See Immunization suggestions:")
911
+ gr.HTML("""
912
+ <iframe src="https://www.aafp.org/family-physician/patient-care/prevention-wellness/immunizations-vaccines/immunization-schedules/adult-immunization-schedule.html"
913
+ width="100%" height="500px"></iframe>
914
+ """)
915
+
916
+
917
+
918
+
919
+
920
+ with gr.Tab("⛨SYMPTOM LOGGER"):
921
+ with gr.Row():
922
+ create_new = gr.Button("Create New")
923
+ fetch_previous = gr.Button("Fetch Previous")
924
+
925
+ with gr.Column(visible=False) as new_record_row:
926
+ with gr.Row():
927
+ symptom = gr.Dropdown(["pain", "rash", "diarrhea", "discharge", "wound", "other"], label="Symptom")
928
+ comment = gr.Textbox(label="Details", placeholder="Rash since 2 days with discharge")
929
+ with gr.Row():
930
+ file = gr.File(label="Attach Image (optional)")
931
+ result = gr.Textbox(label="Alert")
932
+
933
+ outputd = gr.Markdown(label="Darnabot:")
934
+
935
+
936
+ with gr.Row():
937
+ btnw = gr.Button("GENERATE")
938
+ submit_btn = gr.Button("Save")
939
+ btnw.click(symptom_note, inputs=(symptom, comment), outputs=[outputd])
940
+
941
+
942
+
943
+
944
+ with gr.Column(visible=False) as explore_records_row:
945
+ with gr.Row():
946
+ records_dropdown = gr.Dropdown(label="Select Record", choices=["No records available"])
947
+ with gr.Column():
948
+ fetch_btn = gr.Button("Refresh List")
949
+ display_btn = gr.Button("Display Selected Record")
950
+ with gr.Row():
951
+ record_display = gr.Textbox(label="Record Details")
952
+ image_display = gr.Image(label="Attached Image")
953
+
954
+ create_new.click(
955
+ toggle_visibility,
956
+ inputs=gr.Text(value="new", visible=False),
957
+ outputs=[new_record_row, explore_records_row]
958
+ )
959
+
960
+ fetch_previous.click(
961
+ toggle_visibility,
962
+ inputs=gr.Text(value="previous", visible=False),
963
+ outputs=[new_record_row, explore_records_row]
964
+ )
965
+
966
+ submit_btn.click(submit_record, inputs=[symptom, outputd, comment, file], outputs=result)
967
+ fetch_btn.click(fetch_records, outputs=records_dropdown)
968
+ display_btn.click(display_record, inputs=[records_dropdown], outputs=[record_display, image_display])
969
+
970
+
971
+
972
+ if __name__ == "__main__":
973
+
974
+ demo.launch(server_name='0.0.0.0', server_port=3012, pwa=True, share=False)
975
+
976
+