Spaces:
Paused
Paused
Upload 201 files
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +43 -0
- Dockerfile +14 -0
- Health_files/ocr_files/Darna_tesseract/USPTF_Intent.txt +28 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/data_level0.bin +3 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/header.bin +3 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/index_metadata.pickle +3 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/length.bin +3 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/18f67e52-481a-4689-a2c2-9dcb04476f08/link_lists.bin +3 -0
- Health_files/ocr_files/Darna_tesseract/chroma_storage/chroma.sqlite3 +3 -0
- Health_files/ocr_files/Darna_tesseract/combined_output.json +3 -0
- Health_files/ocr_files/Darna_tesseract/darnahi_ocr.png +0 -0
- Health_files/ocr_files/Darna_tesseract/deidentified_records.txt +49 -0
- Health_files/ocr_files/Darna_tesseract/ocr_results.txt +48 -0
- Health_files/ocr_files/Darna_tesseract/wordcloud_summary.json +8 -0
- Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_medications_wordcloud.png +3 -0
- Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_past_medical_history_wordcloud.png +3 -0
- Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_screening_wordcloud.png +3 -0
- Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_summary_wordcloud.png +3 -0
- Health_files/ocr_files/combined_output.json +3 -0
- Health_files/ocr_files/fhir_data.pdf +68 -0
- Health_files/ocr_files/fhir_output.json +42 -0
- Health_files/ocr_files/record_2025-08-07 04-12-02.pdf +0 -0
- Health_files/summary/HHI404.PDF +3 -0
- Health_files/summary/chart.json +0 -0
- Health_files/summary/chest.jpeg +0 -0
- Health_files/summary/darna_sample.DCM +3 -0
- Health_files/summary/medical_records.db +0 -0
- Health_files/summary/sample.xml +133 -0
- Health_files/summary/summary.pdf +0 -0
- Health_files/upload/Welcome.txt +29 -0
- Health_files2/ocr_files/Darna_tesseract/USPTF_Intent.txt +4 -0
- Health_files2/ocr_files/Darna_tesseract/darnahi_ocr.png +3 -0
- Health_files2/ocr_files/Darna_tesseract/deidentified_records.txt +5 -0
- Health_files2/ocr_files/Darna_tesseract/ocr_results.txt +2 -0
- Health_files2/ocr_files/Darna_tesseract/wordcloud_summary.json +8 -0
- Health_files2/ocr_files/fhir_data.pdf +68 -0
- Health_files2/ocr_files/fhir_output.json +13 -0
- Health_files2/ocr_files/record_2024-10-01 06-41-08.pdf +0 -0
- Health_files2/summary/chart.json +991 -0
- Health_files2/summary/darna_sample.DCM +3 -0
- Health_files2/summary/medical_records.db +0 -0
- Health_files2/summary/record_2024-10-01 06-41-08.pdf +0 -0
- Health_files2/summary/sample.xml +133 -0
- Health_files2/summary/summary.pdf +0 -0
- Health_files2/upload/Welcome.txt +25 -0
- README.md +4 -4
- analyze.py +987 -0
- app.py +1025 -0
- darnabot/darnabot.log +11 -0
- 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
|
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_past_medical_history_wordcloud.png
ADDED
|
Git LFS Details
|
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_screening_wordcloud.png
ADDED
|
Git LFS Details
|
Health_files/ocr_files/Darna_tesseract/wordclouds/darnahi_summary_wordcloud.png
ADDED
|
Git LFS Details
|
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 |
+
--> (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 --> (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> "Gastroenterology"</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
|
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 |
+
--> (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 --> (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> "Gastroenterology"</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:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 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 |
+
|