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.
Files changed (80) hide show
  1. medicafe-0.250814.3/MANIFEST.in +24 -0
  2. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot.bat +1 -1
  3. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot.py +48 -28
  4. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Crosswalk_Library.py +6 -6
  5. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Preprocessor_lib.py +90 -26
  6. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_UI.py +76 -1
  7. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_dataformat_library.py +0 -2
  8. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_smart_import.py +1 -1
  9. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/__init__.py +1 -1
  10. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/update_medicafe.py +69 -7
  11. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/__init__.py +1 -1
  12. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_core.py +0 -1
  13. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/__init__.py +1 -1
  14. {medicafe-0.250813.1/medicafe.egg-info → medicafe-0.250814.3}/PKG-INFO +1 -1
  15. {medicafe-0.250813.1 → medicafe-0.250814.3/medicafe.egg-info}/PKG-INFO +1 -1
  16. {medicafe-0.250813.1 → medicafe-0.250814.3}/setup.py +2 -2
  17. medicafe-0.250813.1/MANIFEST.in +0 -4
  18. {medicafe-0.250813.1 → medicafe-0.250814.3}/LICENSE +0 -0
  19. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Charges.py +0 -0
  20. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
  21. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Post.py +0 -0
  22. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_Preprocessor.py +0 -0
  23. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/MediBot_docx_decoder.py +0 -0
  24. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/get_medicafe_version.py +0 -0
  25. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediBot/update_json.py +0 -0
  26. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/MediLink_ConfigLoader.py +0 -0
  27. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/__main__.py +0 -0
  28. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_core_backup.py +0 -0
  29. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_factory.py +0 -0
  30. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/api_utils.py +0 -0
  31. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/core_utils.py +0 -0
  32. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/graphql_utils.py +0 -0
  33. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/logging_config.py +0 -0
  34. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/logging_demo.py +0 -0
  35. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/migration_helpers.py +0 -0
  36. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/smart_import.py +0 -0
  37. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediCafe/submission_index.py +0 -0
  38. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/InsuranceTypeService.py +0 -0
  39. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_cob_library.py +0 -0
  40. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_encoder.py +0 -0
  41. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_encoder_library.py +0 -0
  42. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_837p_utilities.py +0 -0
  43. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_API_Generator.py +0 -0
  44. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Azure.py +0 -0
  45. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_ClaimStatus.py +0 -0
  46. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_DataMgmt.py +0 -0
  47. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Decoder.py +0 -0
  48. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Deductible.py +0 -0
  49. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Deductible_Validator.py +0 -0
  50. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Display_Utils.py +0 -0
  51. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Down.py +0 -0
  52. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Gmail.py +0 -0
  53. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Mailer.py +0 -0
  54. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Parser.py +0 -0
  55. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_PatientProcessor.py +0 -0
  56. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Scan.py +0 -0
  57. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Scheduler.py +0 -0
  58. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_UI.py +0 -0
  59. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_Up.py +0 -0
  60. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_insurance_utils.py +0 -0
  61. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_main.py +0 -0
  62. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/MediLink_smart_import.py +0 -0
  63. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/Soumit_api.py +0 -0
  64. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/gmail_http_utils.py +0 -0
  65. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/gmail_oauth_utils.py +0 -0
  66. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/insurance_type_integration_test.py +0 -0
  67. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/openssl.cnf +0 -0
  68. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test.py +0 -0
  69. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_cob_library.py +0 -0
  70. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_timing.py +0 -0
  71. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/test_validation.py +0 -0
  72. {medicafe-0.250813.1 → medicafe-0.250814.3}/MediLink/webapp.html +0 -0
  73. {medicafe-0.250813.1 → medicafe-0.250814.3}/README.md +0 -0
  74. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/SOURCES.txt +0 -0
  75. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/dependency_links.txt +0 -0
  76. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/entry_points.txt +0 -0
  77. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/not-zip-safe +0 -0
  78. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/requires.txt +0 -0
  79. {medicafe-0.250813.1 → medicafe-0.250814.3}/medicafe.egg-info/top_level.txt +0 -0
  80. {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.250529.2"
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
- surgery_date = next((row.get('Surgery Date') for row in csv_data if row.get(reverse_mapping['Patient ID #2']) == patient_id), None)
618
- if surgery_date is None:
619
- raise ValueError("Surgery Date not found for patient ID: {}".format(patient_id))
620
- patient_info.append((surgery_date, patient_name, patient_id))
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 Surgery Date for patient ID '{}': {}".format(patient_id, e), level="WARNING")
623
- patient_info.append(('Unknown Date', patient_name, patient_id)) # Append with 'Unknown Date' if there's an error
624
-
625
- # Sort by surgery date first and then by patient name
626
- # NOTE: 'surgery_date' may be a string or a datetime depending on upstream data.
627
- # We sort lexicographically which is stable for uniform string inputs. If mixed types
628
- # are encountered, consider normalizing to string first or using a robust key function
629
- # that attempts datetime parsing with a string fallback.
630
- patient_info.sort(key=lambda x: (x[0], x[1]))
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
- else:
646
- print("\nSelected patient(s) are NEW patients and will be processed.")
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 the {} remaining patient(s)? (yes/no): ".format(len(patients_to_process))).lower().strip() in ['yes', 'y']
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 handled in the crosswalk.
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
- if row['Surgery Date'] < existing_row['Surgery Date']:
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/%d/%Y')
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
- # Use pre-converted surgery date string for efficient lookup
1108
- surgery_date_str = surgery_date_strings.get(patient_id, '')
1109
-
1110
- MediLink_ConfigLoader.log("Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
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
- if surgery_date_str in all_patient_data[patient_id]:
1114
- diagnosis_code, left_or_right_eye, femto_yes_or_no = all_patient_data[patient_id][surgery_date_str]
1115
- MediLink_ConfigLoader.log("Found diagnosis data for Patient ID: {}, Surgery Date: {}".format(patient_id, surgery_date_str), level="DEBUG")
1116
-
1117
- # Convert diagnosis code to Medisoft shorthand format.
1118
- medisoft_shorthand = diagnosis_to_medisoft.get(diagnosis_code, None)
1119
- if medisoft_shorthand is None and diagnosis_code:
1120
- # Use fallback logic for missing mapping
1121
- defaulted_code = diagnosis_code.lstrip('H').lstrip('T8').replace('.', '')[-5:]
1122
- medisoft_shorthand = defaulted_code
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
- row['Default Diagnosis #1'] = medisoft_shorthand
1128
- updated_count += 1
1129
- MediLink_ConfigLoader.log("Updated row number {} with new diagnosis code.".format(row_num), level="INFO")
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
- MediLink_ConfigLoader.log("No matching surgery date found for Patient ID: {} in row {}.".format(patient_id, row_num), level="INFO")
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, os, sys
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."""
@@ -6,8 +6,6 @@ Contains functions for formatting various data types and handling CSV operations
6
6
 
7
7
  import os
8
8
  import sys
9
- import csv
10
- import json
11
9
  import re
12
10
  from datetime import datetime
13
11
 
@@ -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, subprocess, tempfile, traceback, re, time, sys
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__))
@@ -19,7 +19,7 @@ Smart Import Integration:
19
19
  medibot_main = get_components('medibot_main')
20
20
  """
21
21
 
22
- __version__ = "0.250728.9"
22
+ __version__ = "0.250814.3"
23
23
  __author__ = "Daniel Vidaud"
24
24
  __email__ = "daniel@personalizedtransformation.com"
25
25
 
@@ -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
- time.sleep(1)
340
- new_version = get_installed_version_fresh(package)
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. Current version: {} Expected at least: {}".format(
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. Current version remains: {}".format(new_version), "ERROR")
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")
@@ -27,7 +27,7 @@ Smart Import System:
27
27
  api_suite = get_api_access()
28
28
  """
29
29
 
30
- __version__ = "0.250728.9"
30
+ __version__ = "0.250814.3"
31
31
  __author__ = "Daniel Vidaud"
32
32
  __email__ = "daniel@personalizedtransformation.com"
33
33
 
@@ -7,7 +7,6 @@ COMPATIBILITY: Python 3.4.4 and Windows XP compatible
7
7
  """
8
8
 
9
9
  import time, json, os, traceback, sys, requests
10
- from datetime import datetime, timedelta
11
10
 
12
11
  # Import centralized logging configuration
13
12
  try:
@@ -22,7 +22,7 @@ Smart Import Integration:
22
22
  datamgmt = get_components('medilink_datamgmt')
23
23
  """
24
24
 
25
- __version__ = "0.250728.9"
25
+ __version__ = "0.250814.3"
26
26
  __author__ = "Daniel Vidaud"
27
27
  __email__ = "daniel@personalizedtransformation.com"
28
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250813.1
3
+ Version: 0.250814.3
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2/MediCafe
6
6
  Author: Daniel Vidaud
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250813.1
3
+ Version: 0.250814.3
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2/MediCafe
6
6
  Author: Daniel Vidaud
@@ -54,7 +54,7 @@ if long_description_text is None:
54
54
 
55
55
  setup(
56
56
  name='medicafe',
57
- version="0.250813.1",
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=True,
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']
@@ -1,4 +0,0 @@
1
- include MediBot/MediBot.bat
2
- include MediLink/openssl.cnf
3
- include MediLink/*.html
4
- include MediCafe/*.py
File without changes
File without changes
File without changes