medicafe 0.250813.2__tar.gz → 0.250814.4__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.4/MANIFEST.in +24 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot.bat +1 -1
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot.py +134 -4
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Preprocessor_lib.py +88 -22
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_UI.py +60 -25
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/__init__.py +1 -1
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/update_medicafe.py +147 -24
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/__init__.py +1 -1
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_837p_encoder_library.py +72 -65
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_DataMgmt.py +11 -9
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/__init__.py +10 -2
- {medicafe-0.250813.2/medicafe.egg-info → medicafe-0.250814.4}/PKG-INFO +1 -1
- {medicafe-0.250813.2 → medicafe-0.250814.4/medicafe.egg-info}/PKG-INFO +1 -1
- {medicafe-0.250813.2 → medicafe-0.250814.4}/setup.py +2 -2
- medicafe-0.250813.2/MANIFEST.in +0 -4
- {medicafe-0.250813.2 → medicafe-0.250814.4}/LICENSE +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Crosswalk_Library.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_dataformat_library.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/MediBot_smart_import.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/get_medicafe_version.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediBot/update_json.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/MediLink_ConfigLoader.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/__main__.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/api_core.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/api_core_backup.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/api_factory.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/api_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/core_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/graphql_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/logging_config.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/logging_demo.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/migration_helpers.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/smart_import.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediCafe/submission_index.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/InsuranceTypeService.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_837p_encoder.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_ClaimStatus.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Deductible.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Display_Utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_PatientProcessor.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_Up.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_insurance_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_main.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/MediLink_smart_import.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/gmail_http_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/gmail_oauth_utils.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/insurance_type_integration_test.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/openssl.cnf +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/test.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/test_cob_library.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/test_timing.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/test_validation.py +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/MediLink/webapp.html +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/README.md +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/entry_points.txt +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.250813.2 → medicafe-0.250814.4}/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 (
|
@@ -12,6 +12,55 @@ except ImportError:
|
|
12
12
|
msvcrt = None # Not available on non-Windows systems
|
13
13
|
from collections import OrderedDict
|
14
14
|
|
15
|
+
# ============================================================================
|
16
|
+
# MINIMAL PROTECTION: Import State Validation
|
17
|
+
# ============================================================================
|
18
|
+
|
19
|
+
def validate_critical_imports():
|
20
|
+
"""Validate that critical imports are in expected state before proceeding"""
|
21
|
+
critical_modules = {
|
22
|
+
'MediBot_Preprocessor': None,
|
23
|
+
'MediBot_Preprocessor_lib': None,
|
24
|
+
'MediBot_UI': None,
|
25
|
+
'MediBot_Crosswalk_Library': None
|
26
|
+
}
|
27
|
+
|
28
|
+
# Test imports and capture state
|
29
|
+
try:
|
30
|
+
print("Testing MediCafe.core_utils import...")
|
31
|
+
from MediCafe.core_utils import import_medibot_module_with_debug
|
32
|
+
print("MediCafe.core_utils import successful")
|
33
|
+
|
34
|
+
for module_name in critical_modules.keys():
|
35
|
+
print("Testing {} import...".format(module_name))
|
36
|
+
try:
|
37
|
+
module = import_medibot_module_with_debug(module_name)
|
38
|
+
critical_modules[module_name] = module
|
39
|
+
if module is None:
|
40
|
+
print(" WARNING: {} import returned None".format(module_name))
|
41
|
+
else:
|
42
|
+
print(" SUCCESS: {} import successful".format(module_name))
|
43
|
+
except Exception as e:
|
44
|
+
print(" ERROR: {} import failed with exception: {}".format(module_name, e))
|
45
|
+
critical_modules[module_name] = None
|
46
|
+
except Exception as e:
|
47
|
+
print("CRITICAL: Failed to import core utilities: {}".format(e))
|
48
|
+
return False, critical_modules
|
49
|
+
|
50
|
+
# Check for None imports (the specific failure pattern)
|
51
|
+
failed_imports = []
|
52
|
+
for module_name, module in critical_modules.items():
|
53
|
+
if module is None:
|
54
|
+
failed_imports.append(module_name)
|
55
|
+
|
56
|
+
if failed_imports:
|
57
|
+
print("CRITICAL: Import failures detected:")
|
58
|
+
for failed in failed_imports:
|
59
|
+
print(" - {}: Import returned None".format(failed))
|
60
|
+
return False, critical_modules
|
61
|
+
|
62
|
+
return True, critical_modules
|
63
|
+
|
15
64
|
# Use core utilities for standardized imports
|
16
65
|
from MediCafe.core_utils import (
|
17
66
|
import_medibot_module_with_debug,
|
@@ -512,6 +561,19 @@ if __name__ == "__main__":
|
|
512
561
|
try:
|
513
562
|
if PERFORMANCE_LOGGING:
|
514
563
|
print("Initializing. Loading configuration and preparing environment...")
|
564
|
+
|
565
|
+
# PROTECTION: Validate critical imports before proceeding
|
566
|
+
print("Validating critical imports...")
|
567
|
+
import_valid, import_state = validate_critical_imports()
|
568
|
+
if not import_valid:
|
569
|
+
print("CRITICAL: Import validation failed. Cannot continue.")
|
570
|
+
print("This indicates a fundamental system configuration issue.")
|
571
|
+
print("Please check:")
|
572
|
+
print(" 1. All MediBot modules exist in the MediBot directory")
|
573
|
+
print(" 2. Python path is correctly configured")
|
574
|
+
print(" 3. No syntax errors in MediBot modules")
|
575
|
+
sys.exit(1)
|
576
|
+
|
515
577
|
# Use MediCafe configuration system
|
516
578
|
try:
|
517
579
|
config_loader = get_config_loader_with_fallback()
|
@@ -553,6 +615,22 @@ if __name__ == "__main__":
|
|
553
615
|
print("Starting CSV preprocessing at: {}".format(time.strftime("%H:%M:%S")))
|
554
616
|
MediLink_ConfigLoader.log("Starting CSV preprocessing at: {}".format(time.strftime("%H:%M:%S")), level="INFO")
|
555
617
|
|
618
|
+
# PROTECTION: Validate MediBot_Preprocessor before calling preprocess_csv_data
|
619
|
+
if MediBot_Preprocessor is None:
|
620
|
+
print("CRITICAL: MediBot_Preprocessor is None when trying to call preprocess_csv_data")
|
621
|
+
print("This indicates the import failed silently during execution.")
|
622
|
+
print("Import state at failure:")
|
623
|
+
for module_name, module in import_state.items():
|
624
|
+
status = "None" if module is None else "OK"
|
625
|
+
print(" - {}: {}".format(module_name, status))
|
626
|
+
print("Please check for syntax errors or missing dependencies in MediBot modules.")
|
627
|
+
sys.exit(1)
|
628
|
+
|
629
|
+
if not hasattr(MediBot_Preprocessor, 'preprocess_csv_data'):
|
630
|
+
print("CRITICAL: MediBot_Preprocessor missing preprocess_csv_data function")
|
631
|
+
print("Available functions: {}".format([attr for attr in dir(MediBot_Preprocessor) if not attr.startswith('_')]))
|
632
|
+
sys.exit(1)
|
633
|
+
|
556
634
|
MediBot_Preprocessor.preprocess_csv_data(csv_data, e_state.crosswalk)
|
557
635
|
|
558
636
|
# TIMING: End CSV preprocessing timing
|
@@ -623,7 +701,16 @@ if __name__ == "__main__":
|
|
623
701
|
|
624
702
|
# Create entries for each surgery date with its specific diagnosis code
|
625
703
|
for surgery_date in all_surgery_dates:
|
626
|
-
|
704
|
+
# Convert surgery_date to string format for consistent lookup (XP SP3 + Py3.4.4 compatible)
|
705
|
+
try:
|
706
|
+
if hasattr(surgery_date, 'strftime'):
|
707
|
+
surgery_date_str = surgery_date.strftime('%m-%d-%Y')
|
708
|
+
else:
|
709
|
+
surgery_date_str = str(surgery_date)
|
710
|
+
except Exception:
|
711
|
+
surgery_date_str = str(surgery_date)
|
712
|
+
|
713
|
+
diagnosis_code = surgery_date_to_diagnosis.get(surgery_date_str, 'N/A')
|
627
714
|
patient_info.append((surgery_date, patient_name, patient_id, diagnosis_code, patient_row))
|
628
715
|
|
629
716
|
except Exception as e:
|
@@ -633,7 +720,7 @@ if __name__ == "__main__":
|
|
633
720
|
# Display existing patients table using the enhanced display function
|
634
721
|
MediBot_UI.display_enhanced_patient_table(
|
635
722
|
patient_info,
|
636
|
-
"NOTE: The following patient(s) already EXIST in the system but have new dates of service.\n Their diagnosis codes
|
723
|
+
"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
724
|
show_line_numbers=False
|
638
725
|
)
|
639
726
|
|
@@ -655,7 +742,16 @@ if __name__ == "__main__":
|
|
655
742
|
|
656
743
|
# Create entries for each surgery date with its specific diagnosis code
|
657
744
|
for surgery_date in all_surgery_dates:
|
658
|
-
|
745
|
+
# Convert surgery_date to string format for consistent lookup (XP SP3 + Py3.4.4 compatible)
|
746
|
+
try:
|
747
|
+
if hasattr(surgery_date, 'strftime'):
|
748
|
+
surgery_date_str = surgery_date.strftime('%m-%d-%Y')
|
749
|
+
else:
|
750
|
+
surgery_date_str = str(surgery_date)
|
751
|
+
except Exception:
|
752
|
+
surgery_date_str = str(surgery_date)
|
753
|
+
|
754
|
+
diagnosis_code = surgery_date_to_diagnosis.get(surgery_date_str, 'N/A')
|
659
755
|
new_patient_info.append((surgery_date, patient_name, patient_id, diagnosis_code, row))
|
660
756
|
|
661
757
|
# Display new patients table using the enhanced display function
|
@@ -695,7 +791,41 @@ if __name__ == "__main__":
|
|
695
791
|
if e_state:
|
696
792
|
interaction_mode = 'error' # Switch to error mode
|
697
793
|
error_message = str(e) # Capture the error message
|
698
|
-
|
794
|
+
|
795
|
+
# ENHANCED ERROR DIAGNOSTICS
|
796
|
+
print("=" * 60)
|
797
|
+
print("MEDIBOT EXECUTION FAILURE")
|
798
|
+
print("=" * 60)
|
799
|
+
print("Error: {}".format(e))
|
800
|
+
print("Error type: {}".format(type(e).__name__))
|
801
|
+
|
802
|
+
# Check for the specific failure pattern
|
803
|
+
if "'NoneType' object has no attribute" in str(e):
|
804
|
+
print("DIAGNOSIS: This is the import failure pattern.")
|
805
|
+
print("A module import returned None, causing a method call to fail.")
|
806
|
+
print("This typically indicates:")
|
807
|
+
print(" 1. Syntax error in a MediBot module")
|
808
|
+
print(" 2. Missing dependency")
|
809
|
+
print(" 3. Import path issue")
|
810
|
+
print(" 4. Circular import problem")
|
811
|
+
|
812
|
+
# Show current import state
|
813
|
+
print("Current import state:")
|
814
|
+
try:
|
815
|
+
import_state = {
|
816
|
+
'MediBot_Preprocessor': MediBot_Preprocessor,
|
817
|
+
'MediBot_Preprocessor_lib': MediBot_Preprocessor_lib,
|
818
|
+
'MediBot_UI': MediBot_UI,
|
819
|
+
'MediBot_Crosswalk_Library': MediBot_Crosswalk_Library
|
820
|
+
}
|
821
|
+
for module_name, module in import_state.items():
|
822
|
+
status = "None" if module is None else "OK"
|
823
|
+
print(" - {}: {}".format(module_name, status))
|
824
|
+
except Exception as diag_e:
|
825
|
+
print(" - Unable to diagnose import state: {}".format(diag_e))
|
826
|
+
|
827
|
+
print("=" * 60)
|
828
|
+
|
699
829
|
# Handle the error by calling user interaction with the error information
|
700
830
|
if 'identified_fields' in locals():
|
701
831
|
_ = user_interaction(csv_data, interaction_mode, error_message, reverse_mapping)
|
@@ -547,7 +547,18 @@ def sort_and_deduplicate(csv_data):
|
|
547
547
|
|
548
548
|
# Store the surgery dates information in the first row of each patient for later access
|
549
549
|
for patient_id, row in unique_patients.items():
|
550
|
-
|
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)
|
551
562
|
row['_primary_surgery_date'] = row['Surgery Date'] # Keep track of which date has the demographics
|
552
563
|
|
553
564
|
# Convert the unique_patients dictionary back to a list and sort it
|
@@ -565,7 +576,7 @@ def combine_fields(csv_data):
|
|
565
576
|
if surgery_date == datetime.min:
|
566
577
|
row['Surgery Date'] = 'MISSING'
|
567
578
|
else:
|
568
|
-
row['Surgery Date'] = surgery_date.strftime('%m
|
579
|
+
row['Surgery Date'] = surgery_date.strftime('%m-%d-%Y')
|
569
580
|
elif surgery_date:
|
570
581
|
# Already a non-empty string
|
571
582
|
row['Surgery Date'] = str(surgery_date)
|
@@ -1149,30 +1160,69 @@ def update_diagnosis_codes(csv_data):
|
|
1149
1160
|
MediLink_ConfigLoader.log("Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
|
1150
1161
|
|
1151
1162
|
if surgery_date_str in all_patient_data[patient_id]:
|
1152
|
-
|
1163
|
+
diagnosis_data = all_patient_data[patient_id][surgery_date_str]
|
1164
|
+
# XP SP3 + Py3.4.4 compatible tuple unpacking with safety check
|
1165
|
+
try:
|
1166
|
+
if isinstance(diagnosis_data, (list, tuple)) and len(diagnosis_data) >= 3:
|
1167
|
+
diagnosis_code, left_or_right_eye, femto_yes_or_no = diagnosis_data
|
1168
|
+
else:
|
1169
|
+
# Handle case where diagnosis_data is not a proper tuple
|
1170
|
+
diagnosis_code = diagnosis_data if diagnosis_data else None
|
1171
|
+
left_or_right_eye = None
|
1172
|
+
femto_yes_or_no = None
|
1173
|
+
except Exception as e:
|
1174
|
+
MediLink_ConfigLoader.log("Error unpacking diagnosis data for Patient ID: {}, Surgery Date: {}: {}".format(
|
1175
|
+
patient_id, surgery_date_str, str(e)), level="WARNING")
|
1176
|
+
diagnosis_code = None
|
1177
|
+
left_or_right_eye = None
|
1178
|
+
femto_yes_or_no = None
|
1179
|
+
|
1153
1180
|
MediLink_ConfigLoader.log("Found diagnosis data for Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
|
1154
1181
|
|
1155
|
-
|
1156
|
-
|
1157
|
-
if
|
1158
|
-
|
1159
|
-
|
1160
|
-
|
1161
|
-
|
1162
|
-
|
1182
|
+
# Convert diagnosis code to Medisoft shorthand format.
|
1183
|
+
# XP SP3 + Py3.4.4 compatible null check
|
1184
|
+
if diagnosis_code is None:
|
1185
|
+
medisoft_shorthand = 'N/A'
|
1186
|
+
MediLink_ConfigLoader.log("Diagnosis code is None for Patient ID: {}, Surgery Date: {}".format(
|
1187
|
+
patient_id, surgery_date_str), level="WARNING")
|
1188
|
+
else:
|
1189
|
+
medisoft_shorthand = diagnosis_to_medisoft.get(diagnosis_code, None)
|
1190
|
+
if medisoft_shorthand is None and diagnosis_code:
|
1191
|
+
# Use fallback logic for missing mapping (XP SP3 + Py3.4.4 compatible)
|
1192
|
+
try:
|
1193
|
+
defaulted_code = diagnosis_code.lstrip('H').lstrip('T8').replace('.', '')[-5:]
|
1194
|
+
# Basic validation: ensure code is not empty and has reasonable length
|
1195
|
+
if defaulted_code and len(defaulted_code) >= 3:
|
1196
|
+
medisoft_shorthand = defaulted_code
|
1197
|
+
MediLink_ConfigLoader.log("Missing diagnosis mapping for '{}', using fallback code '{}'".format(
|
1198
|
+
diagnosis_code, medisoft_shorthand), level="WARNING")
|
1199
|
+
else:
|
1200
|
+
medisoft_shorthand = 'N/A'
|
1201
|
+
MediLink_ConfigLoader.log("Fallback diagnosis code validation failed for '{}', using 'N/A'".format(
|
1202
|
+
diagnosis_code), level="WARNING")
|
1203
|
+
except Exception as e:
|
1204
|
+
medisoft_shorthand = 'N/A'
|
1205
|
+
MediLink_ConfigLoader.log("Error in fallback diagnosis code generation for '{}': {}".format(
|
1206
|
+
diagnosis_code, str(e)), level="WARNING")
|
1207
|
+
|
1163
1208
|
MediLink_ConfigLoader.log("Converted diagnosis code to Medisoft shorthand: {}".format(medisoft_shorthand), level="DEBUG")
|
1164
1209
|
|
1165
|
-
surgery_date_to_diagnosis[
|
1210
|
+
surgery_date_to_diagnosis[surgery_date_str] = medisoft_shorthand
|
1166
1211
|
else:
|
1167
1212
|
MediLink_ConfigLoader.log("No matching surgery date found for Patient ID: {} on date {}.".format(patient_id, surgery_date_str), level="INFO")
|
1168
|
-
surgery_date_to_diagnosis[
|
1213
|
+
surgery_date_to_diagnosis[surgery_date_str] = 'N/A'
|
1169
1214
|
|
1170
1215
|
# Store the diagnosis mapping for all surgery dates
|
1171
1216
|
row['_surgery_date_to_diagnosis'] = surgery_date_to_diagnosis
|
1172
1217
|
|
1173
1218
|
# Set the primary diagnosis code (for the main surgery date)
|
1174
1219
|
primary_surgery_date = row.get('Surgery Date')
|
1175
|
-
|
1220
|
+
# Convert primary surgery date to string for lookup
|
1221
|
+
if isinstance(primary_surgery_date, datetime):
|
1222
|
+
primary_surgery_date_str = primary_surgery_date.strftime('%m-%d-%Y')
|
1223
|
+
else:
|
1224
|
+
primary_surgery_date_str = str(primary_surgery_date)
|
1225
|
+
primary_diagnosis = surgery_date_to_diagnosis.get(primary_surgery_date_str, 'N/A')
|
1176
1226
|
row['Default Diagnosis #1'] = primary_diagnosis
|
1177
1227
|
|
1178
1228
|
updated_count += 1
|
@@ -1472,12 +1522,28 @@ def capitalize_all_fields(csv_data):
|
|
1472
1522
|
Returns:
|
1473
1523
|
None: The function modifies the csv_data in place.
|
1474
1524
|
"""
|
1475
|
-
# PERFORMANCE FIX: Optimize uppercase conversion
|
1525
|
+
# PERFORMANCE FIX: Optimize uppercase conversion while preserving complex types
|
1476
1526
|
for row in csv_data:
|
1477
|
-
|
1478
|
-
row.
|
1479
|
-
|
1480
|
-
|
1481
|
-
|
1482
|
-
|
1483
|
-
|
1527
|
+
updated_row = {}
|
1528
|
+
for key, value in row.items():
|
1529
|
+
# Preserve internal/derived fields intact (e.g., `_all_surgery_dates`, `_surgery_date_to_diagnosis`)
|
1530
|
+
if isinstance(key, str) and key.startswith('_'):
|
1531
|
+
updated_row[key] = value
|
1532
|
+
continue
|
1533
|
+
# Uppercase plain strings
|
1534
|
+
if isinstance(value, str):
|
1535
|
+
updated_row[key] = value.upper()
|
1536
|
+
continue
|
1537
|
+
# Preserve complex containers; optionally uppercase their string contents
|
1538
|
+
if isinstance(value, list):
|
1539
|
+
updated_row[key] = [elem.upper() if isinstance(elem, str) else elem for elem in value]
|
1540
|
+
continue
|
1541
|
+
if isinstance(value, dict):
|
1542
|
+
updated_row[key] = {k: (v.upper() if isinstance(v, str) else v) for k, v in value.items()}
|
1543
|
+
continue
|
1544
|
+
# Leave datetimes as-is; coerce simple scalars to string upper for consistency
|
1545
|
+
if isinstance(value, datetime):
|
1546
|
+
updated_row[key] = value
|
1547
|
+
else:
|
1548
|
+
updated_row[key] = str(value).upper() if value is not None else value
|
1549
|
+
row.update(updated_row)
|
@@ -2,6 +2,7 @@
|
|
2
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
|
|
@@ -42,53 +43,87 @@ def display_enhanced_patient_table(patient_info, title, show_line_numbers=True):
|
|
42
43
|
print(title)
|
43
44
|
print()
|
44
45
|
|
45
|
-
#
|
46
|
-
|
46
|
+
# Normalize data to avoid None and unexpected container types in sort key
|
47
|
+
normalized_info = []
|
48
|
+
for surgery_date, patient_name, patient_id, diagnosis_code, patient_row in patient_info:
|
49
|
+
# Normalize date into comparable key and display string
|
50
|
+
display_date = None
|
51
|
+
sort_key = None
|
52
|
+
try:
|
53
|
+
if hasattr(surgery_date, 'strftime'):
|
54
|
+
display_date = surgery_date.strftime('%m-%d')
|
55
|
+
sort_key = surgery_date
|
56
|
+
elif isinstance(surgery_date, str):
|
57
|
+
# Date strings may be MM-DD-YYYY or already MM-DD
|
58
|
+
parts = surgery_date.split('-') if surgery_date else []
|
59
|
+
if len(parts) == 3 and all(parts):
|
60
|
+
display_date = "{}-{}".format(parts[0], parts[1])
|
61
|
+
else:
|
62
|
+
display_date = surgery_date or 'Unknown Date'
|
63
|
+
# Use the raw string as sort key fallback
|
64
|
+
sort_key = surgery_date or ''
|
65
|
+
else:
|
66
|
+
display_date = str(surgery_date) if surgery_date is not None else 'Unknown Date'
|
67
|
+
sort_key = display_date
|
68
|
+
except Exception:
|
69
|
+
display_date = str(surgery_date) if surgery_date is not None else 'Unknown Date'
|
70
|
+
sort_key = display_date
|
71
|
+
|
72
|
+
# Normalize diagnosis display: only show "-Not Found-" when explicitly flagged as N/A
|
73
|
+
# XP SP3 + Py3.4.4 compatible error handling
|
74
|
+
display_diagnosis = diagnosis_code
|
75
|
+
try:
|
76
|
+
if diagnosis_code == "N/A":
|
77
|
+
display_diagnosis = "-Not Found-"
|
78
|
+
elif diagnosis_code is None:
|
79
|
+
display_diagnosis = "-Not Found-"
|
80
|
+
elif isinstance(diagnosis_code, str) and diagnosis_code.strip() == "":
|
81
|
+
display_diagnosis = "-Not Found-"
|
82
|
+
else:
|
83
|
+
display_diagnosis = str(diagnosis_code)
|
84
|
+
except (TypeError, ValueError) as e:
|
85
|
+
# Log the specific error for debugging (ASCII-only compatible)
|
86
|
+
try:
|
87
|
+
error_msg = "Error converting diagnosis code to string: {}".format(str(e))
|
88
|
+
MediLink_ConfigLoader.log(error_msg, level="WARNING")
|
89
|
+
except Exception:
|
90
|
+
# Fallback logging if string formatting fails
|
91
|
+
MediLink_ConfigLoader.log("Error converting diagnosis code to string", level="WARNING")
|
92
|
+
display_diagnosis = "-Not Found-"
|
93
|
+
|
94
|
+
normalized_info.append((sort_key, display_date, str(patient_name or ''), str(patient_id or ''), display_diagnosis))
|
95
|
+
|
96
|
+
# Sort by normalized sort key then patient name
|
97
|
+
normalized_info.sort(key=lambda x: (x[0], x[2]))
|
47
98
|
|
48
99
|
# Calculate column widths for proper alignment
|
49
|
-
max_patient_id_len = max(len(
|
50
|
-
max_patient_name_len = max(len(pname) for _,
|
51
|
-
max_diagnosis_len = max(len(dcode) for _, _, _,
|
100
|
+
max_patient_id_len = max(len(pid) for _, _, _, pid, _ in normalized_info)
|
101
|
+
max_patient_name_len = max(len(pname) for _, _, pname, _, _ in normalized_info)
|
102
|
+
max_diagnosis_len = max(len(dcode) for _, _, _, _, dcode in normalized_info)
|
52
103
|
|
53
104
|
# Ensure minimum widths for readability
|
54
105
|
max_patient_id_len = max(max_patient_id_len, 5) # 5-digit ID max
|
55
106
|
max_patient_name_len = max(max_patient_name_len, 12) # "Patient Name" header
|
56
107
|
max_diagnosis_len = max(max_diagnosis_len, 10) # "Diagnosis" header
|
57
108
|
|
58
|
-
# Print the sorted patient info with multiple surgery dates
|
59
109
|
current_patient = None
|
60
110
|
line_number = 1
|
61
111
|
|
62
|
-
for
|
63
|
-
# Format surgery_date safely whether it's a datetime/date or a string
|
64
|
-
try:
|
65
|
-
formatted_date = surgery_date.strftime('%m-%d')
|
66
|
-
except Exception:
|
67
|
-
formatted_date = str(surgery_date)
|
68
|
-
|
69
|
-
# Transform diagnosis code display: show "-Not Found-" instead of "N/A"
|
70
|
-
display_diagnosis = "-Not Found-" if diagnosis_code == "N/A" else diagnosis_code
|
71
|
-
|
72
|
-
# Check if this is the same patient as the previous line
|
112
|
+
for sort_key, formatted_date, patient_name, patient_id, display_diagnosis in normalized_info:
|
73
113
|
if current_patient == patient_id:
|
74
|
-
|
75
|
-
# Use exact character count matching for dashes
|
76
|
-
patient_id_dashes = '-' * len(str(patient_id))
|
114
|
+
patient_id_dashes = '-' * len(patient_id)
|
77
115
|
patient_name_dashes = '-' * len(patient_name)
|
78
|
-
|
79
116
|
secondary_format = " {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
80
117
|
print(secondary_format.format(formatted_date, patient_id_dashes, patient_name_dashes, display_diagnosis))
|
81
118
|
else:
|
82
|
-
# New patient - show full patient info
|
83
119
|
current_patient = patient_id
|
84
120
|
if show_line_numbers:
|
85
121
|
primary_format = "{:03d}: {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
86
|
-
print(primary_format.format(line_number, formatted_date,
|
122
|
+
print(primary_format.format(line_number, formatted_date, patient_id, patient_name, display_diagnosis))
|
87
123
|
line_number += 1
|
88
124
|
else:
|
89
|
-
# For existing patients, don't show line numbers
|
90
125
|
primary_format = " {:<6} | {:<" + str(max_patient_id_len) + "} | {:<" + str(max_patient_name_len) + "} | {:<" + str(max_diagnosis_len) + "}"
|
91
|
-
print(primary_format.format(formatted_date,
|
126
|
+
print(primary_format.format(formatted_date, patient_id, patient_name, display_diagnosis))
|
92
127
|
|
93
128
|
# Function to check if a specific key is pressed
|
94
129
|
def _get_vk_codes():
|