medicafe 0.250813.1__tar.gz → 0.250814.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- medicafe-0.250814.3/MANIFEST.in +24 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot.bat +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot.py +48 -28
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Crosswalk_Library.py +6 -6
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Preprocessor_lib.py +90 -26
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_UI.py +76 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_dataformat_library.py +0 -2
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_smart_import.py +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/__init__.py +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/update_medicafe.py +69 -7
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/__init__.py +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_core.py +0 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/__init__.py +1 -1
- {medicafe-0.250813.1/medicafe.egg-info → medicafe-0.250814.3}/PKG-INFO +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3/medicafe.egg-info}/PKG-INFO +1 -1
- {medicafe-0.250813.1 → medicafe-0.250814.3}/setup.py +2 -2
- medicafe-0.250813.1/MANIFEST.in +0 -4
- {medicafe-0.250813.1 → medicafe-0.250814.3}/LICENSE +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/get_medicafe_version.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/update_json.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/MediLink_ConfigLoader.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/__main__.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_core_backup.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_factory.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/core_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/graphql_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/logging_config.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/logging_demo.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/migration_helpers.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/smart_import.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/submission_index.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/InsuranceTypeService.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_encoder.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_encoder_library.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_ClaimStatus.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_DataMgmt.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Deductible.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Display_Utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_PatientProcessor.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Up.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_insurance_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_main.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_smart_import.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/gmail_http_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/gmail_oauth_utils.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/insurance_type_integration_test.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/openssl.cnf +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_cob_library.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_timing.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_validation.py +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/webapp.html +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/README.md +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/entry_points.txt +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.250813.1 → medicafe-0.250814.3}/setup.cfg +0 -0
@@ -0,0 +1,24 @@
|
|
1
|
+
include MediBot/MediBot.bat
|
2
|
+
include MediLink/openssl.cnf
|
3
|
+
include MediLink/*.html
|
4
|
+
include MediCafe/*.py
|
5
|
+
|
6
|
+
# Exclude CSV files and other data files that slow down the build
|
7
|
+
exclude *.csv
|
8
|
+
exclude *.log
|
9
|
+
exclude *.tmp
|
10
|
+
exclude *.bak
|
11
|
+
exclude __pycache__/*
|
12
|
+
exclude *.pyc
|
13
|
+
exclude *.pyo
|
14
|
+
exclude .git/*
|
15
|
+
exclude .gitignore
|
16
|
+
exclude .DS_Store
|
17
|
+
exclude Thumbs.db
|
18
|
+
exclude archive/*
|
19
|
+
exclude ERA_TEST_DUMP/*
|
20
|
+
exclude input/*
|
21
|
+
exclude tmpdl/*
|
22
|
+
exclude json/*
|
23
|
+
exclude docs/reports/*
|
24
|
+
exclude Installers/*
|
@@ -772,7 +772,7 @@ if "!internet_available!"=="0" (
|
|
772
772
|
pause >nul
|
773
773
|
goto troubleshooting_menu
|
774
774
|
)
|
775
|
-
set "rollback_version=0.
|
775
|
+
set "rollback_version=0.250813.1"
|
776
776
|
echo Forcing reinstall of %medicafe_package%==%rollback_version% with no dependencies...
|
777
777
|
python -m pip install --no-deps --force-reinstall %medicafe_package%==%rollback_version%
|
778
778
|
if errorlevel 1 (
|
@@ -608,48 +608,68 @@ if __name__ == "__main__":
|
|
608
608
|
existing_patients, patients_to_process = MediBot_Preprocessor.check_existing_patients(selected_patient_ids, _ac().get_mapat_med_path() if _ac() else '')
|
609
609
|
|
610
610
|
if existing_patients:
|
611
|
-
print("\nNOTE: The following patient(s) already EXIST in the system and \n will be excluded from processing:")
|
612
|
-
# BUG There a "ERROR: 'str' object has no attribute 'strftime'" that shows up after this somewhere.
|
613
611
|
# Collect surgery dates and patient info for existing patients
|
614
612
|
patient_info = []
|
615
613
|
for patient_id, patient_name in existing_patients:
|
616
614
|
try:
|
617
|
-
|
618
|
-
if
|
619
|
-
|
620
|
-
|
615
|
+
# Find the row for this patient
|
616
|
+
patient_row = next((row for row in csv_data if row.get(reverse_mapping['Patient ID #2']) == patient_id), None)
|
617
|
+
if patient_row is None:
|
618
|
+
raise ValueError("Patient row not found for patient ID: {}".format(patient_id))
|
619
|
+
|
620
|
+
# Get all surgery dates for this patient
|
621
|
+
all_surgery_dates = patient_row.get('_all_surgery_dates', [patient_row.get('Surgery Date')])
|
622
|
+
surgery_date_to_diagnosis = patient_row.get('_surgery_date_to_diagnosis', {})
|
623
|
+
|
624
|
+
# Create entries for each surgery date with its specific diagnosis code
|
625
|
+
for surgery_date in all_surgery_dates:
|
626
|
+
diagnosis_code = surgery_date_to_diagnosis.get(surgery_date, 'N/A')
|
627
|
+
patient_info.append((surgery_date, patient_name, patient_id, diagnosis_code, patient_row))
|
628
|
+
|
621
629
|
except Exception as e:
|
622
|
-
MediLink_ConfigLoader.log("Warning: Error retrieving
|
623
|
-
patient_info.append(('Unknown Date', patient_name, patient_id)) # Append with 'Unknown Date' if there's an error
|
624
|
-
|
625
|
-
#
|
626
|
-
|
627
|
-
|
628
|
-
|
629
|
-
|
630
|
-
|
631
|
-
|
632
|
-
# Print the sorted patient info
|
633
|
-
for index, (surgery_date, patient_name, patient_id) in enumerate(patient_info):
|
634
|
-
# Format surgery_date safely whether it's a datetime/date or a string
|
635
|
-
# This guards the observed "'str' object has no attribute 'strftime'" error on XP.
|
636
|
-
try:
|
637
|
-
formatted_date = surgery_date.strftime('%m-%d')
|
638
|
-
except Exception:
|
639
|
-
formatted_date = str(surgery_date)
|
640
|
-
print("{:03d}: {} (ID: {}) {}".format(index + 1, formatted_date, patient_id, patient_name))
|
630
|
+
MediLink_ConfigLoader.log("Warning: Error retrieving data for patient ID '{}': {}".format(patient_id, e), level="WARNING")
|
631
|
+
patient_info.append(('Unknown Date', patient_name, patient_id, 'N/A', None)) # Append with 'Unknown Date' if there's an error
|
632
|
+
|
633
|
+
# Display existing patients table using the enhanced display function
|
634
|
+
MediBot_UI.display_enhanced_patient_table(
|
635
|
+
patient_info,
|
636
|
+
"NOTE: The following patient(s) already EXIST in the system but may have new dates of service.\n Their diagnosis codes may need to be updated manually by the user to the following list:",
|
637
|
+
show_line_numbers=False
|
638
|
+
)
|
641
639
|
|
642
640
|
# Update csv_data to exclude existing patients
|
643
641
|
# TODO: Update this logic to handle patients that exist but need new charges added.
|
644
642
|
csv_data = [row for row in csv_data if row[reverse_mapping['Patient ID #2']] in patients_to_process]
|
645
|
-
|
646
|
-
|
643
|
+
|
644
|
+
# Show NEW patients that will be processed (if any)
|
645
|
+
if patients_to_process:
|
646
|
+
# Collect surgery dates and patient info for NEW patients
|
647
|
+
new_patient_info = []
|
648
|
+
for row in csv_data:
|
649
|
+
patient_id = row.get(reverse_mapping['Patient ID #2'])
|
650
|
+
patient_name = row.get(reverse_mapping['Patient Name'])
|
651
|
+
|
652
|
+
# Get all surgery dates for this patient
|
653
|
+
all_surgery_dates = row.get('_all_surgery_dates', [row.get('Surgery Date')])
|
654
|
+
surgery_date_to_diagnosis = row.get('_surgery_date_to_diagnosis', {})
|
655
|
+
|
656
|
+
# Create entries for each surgery date with its specific diagnosis code
|
657
|
+
for surgery_date in all_surgery_dates:
|
658
|
+
diagnosis_code = surgery_date_to_diagnosis.get(surgery_date, 'N/A')
|
659
|
+
new_patient_info.append((surgery_date, patient_name, patient_id, diagnosis_code, row))
|
660
|
+
|
661
|
+
# Display new patients table using the enhanced display function
|
662
|
+
MediBot_UI.display_enhanced_patient_table(
|
663
|
+
new_patient_info,
|
664
|
+
"NOTE: The following NEW patient(s) will be automatically entered into Medisoft:",
|
665
|
+
show_line_numbers=True
|
666
|
+
)
|
647
667
|
|
648
668
|
# Check if there are patients left to process
|
649
669
|
if len(patients_to_process) == 0:
|
650
670
|
proceed = input("\nAll patients have been processed. Continue anyway?: ").lower().strip() in ['yes', 'y']
|
651
671
|
else:
|
652
|
-
proceed = input("\nDo you want to proceed with
|
672
|
+
proceed = input("\nDo you want to proceed with entering {} new patient(s) into Medisoft? (yes/no): ".format(len(patients_to_process))).lower().strip() in ['yes', 'y']
|
653
673
|
|
654
674
|
# TODO: Here is where we need to add the step where we move to MediBot_Charges.
|
655
675
|
# The return is an enriched dataset to be picked up by MediBot which means we need to return:
|
@@ -4,17 +4,13 @@ Core crosswalk library for MediBot
|
|
4
4
|
Handles crosswalk operations and API interactions.
|
5
5
|
"""
|
6
6
|
|
7
|
-
import os
|
8
7
|
import sys
|
9
|
-
import csv
|
10
|
-
import json
|
11
|
-
import re
|
12
|
-
from datetime import datetime
|
13
8
|
|
14
9
|
# Use core utilities for standardized imports
|
15
10
|
from MediCafe.core_utils import (
|
16
11
|
import_medibot_module,
|
17
|
-
get_config_loader_with_fallback
|
12
|
+
get_config_loader_with_fallback,
|
13
|
+
smart_import
|
18
14
|
)
|
19
15
|
|
20
16
|
# Initialize configuration loader with fallback
|
@@ -45,6 +41,10 @@ else:
|
|
45
41
|
update_crosswalk_with_new_payer_id = None
|
46
42
|
load_and_parse_z_data = None
|
47
43
|
|
44
|
+
# Import API functions using centralized import pattern
|
45
|
+
MediLink_API_v3 = smart_import(['MediCafe.api_core', 'MediCafe.api_core_backup'])
|
46
|
+
fetch_payer_name_from_api = getattr(MediLink_API_v3, 'fetch_payer_name_from_api', None) if MediLink_API_v3 else None
|
47
|
+
|
48
48
|
# Module-level cache to prevent redundant API calls
|
49
49
|
_api_cache = {}
|
50
50
|
|
@@ -262,7 +262,7 @@ def add_columns(csv_data, column_headers):
|
|
262
262
|
|
263
263
|
# Extracting the list to a variable for future refactoring:
|
264
264
|
def filter_rows(csv_data):
|
265
|
-
# TODO: This should be
|
265
|
+
# TODO: This should be written in the crosswalk and not hardcoded here.
|
266
266
|
excluded_insurance = {'AETNA', 'AETNA MEDICARE', 'HUMANA MED HMO'}
|
267
267
|
csv_data[:] = [row for row in csv_data if row.get('Patient ID') and row.get('Primary Insurance') not in excluded_insurance]
|
268
268
|
|
@@ -515,17 +515,51 @@ def convert_surgery_date(csv_data):
|
|
515
515
|
def sort_and_deduplicate(csv_data):
|
516
516
|
# Create a dictionary to hold unique patients based on Patient ID
|
517
517
|
unique_patients = {}
|
518
|
+
# Create a dictionary to store multiple surgery dates per patient
|
519
|
+
patient_surgery_dates = {}
|
518
520
|
|
519
521
|
# Iterate through the CSV data and populate the unique_patients dictionary
|
520
522
|
for row in csv_data:
|
521
523
|
patient_id = row.get('Patient ID')
|
524
|
+
surgery_date = row.get('Surgery Date')
|
525
|
+
|
522
526
|
if patient_id not in unique_patients:
|
523
527
|
unique_patients[patient_id] = row
|
528
|
+
patient_surgery_dates[patient_id] = [surgery_date]
|
524
529
|
else:
|
525
530
|
# If the patient ID already exists, compare surgery dates
|
526
531
|
existing_row = unique_patients[patient_id]
|
527
|
-
|
532
|
+
existing_date = existing_row['Surgery Date']
|
533
|
+
|
534
|
+
# Keep the most current demographic data (later surgery date takes precedence)
|
535
|
+
if surgery_date > existing_date:
|
536
|
+
# Store the old row's surgery date before replacing
|
537
|
+
old_date = existing_row['Surgery Date']
|
538
|
+
patient_surgery_dates[patient_id].append(old_date)
|
539
|
+
# Replace with newer row (better demographics)
|
528
540
|
unique_patients[patient_id] = row
|
541
|
+
# Update the surgery dates list to reflect the new primary date
|
542
|
+
patient_surgery_dates[patient_id] = [surgery_date] + [d for d in patient_surgery_dates[patient_id] if d != surgery_date]
|
543
|
+
else:
|
544
|
+
# Add this surgery date to the list for this patient
|
545
|
+
if surgery_date not in patient_surgery_dates[patient_id]:
|
546
|
+
patient_surgery_dates[patient_id].append(surgery_date)
|
547
|
+
|
548
|
+
# Store the surgery dates information in the first row of each patient for later access
|
549
|
+
for patient_id, row in unique_patients.items():
|
550
|
+
# Convert surgery dates to strings for consistent storage
|
551
|
+
surgery_date_strings = []
|
552
|
+
for date in patient_surgery_dates[patient_id]:
|
553
|
+
if isinstance(date, datetime):
|
554
|
+
if date == datetime.min:
|
555
|
+
surgery_date_strings.append('MISSING')
|
556
|
+
else:
|
557
|
+
surgery_date_strings.append(date.strftime('%m-%d-%Y'))
|
558
|
+
else:
|
559
|
+
surgery_date_strings.append(str(date) if date else 'MISSING')
|
560
|
+
|
561
|
+
row['_all_surgery_dates'] = sorted(surgery_date_strings)
|
562
|
+
row['_primary_surgery_date'] = row['Surgery Date'] # Keep track of which date has the demographics
|
529
563
|
|
530
564
|
# Convert the unique_patients dictionary back to a list and sort it
|
531
565
|
csv_data[:] = sorted(unique_patients.values(), key=lambda x: (x['Surgery Date'], x.get('Patient Last', '').strip())) # TODO Does this need to be sorted twice? once before and once after?
|
@@ -542,7 +576,7 @@ def combine_fields(csv_data):
|
|
542
576
|
if surgery_date == datetime.min:
|
543
577
|
row['Surgery Date'] = 'MISSING'
|
544
578
|
else:
|
545
|
-
row['Surgery Date'] = surgery_date.strftime('%m
|
579
|
+
row['Surgery Date'] = surgery_date.strftime('%m-%d-%Y')
|
546
580
|
elif surgery_date:
|
547
581
|
# Already a non-empty string
|
548
582
|
row['Surgery Date'] = str(surgery_date)
|
@@ -1096,7 +1130,7 @@ def update_diagnosis_codes(csv_data):
|
|
1096
1130
|
updated_count = 0
|
1097
1131
|
|
1098
1132
|
# PERFORMANCE OPTIMIZATION: Single pass through CSV data with pre-processed lookups
|
1099
|
-
# Update the "Default Diagnosis #1" column in the CSV data
|
1133
|
+
# Update the "Default Diagnosis #1" column in the CSV data and store diagnosis codes for all surgery dates
|
1100
1134
|
for row_num, row in enumerate(csv_data, start=1):
|
1101
1135
|
patient_id = row.get('Patient ID', '').strip()
|
1102
1136
|
# Use pre-processed patient ID lookup for efficiency
|
@@ -1104,31 +1138,61 @@ def update_diagnosis_codes(csv_data):
|
|
1104
1138
|
continue # Skip rows that do not match any patient ID
|
1105
1139
|
|
1106
1140
|
MediLink_ConfigLoader.log("Processing row number {}.".format(row_num), level="DEBUG")
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1110
|
-
|
1111
|
-
|
1141
|
+
|
1142
|
+
# Get all surgery dates for this patient
|
1143
|
+
all_surgery_dates = row.get('_all_surgery_dates', [row.get('Surgery Date')])
|
1144
|
+
|
1145
|
+
# Create a mapping of surgery dates to diagnosis codes for this patient
|
1146
|
+
surgery_date_to_diagnosis = {}
|
1147
|
+
|
1112
1148
|
if patient_id in all_patient_data:
|
1113
|
-
|
1114
|
-
|
1115
|
-
|
1116
|
-
|
1117
|
-
|
1118
|
-
|
1119
|
-
|
1120
|
-
|
1121
|
-
|
1122
|
-
|
1123
|
-
MediLink_ConfigLoader.log("Missing diagnosis mapping for '{}', using fallback code '{}'".format(
|
1124
|
-
diagnosis_code, medisoft_shorthand), level="WARNING")
|
1125
|
-
MediLink_ConfigLoader.log("Converted diagnosis code to Medisoft shorthand: {}".format(medisoft_shorthand), level="DEBUG")
|
1149
|
+
# Process each surgery date for this patient
|
1150
|
+
for surgery_date in all_surgery_dates:
|
1151
|
+
# Convert surgery date to string format for lookup
|
1152
|
+
try:
|
1153
|
+
if hasattr(surgery_date, 'strftime'):
|
1154
|
+
surgery_date_str = surgery_date.strftime('%m-%d-%Y')
|
1155
|
+
else:
|
1156
|
+
surgery_date_str = str(surgery_date)
|
1157
|
+
except Exception:
|
1158
|
+
surgery_date_str = str(surgery_date)
|
1126
1159
|
|
1127
|
-
|
1128
|
-
|
1129
|
-
|
1160
|
+
MediLink_ConfigLoader.log("Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
|
1161
|
+
|
1162
|
+
if surgery_date_str in all_patient_data[patient_id]:
|
1163
|
+
diagnosis_code, left_or_right_eye, femto_yes_or_no = all_patient_data[patient_id][surgery_date_str]
|
1164
|
+
MediLink_ConfigLoader.log("Found diagnosis data for Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
|
1165
|
+
|
1166
|
+
# Convert diagnosis code to Medisoft shorthand format.
|
1167
|
+
medisoft_shorthand = diagnosis_to_medisoft.get(diagnosis_code, None)
|
1168
|
+
if medisoft_shorthand is None and diagnosis_code:
|
1169
|
+
# Use fallback logic for missing mapping
|
1170
|
+
defaulted_code = diagnosis_code.lstrip('H').lstrip('T8').replace('.', '')[-5:]
|
1171
|
+
medisoft_shorthand = defaulted_code
|
1172
|
+
MediLink_ConfigLoader.log("Missing diagnosis mapping for '{}', using fallback code '{}'".format(
|
1173
|
+
diagnosis_code, medisoft_shorthand), level="WARNING")
|
1174
|
+
MediLink_ConfigLoader.log("Converted diagnosis code to Medisoft shorthand: {}".format(medisoft_shorthand), level="DEBUG")
|
1175
|
+
|
1176
|
+
surgery_date_to_diagnosis[surgery_date_str] = medisoft_shorthand
|
1177
|
+
else:
|
1178
|
+
MediLink_ConfigLoader.log("No matching surgery date found for Patient ID: {} on date {}.".format(patient_id, surgery_date_str), level="INFO")
|
1179
|
+
surgery_date_to_diagnosis[surgery_date_str] = 'N/A'
|
1180
|
+
|
1181
|
+
# Store the diagnosis mapping for all surgery dates
|
1182
|
+
row['_surgery_date_to_diagnosis'] = surgery_date_to_diagnosis
|
1183
|
+
|
1184
|
+
# Set the primary diagnosis code (for the main surgery date)
|
1185
|
+
primary_surgery_date = row.get('Surgery Date')
|
1186
|
+
# Convert primary surgery date to string for lookup
|
1187
|
+
if isinstance(primary_surgery_date, datetime):
|
1188
|
+
primary_surgery_date_str = primary_surgery_date.strftime('%m-%d-%Y')
|
1130
1189
|
else:
|
1131
|
-
|
1190
|
+
primary_surgery_date_str = str(primary_surgery_date)
|
1191
|
+
primary_diagnosis = surgery_date_to_diagnosis.get(primary_surgery_date_str, 'N/A')
|
1192
|
+
row['Default Diagnosis #1'] = primary_diagnosis
|
1193
|
+
|
1194
|
+
updated_count += 1
|
1195
|
+
MediLink_ConfigLoader.log("Updated row number {} with diagnosis codes for {} surgery dates.".format(row_num, len(all_surgery_dates)), level="INFO")
|
1132
1196
|
else:
|
1133
1197
|
MediLink_ConfigLoader.log("Patient ID: {} not found in DOCX data for row {}.".format(patient_id, row_num), level="INFO")
|
1134
1198
|
|
@@ -1,7 +1,8 @@
|
|
1
1
|
#MediBot_UI.py
|
2
|
-
import ctypes, time, re
|
2
|
+
import ctypes, time, re
|
3
3
|
from ctypes import wintypes
|
4
4
|
from sys import exit
|
5
|
+
from datetime import datetime
|
5
6
|
|
6
7
|
# Set up paths using core utilities
|
7
8
|
|
@@ -27,6 +28,80 @@ except ImportError:
|
|
27
28
|
from MediCafe.core_utils import create_config_cache
|
28
29
|
_get_config, (_config_cache, _crosswalk_cache) = create_config_cache()
|
29
30
|
|
31
|
+
def display_enhanced_patient_table(patient_info, title, show_line_numbers=True):
|
32
|
+
"""
|
33
|
+
Display an enhanced patient table with multiple surgery dates and diagnosis codes.
|
34
|
+
|
35
|
+
Args:
|
36
|
+
patient_info: List of tuples (surgery_date, patient_name, patient_id, diagnosis_code, patient_row)
|
37
|
+
title: Title for the table section
|
38
|
+
show_line_numbers: Whether to show line numbers (True for new patients, False for existing)
|
39
|
+
"""
|
40
|
+
if not patient_info:
|
41
|
+
return
|
42
|
+
|
43
|
+
print(title)
|
44
|
+
print()
|
45
|
+
|
46
|
+
# Sort by surgery date first and then by patient name
|
47
|
+
patient_info.sort(key=lambda x: (x[0], x[1]))
|
48
|
+
|
49
|
+
# Calculate column widths for proper alignment
|
50
|
+
max_patient_id_len = max(len(str(pid)) for _, _, pid, _, _ in patient_info)
|
51
|
+
max_patient_name_len = max(len(pname) for _, pname, _, _, _ in patient_info)
|
52
|
+
max_diagnosis_len = max(len(dcode) for _, _, _, dcode, _ in patient_info)
|
53
|
+
|
54
|
+
# Ensure minimum widths for readability
|
55
|
+
max_patient_id_len = max(max_patient_id_len, 5) # 5-digit ID max
|
56
|
+
max_patient_name_len = max(max_patient_name_len, 12) # "Patient Name" header
|
57
|
+
max_diagnosis_len = max(max_diagnosis_len, 10) # "Diagnosis" header
|
58
|
+
|
59
|
+
# Print the sorted patient info with multiple surgery dates
|
60
|
+
current_patient = None
|
61
|
+
line_number = 1
|
62
|
+
|
63
|
+
for surgery_date, patient_name, patient_id, diagnosis_code, patient_row in patient_info:
|
64
|
+
# Format surgery_date safely whether it's a datetime/date or a string
|
65
|
+
try:
|
66
|
+
if isinstance(surgery_date, datetime):
|
67
|
+
formatted_date = surgery_date.strftime('%m-%d')
|
68
|
+
else:
|
69
|
+
# Handle string dates - this should be the Surgery Date Display field
|
70
|
+
formatted_date = str(surgery_date)
|
71
|
+
# If it's a date string like "12-25-2023", format it as "12-25"
|
72
|
+
if '-' in formatted_date and len(formatted_date.split('-')) == 3:
|
73
|
+
try:
|
74
|
+
parts = formatted_date.split('-')
|
75
|
+
formatted_date = "{}-{}".format(parts[0], parts[1])
|
76
|
+
except:
|
77
|
+
pass # Use original string if parsing fails
|
78
|
+
except Exception:
|
79
|
+
formatted_date = str(surgery_date)
|
80
|
+
|
81
|
+
# Transform diagnosis code display: show "-Not Found-" instead of "N/A"
|
82
|
+
display_diagnosis = "-Not Found-" if diagnosis_code == "N/A" else diagnosis_code
|
83
|
+
|
84
|
+
# Check if this is the same patient as the previous line
|
85
|
+
if current_patient == patient_id:
|
86
|
+
# Secondary surgery date - indent and show dashes for patient info
|
87
|
+
# Use exact character count matching for dashes
|
88
|
+
patient_id_dashes = '-' * len(str(patient_id))
|
89
|
+
patient_name_dashes = '-' * len(patient_name)
|
90
|
+
|
91
|
+
secondary_format = " {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
92
|
+
print(secondary_format.format(formatted_date, patient_id_dashes, patient_name_dashes, display_diagnosis))
|
93
|
+
else:
|
94
|
+
# New patient - show full patient info
|
95
|
+
current_patient = patient_id
|
96
|
+
if show_line_numbers:
|
97
|
+
primary_format = "{:03d}: {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
98
|
+
print(primary_format.format(line_number, formatted_date, str(patient_id), patient_name, display_diagnosis))
|
99
|
+
line_number += 1
|
100
|
+
else:
|
101
|
+
# For existing patients, don't show line numbers
|
102
|
+
primary_format = " {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
103
|
+
print(primary_format.format(formatted_date, str(patient_id), patient_name, display_diagnosis))
|
104
|
+
|
30
105
|
# Function to check if a specific key is pressed
|
31
106
|
def _get_vk_codes():
|
32
107
|
"""Get VK codes from config."""
|
@@ -7,7 +7,7 @@ new centralized smart import system, eliminating sys.path manipulation
|
|
7
7
|
and circular import risks.
|
8
8
|
"""
|
9
9
|
|
10
|
-
import os,
|
10
|
+
import os, sys
|
11
11
|
|
12
12
|
# Add workspace directory to Python path for MediCafe imports
|
13
13
|
current_dir = os.path.dirname(os.path.abspath(__file__))
|
@@ -1,4 +1,5 @@
|
|
1
1
|
#update_medicafe.py
|
2
|
+
# Version: 1.0.0
|
2
3
|
import subprocess, sys, time, platform, os, shutil, random
|
3
4
|
|
4
5
|
# Safe import for pkg_resources with fallback
|
@@ -305,6 +306,7 @@ def upgrade_package(package, retries=4, delay=2, target_version=None): # Update
|
|
305
306
|
def get_installed_version_fresh(package):
|
306
307
|
"""Get installed version using a fresh subprocess to avoid pkg_resources cache issues."""
|
307
308
|
try:
|
309
|
+
# First try pip show
|
308
310
|
process = subprocess.Popen(
|
309
311
|
[sys.executable, '-m', 'pip', 'show', package],
|
310
312
|
stdout=subprocess.PIPE,
|
@@ -315,6 +317,21 @@ def upgrade_package(package, retries=4, delay=2, target_version=None): # Update
|
|
315
317
|
for line in stdout.decode().splitlines():
|
316
318
|
if line.startswith("Version:"):
|
317
319
|
return line.split(":", 1)[1].strip()
|
320
|
+
|
321
|
+
# If pip show fails, try pkg_resources in a fresh subprocess
|
322
|
+
try:
|
323
|
+
import subprocess
|
324
|
+
process = subprocess.Popen(
|
325
|
+
[sys.executable, '-c', 'import pkg_resources; print(pkg_resources.get_distribution("{}").version)'.format(package)],
|
326
|
+
stdout=subprocess.PIPE,
|
327
|
+
stderr=subprocess.PIPE
|
328
|
+
)
|
329
|
+
stdout, stderr = process.communicate()
|
330
|
+
if process.returncode == 0:
|
331
|
+
return stdout.decode().strip()
|
332
|
+
except Exception:
|
333
|
+
pass
|
334
|
+
|
318
335
|
return None
|
319
336
|
except Exception as e:
|
320
337
|
print("Warning: Could not get fresh version: {}".format(e))
|
@@ -335,17 +352,40 @@ def upgrade_package(package, retries=4, delay=2, target_version=None): # Update
|
|
335
352
|
|
336
353
|
if process.returncode == 0:
|
337
354
|
print(stdout.decode().strip())
|
338
|
-
# Add delay to allow file system to settle
|
339
|
-
|
340
|
-
|
355
|
+
# Add longer delay to allow file system and package metadata to settle
|
356
|
+
print("Waiting for package metadata to update...")
|
357
|
+
time.sleep(3)
|
358
|
+
|
359
|
+
# Try multiple times to get the new version with increasing delays
|
360
|
+
new_version = None
|
361
|
+
for retry in range(3):
|
362
|
+
# Clear pkg_resources cache before each attempt
|
363
|
+
if pkg_resources:
|
364
|
+
try:
|
365
|
+
pkg_resources.working_set = pkg_resources.WorkingSet()
|
366
|
+
except Exception:
|
367
|
+
pass
|
368
|
+
|
369
|
+
new_version = get_installed_version_fresh(package)
|
370
|
+
if new_version:
|
371
|
+
print("Detected new version: {}".format(new_version))
|
372
|
+
break
|
373
|
+
print("Version detection attempt {} failed, retrying...".format(retry + 1))
|
374
|
+
time.sleep(2)
|
375
|
+
|
341
376
|
expected_version = target_version or get_latest_version(package)
|
342
377
|
|
343
378
|
if expected_version and new_version and compare_versions(new_version, expected_version) >= 0:
|
344
379
|
print_status("Attempt {}: Upgrade succeeded with {}!".format(attempt, strategy_name), "SUCCESS")
|
345
380
|
return True
|
381
|
+
elif new_version:
|
382
|
+
print_status("Upgrade may have succeeded but version mismatch. Current: {} Expected: {}".format(
|
383
|
+
new_version, expected_version), "WARNING")
|
384
|
+
# If we got a new version but it doesn't match expected, still consider it a success
|
385
|
+
# as the package was updated
|
386
|
+
return True
|
346
387
|
else:
|
347
|
-
print_status("Upgrade incomplete.
|
348
|
-
new_version or "unknown", expected_version), "WARNING")
|
388
|
+
print_status("Upgrade incomplete. Could not detect new version.", "WARNING")
|
349
389
|
return False
|
350
390
|
else:
|
351
391
|
print(stderr.decode().strip())
|
@@ -604,10 +644,19 @@ def main():
|
|
604
644
|
if upgrade_package(package, target_version=latest_version):
|
605
645
|
# STEP 8: Verify upgrade
|
606
646
|
debug_step(8, "Upgrade Verification")
|
647
|
+
|
648
|
+
# Clear cache and wait for package metadata to settle
|
649
|
+
if pkg_resources:
|
650
|
+
try:
|
651
|
+
pkg_resources.working_set = pkg_resources.WorkingSet()
|
652
|
+
except Exception:
|
653
|
+
pass
|
654
|
+
|
655
|
+
time.sleep(2)
|
607
656
|
new_version = get_installed_version(package)
|
608
657
|
print("New installed version: {}".format(new_version))
|
609
658
|
|
610
|
-
if compare_versions(new_version, latest_version) >= 0:
|
659
|
+
if new_version and compare_versions(new_version, latest_version) >= 0:
|
611
660
|
print_status("Upgrade successful. New version: {}".format(new_version), "SUCCESS")
|
612
661
|
|
613
662
|
# DEBUG STEP 9: Clear cache
|
@@ -619,8 +668,21 @@ def main():
|
|
619
668
|
print_status("Cache clearing failed, but update was successful.", "WARNING")
|
620
669
|
|
621
670
|
print_final_result(True, "Successfully upgraded to version {}".format(new_version))
|
671
|
+
elif new_version:
|
672
|
+
print_status("Upgrade completed but version verification unclear. New version: {}".format(new_version), "WARNING")
|
673
|
+
print_status("The package was updated, but version comparison failed. This may be due to caching issues.", "WARNING")
|
674
|
+
|
675
|
+
# Still clear cache and exit successfully
|
676
|
+
debug_step(9, "Cache Clearing")
|
677
|
+
print_status("Clearing Python cache to prevent import issues...", "INFO")
|
678
|
+
if clear_python_cache():
|
679
|
+
print_status("Cache cleared successfully. Update complete.", "SUCCESS")
|
680
|
+
else:
|
681
|
+
print_status("Cache clearing failed, but update was successful.", "WARNING")
|
682
|
+
|
683
|
+
print_final_result(True, "Package updated (version verification unclear)")
|
622
684
|
else:
|
623
|
-
print_status("Upgrade failed.
|
685
|
+
print_status("Upgrade verification failed. Could not detect new version.", "ERROR")
|
624
686
|
print_final_result(False, "Upgrade verification failed")
|
625
687
|
else:
|
626
688
|
print_final_result(False, "Upgrade process failed")
|
@@ -54,7 +54,7 @@ if long_description_text is None:
|
|
54
54
|
|
55
55
|
setup(
|
56
56
|
name='medicafe',
|
57
|
-
version="0.
|
57
|
+
version="0.250814.3",
|
58
58
|
description='MediCafe',
|
59
59
|
long_description=long_description_text,
|
60
60
|
long_description_content_type='text/markdown',
|
@@ -74,7 +74,7 @@ setup(
|
|
74
74
|
'Operating System :: OS Independent',
|
75
75
|
],
|
76
76
|
packages=find_packages(include=['MediCafe', 'MediCafe.*', 'MediBot', 'MediBot.*', 'MediLink', 'MediLink.*']),
|
77
|
-
include_package_data=
|
77
|
+
include_package_data=False, # Disable automatic inclusion to prevent CSV files from being included
|
78
78
|
package_data={
|
79
79
|
'MediBot': ['MediBot.bat'],
|
80
80
|
'MediLink': ['openssl.cnf', '*.html']
|
medicafe-0.250813.1/MANIFEST.in
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|