medicafe 0.250728.7__tar.gz → 0.250728.9__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.

Potentially problematic release.


This version of medicafe might be problematic. Click here for more details.

Files changed (62) hide show
  1. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink.py +92 -10
  2. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_837p_encoder_library.py +35 -5
  3. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_API_v3.py +62 -0
  4. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_DataMgmt.py +27 -9
  5. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_UI.py +20 -10
  6. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Up.py +6 -1
  7. medicafe-0.250728.9/MediLink/MediLink_api_utils.py +305 -0
  8. medicafe-0.250728.9/MediLink/MediLink_insurance_utils.py +231 -0
  9. medicafe-0.250728.9/MediLink/__init__.py +1 -0
  10. medicafe-0.250728.9/MediLink/insurance_type_integration_test.py +348 -0
  11. {medicafe-0.250728.7 → medicafe-0.250728.9}/PKG-INFO +1 -1
  12. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/PKG-INFO +1 -1
  13. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/SOURCES.txt +3 -1
  14. {medicafe-0.250728.7 → medicafe-0.250728.9}/setup.py +1 -1
  15. medicafe-0.250728.7/MediLink/__init__.py +0 -0
  16. medicafe-0.250728.7/README.md +0 -58
  17. {medicafe-0.250728.7 → medicafe-0.250728.9}/LICENSE +0 -0
  18. {medicafe-0.250728.7 → medicafe-0.250728.9}/MANIFEST.in +0 -0
  19. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot.bat +0 -0
  20. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot.py +0 -0
  21. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_Charges.py +0 -0
  22. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_Crosswalk_Library.py +0 -0
  23. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_Post.py +0 -0
  24. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_Preprocessor.py +0 -0
  25. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_Preprocessor_lib.py +0 -0
  26. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_UI.py +0 -0
  27. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_dataformat_library.py +0 -0
  28. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/MediBot_docx_decoder.py +0 -0
  29. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/PDF_to_CSV_Cleaner.py +0 -0
  30. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/__init__.py +0 -0
  31. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/update_json.py +0 -0
  32. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediBot/update_medicafe.py +0 -0
  33. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_837p_cob_library.py +0 -0
  34. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_837p_encoder.py +0 -0
  35. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_837p_utilities.py +0 -0
  36. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_API_Generator.py +0 -0
  37. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_API_v2.py +0 -0
  38. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_APIs.py +0 -0
  39. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Azure.py +0 -0
  40. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_ClaimStatus.py +0 -0
  41. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_ConfigLoader.py +0 -0
  42. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Decoder.py +0 -0
  43. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Deductible.py +0 -0
  44. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Deductible_Validator.py +0 -0
  45. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Down.py +0 -0
  46. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Gmail.py +0 -0
  47. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_GraphQL.py +0 -0
  48. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Mailer.py +0 -0
  49. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Parser.py +0 -0
  50. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Scan.py +0 -0
  51. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/MediLink_Scheduler.py +0 -0
  52. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/Soumit_api.py +0 -0
  53. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/openssl.cnf +0 -0
  54. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/test.py +0 -0
  55. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/test_cob_library.py +0 -0
  56. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/test_validation.py +0 -0
  57. {medicafe-0.250728.7 → medicafe-0.250728.9}/MediLink/webapp.html +0 -0
  58. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/dependency_links.txt +0 -0
  59. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/not-zip-safe +0 -0
  60. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/requires.txt +0 -0
  61. {medicafe-0.250728.7 → medicafe-0.250728.9}/medicafe.egg-info/top_level.txt +0 -0
  62. {medicafe-0.250728.7 → medicafe-0.250728.9}/setup.cfg +0 -0
@@ -1,5 +1,5 @@
1
1
  # MediLink.py
2
- import os, sys
2
+ import os, sys, time
3
3
  import MediLink_Down
4
4
  import MediLink_Up
5
5
  import MediLink_ConfigLoader
@@ -7,7 +7,12 @@ import MediLink_DataMgmt
7
7
 
8
8
  # For UI Functions
9
9
  import MediLink_UI # Import UI module for handling all user interfaces
10
- from tqdm import tqdm
10
+ try:
11
+ from tqdm import tqdm
12
+ except ImportError:
13
+ # Fallback for when tqdm is not available
14
+ def tqdm(iterable, **kwargs):
15
+ return iterable
11
16
 
12
17
  # Add parent directory of the project to the Python path
13
18
  project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
@@ -28,17 +33,25 @@ def collect_detailed_patient_data(selected_files, config, crosswalk):
28
33
  """
29
34
  Collects detailed patient data from the selected files.
30
35
 
36
+ DATA FLOW CLARIFICATION:
37
+ This function processes fixed-width files through extract_and_suggest_endpoint(),
38
+ which creates data structures with 'patient_id' field (sourced from 'CHART' field).
39
+ This is DIFFERENT from MediBot's parse_z_dat() flow which uses 'PATID' field.
40
+
31
41
  :param selected_files: List of selected file paths.
32
42
  :param config: Configuration settings loaded from a JSON file.
33
43
  :param crosswalk: Crosswalk data for mapping purposes.
34
- :return: A list of detailed patient data.
44
+ :return: A list of detailed patient data with 'patient_id' field populated.
35
45
  """
36
46
  detailed_patient_data = []
37
47
  for file_path in selected_files:
48
+ # IMPORTANT: extract_and_suggest_endpoint creates data with 'patient_id' field
49
+ # sourced from the 'CHART' field in fixed-width files
38
50
  detailed_data = extract_and_suggest_endpoint(file_path, config, crosswalk)
39
51
  detailed_patient_data.extend(detailed_data) # Accumulate detailed data for processing
40
52
 
41
53
  # Enrich the detailed patient data with insurance type
54
+ # NOTE: This receives data from extract_and_suggest_endpoint with 'patient_id' field
42
55
  detailed_patient_data = enrich_with_insurance_type(detailed_patient_data, insurance_options)
43
56
 
44
57
  # Display summaries and provide an option for bulk edit
@@ -49,9 +62,18 @@ def collect_detailed_patient_data(selected_files, config, crosswalk):
49
62
  def enrich_with_insurance_type(detailed_patient_data, patient_insurance_type_mapping=None):
50
63
  """
51
64
  Enriches the detailed patient data with insurance type based on patient ID.
65
+ Enhanced with optional API integration and comprehensive logging.
66
+
67
+ DATA FLOW CLARIFICATION:
68
+ This function receives data from collect_detailed_patient_data() -> extract_and_suggest_endpoint().
69
+ The patient ID field is 'patient_id' (NOT 'PATID').
70
+
71
+ IMPORTANT: Do not confuse with MediBot's parse_z_dat() flow which uses 'PATID'.
72
+ MediLink flow: fixed-width files -> extract_and_suggest_endpoint() -> 'patient_id' field (from 'CHART')
73
+ MediBot flow: Z.dat files -> parse_z_dat() -> 'PATID' field
52
74
 
53
75
  Parameters:
54
- - detailed_patient_data: List of dictionaries containing detailed patient data.
76
+ - detailed_patient_data: List of dictionaries containing detailed patient data with 'patient_id' field.
55
77
  - patient_insurance_mapping: Dictionary mapping patient IDs to their insurance types.
56
78
 
57
79
  Returns:
@@ -64,14 +86,62 @@ def enrich_with_insurance_type(detailed_patient_data, patient_insurance_type_map
64
86
  UHC and which ones are not yet supported so they know which ones they need to edit. It is possible that we may want to isolate the
65
87
  patient data that is already pulled from UHC so that the user can see which ones are already using the enriched data.
66
88
  """
89
+ # Enhanced mode check with graceful degradation
90
+ enhanced_mode = False
91
+
92
+ try:
93
+ from MediLink_insurance_utils import (
94
+ get_feature_flag,
95
+ validate_insurance_type_from_config
96
+ )
97
+ enhanced_mode = get_feature_flag('enhanced_insurance_enrichment', default=False)
98
+ MediLink_ConfigLoader.log("Insurance enhancement utilities loaded successfully", level="DEBUG")
99
+ except ImportError as e:
100
+ MediLink_ConfigLoader.log("Insurance utils not available: {}. Using legacy mode.".format(str(e)), level="INFO")
101
+ enhanced_mode = False
102
+ except Exception as e:
103
+ MediLink_ConfigLoader.log("Error initializing insurance enhancements: {}. Using legacy mode.".format(str(e)), level="ERROR")
104
+ enhanced_mode = False
105
+
67
106
  if patient_insurance_type_mapping is None:
68
- MediLink_ConfigLoader.log("No Patient:Insurance-Type mapping available.", level="WARNING")
107
+ MediLink_ConfigLoader.log("No Patient:Insurance-Type mapping available.", level="INFO")
69
108
  patient_insurance_type_mapping = {}
70
109
 
71
- for data in detailed_patient_data:
72
- patient_id = data.get('PATID') # I think this is the right name?
73
- insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO)
74
- data['insurance_type'] = insurance_type
110
+ # Enhanced mode with validation
111
+ if enhanced_mode:
112
+ MediLink_ConfigLoader.log("Using enhanced insurance type enrichment", level="INFO")
113
+
114
+ for data in detailed_patient_data:
115
+ # FIELD NAME CLARIFICATION: Use 'patient_id' field created by extract_and_suggest_endpoint()
116
+ # This field contains the value from the 'CHART' field in the original fixed-width file
117
+ patient_id = data.get('patient_id')
118
+ if patient_id:
119
+ raw_insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO/SBR09)
120
+ validated_insurance_type = validate_insurance_type_from_config(raw_insurance_type, patient_id)
121
+ data['insurance_type'] = validated_insurance_type
122
+ data['insurance_type_source'] = 'MANUAL' if patient_id in patient_insurance_type_mapping else 'DEFAULT'
123
+ else:
124
+ # Handle case where patient_id is missing or empty
125
+ MediLink_ConfigLoader.log("No patient_id found in data record", level="WARNING")
126
+ data['insurance_type'] = '12' # SBR09 default PPO
127
+ data['insurance_type_source'] = 'DEFAULT_FALLBACK'
128
+
129
+ else:
130
+ # Legacy mode (preserve existing behavior exactly)
131
+ MediLink_ConfigLoader.log("Using legacy insurance type enrichment", level="INFO")
132
+ for data in detailed_patient_data:
133
+ # FIELD NAME CLARIFICATION: Use 'patient_id' field created by extract_and_suggest_endpoint()
134
+ # This field contains the value from the 'CHART' field in the original fixed-width file
135
+ patient_id = data.get('patient_id')
136
+ if patient_id:
137
+ insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO/SBR09)
138
+ else:
139
+ # Handle case where patient_id is missing or empty
140
+ MediLink_ConfigLoader.log("No patient_id found in data record", level="WARNING")
141
+ insurance_type = '12' # Default when no patient ID available
142
+
143
+ data['insurance_type'] = insurance_type
144
+
75
145
  return detailed_patient_data
76
146
 
77
147
  def extract_and_suggest_endpoint(file_path, config, crosswalk):
@@ -81,6 +151,16 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
81
151
  an endpoint based on insurance provider information found in the crosswalk and prepares
82
152
  detailed patient data for processing.
83
153
 
154
+ DATA FLOW CLARIFICATION:
155
+ This function is the PRIMARY data source for MediLink patient processing.
156
+ It creates the 'patient_id' field by extracting the 'CHART' field from fixed-width files.
157
+
158
+ IMPORTANT: This is DIFFERENT from MediBot's parse_z_dat() which extracts 'PATID'.
159
+
160
+ Field mapping for MediLink flow:
161
+ - Fixed-width file 'CHART' field -> detailed_data['patient_id']
162
+ - This 'patient_id' is then used by enrich_with_insurance_type()
163
+
84
164
  Parameters:
85
165
  - file_path: Path to the fixed-width file.
86
166
  - crosswalk: Crosswalk dictionary loaded from a JSON file.
@@ -135,7 +215,9 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
135
215
  detailed_data = parsed_data.copy() # Copy parsed_data to avoid modifying the original dictionary
136
216
  detailed_data.update({
137
217
  'file_path': file_path,
138
- 'patient_id': parsed_data.get('CHART'),
218
+ # CRITICAL FIELD MAPPING: 'CHART' field from fixed-width file becomes 'patient_id'
219
+ # This is the field that enrich_with_insurance_type() will use
220
+ 'patient_id': parsed_data.get('CHART'), # ← This is the key field mapping for MediLink flow
139
221
  'surgery_date': parsed_data.get('DATE'),
140
222
  'patient_name': ' '.join([parsed_data.get(key, '') for key in ['FIRST', 'MIDDLE', 'LAST']]),
141
223
  'amount': parsed_data.get('AMOUNT'),
@@ -572,7 +572,8 @@ def create_sbr_segment(config, parsed_data, endpoint):
572
572
 
573
573
  def insurance_type_selection(parsed_data):
574
574
  """
575
- Quick Fix: This funtion selects the insurance type for a patient based on predefined codes.
575
+ Enhanced insurance type selection with optional API integration and safe fallbacks.
576
+ Maintains exact same signature as existing implementation.
576
577
 
577
578
  TODO (HIGH SBR09) Finish making this function.
578
579
  This should eventually integrate into a menu upstream. This menu flow probably needs to be alongside the suggested endpoint flow.
@@ -602,6 +603,22 @@ def insurance_type_selection(parsed_data):
602
603
  """
603
604
  MediLink_ConfigLoader.log("insurance_type_selection(parsed_data): {}".format(parsed_data), level="DEBUG")
604
605
 
606
+ # Try enhanced selection with safe fallback
607
+ try:
608
+ from MediLink_insurance_utils import safe_insurance_type_selection
609
+ return safe_insurance_type_selection(parsed_data, _original_insurance_type_selection_logic)
610
+ except ImportError as e:
611
+ MediLink_ConfigLoader.log("Enhanced insurance selection not available: {}. Using original logic".format(str(e)), level="INFO")
612
+ return _original_insurance_type_selection_logic(parsed_data)
613
+ except Exception as e:
614
+ MediLink_ConfigLoader.log("Error in enhanced insurance selection: {}. Using original logic".format(str(e)), level="ERROR")
615
+ return _original_insurance_type_selection_logic(parsed_data)
616
+
617
+ def _original_insurance_type_selection_logic(parsed_data):
618
+ """
619
+ Original insurance type selection logic extracted to preserve exact behavior.
620
+ This ensures backward compatibility when enhanced features are not available.
621
+ """
605
622
  # Check if insurance type is already assigned and is valid
606
623
  insurance_type_code = parsed_data.get('insurance_type')
607
624
  if insurance_type_code and len(insurance_type_code) == 2 and insurance_type_code.isalnum():
@@ -639,11 +656,24 @@ def insurance_type_selection(parsed_data):
639
656
  # User input for insurance type
640
657
  user_input = input("Enter the 2-character code for the insurance type (or press Enter to default to '12' for PPO): ").strip().upper()
641
658
 
642
- # Validate input and set the insurance type code
643
- if user_input in insurance_options:
644
- insurance_type_code = user_input
659
+ # Input validation and assignment
660
+ if user_input:
661
+ # Basic format validation
662
+ if len(user_input) > 3 or not user_input.isalnum():
663
+ print("Invalid format: Insurance codes should be 1-3 alphanumeric characters. Defaulting to PPO.")
664
+ elif user_input in insurance_options:
665
+ insurance_type_code = user_input
666
+ print("Selected: {} - {}".format(user_input, insurance_options[user_input]))
667
+ else:
668
+ # User wants to use a code not in config - confirm with them
669
+ confirm = input("Code '{}' not found in configuration. Use it anyway? (y/n): ".format(user_input)).strip().lower()
670
+ if confirm in ['y', 'yes']:
671
+ insurance_type_code = user_input
672
+ print("Using code: {}".format(user_input))
673
+ else:
674
+ print("Defaulting to PPO (Preferred Provider Organization)")
645
675
  else:
646
- print("Skipped or Input not recognized. Defaulting to Preferred Provider Organization (PPO)\n")
676
+ print("Using default: PPO (Preferred Provider Organization)")
647
677
 
648
678
  return insurance_type_code
649
679
 
@@ -112,6 +112,29 @@ class APIClient(BaseAPIClient):
112
112
  def __init__(self):
113
113
  config, _ = MediLink_ConfigLoader.load_configuration()
114
114
  super().__init__(config)
115
+
116
+ # Add enhanced features if available
117
+ try:
118
+ from MediLink_api_utils import APICircuitBreaker, APICache, APIRateLimiter
119
+ from MediLink_insurance_utils import get_feature_flag
120
+
121
+ # Initialize enhancements if enabled
122
+ enable_circuit_breaker = get_feature_flag('api_circuit_breaker', default=False)
123
+ enable_caching = get_feature_flag('api_caching', default=False)
124
+ enable_rate_limiting = get_feature_flag('api_rate_limiting', default=False)
125
+
126
+ self.circuit_breaker = APICircuitBreaker() if enable_circuit_breaker else None
127
+ self.api_cache = APICache() if enable_caching else None
128
+ self.rate_limiter = APIRateLimiter() if enable_rate_limiting else None
129
+
130
+ if any([enable_circuit_breaker, enable_caching, enable_rate_limiting]):
131
+ MediLink_ConfigLoader.log("Enhanced API client initialized with circuit_breaker={}, caching={}, rate_limiting={}".format(
132
+ enable_circuit_breaker, enable_caching, enable_rate_limiting), level="INFO")
133
+ except ImportError:
134
+ MediLink_ConfigLoader.log("API enhancements not available, using standard client", level="DEBUG")
135
+ self.circuit_breaker = None
136
+ self.api_cache = None
137
+ self.rate_limiter = None
115
138
 
116
139
  def get_access_token(self, endpoint_name):
117
140
  MediLink_ConfigLoader.log("[Get Access Token] Called for {}".format(endpoint_name), level="DEBUG")
@@ -177,6 +200,45 @@ class APIClient(BaseAPIClient):
177
200
  return None
178
201
 
179
202
  def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
203
+ # Try enhanced API call if available
204
+ if hasattr(self, 'circuit_breaker') and self.circuit_breaker:
205
+ try:
206
+ return self._make_enhanced_api_call(endpoint_name, call_type, url_extension, params, data, headers)
207
+ except Exception as e:
208
+ MediLink_ConfigLoader.log("Enhanced API call failed, falling back to standard: {}".format(str(e)), level="WARNING")
209
+
210
+ # Standard API call logic
211
+ return self._make_standard_api_call(endpoint_name, call_type, url_extension, params, data, headers)
212
+
213
+ def _make_enhanced_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
214
+ """Enhanced API call with circuit breaker, caching, and rate limiting"""
215
+ # Check cache first (for GET requests)
216
+ if self.api_cache and call_type == 'GET':
217
+ cached_result = self.api_cache.get(endpoint_name, call_type, url_extension, params)
218
+ if cached_result is not None:
219
+ MediLink_ConfigLoader.log("Cache hit for {} {} {}".format(call_type, endpoint_name, url_extension), level="DEBUG")
220
+ return cached_result
221
+
222
+ # Check rate limits
223
+ if self.rate_limiter:
224
+ self.rate_limiter.wait_if_needed()
225
+
226
+ # Make call with circuit breaker protection
227
+ result = self.circuit_breaker.call_with_breaker(
228
+ self._make_standard_api_call, endpoint_name, call_type, url_extension, params, data, headers)
229
+
230
+ # Record rate limit call
231
+ if self.rate_limiter:
232
+ self.rate_limiter.record_call()
233
+
234
+ # Cache result (for GET requests)
235
+ if self.api_cache and call_type == 'GET':
236
+ self.api_cache.set(result, endpoint_name, call_type, url_extension, params)
237
+
238
+ return result
239
+
240
+ def _make_standard_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
241
+ """Standard API call logic preserved for compatibility"""
180
242
  token = self.get_access_token(endpoint_name)
181
243
  if token:
182
244
  MediLink_ConfigLoader.log("[Make API Call] Token found for {}".format(endpoint_name), level="DEBUG")
@@ -616,25 +616,43 @@ def confirm_all_suggested_endpoints(detailed_patient_data):
616
616
  return detailed_patient_data
617
617
 
618
618
  def bulk_edit_insurance_types(detailed_patient_data, insurance_options):
619
- # Allow user to edit insurance types in a table-like format with validation
620
- print("Edit Insurance Type (Enter the 2-character code). Enter 'LIST' to display available insurance types.")
619
+ """Allow user to edit insurance types in a table-like format with validation"""
620
+ print("\nEdit Insurance Type (Enter the code). Enter 'LIST' to display available insurance types.")
621
621
 
622
622
  for data in detailed_patient_data:
623
- current_insurance_type = data['insurance_type']
623
+ patient_id = data.get('patient_id', 'Unknown')
624
+ patient_name = data.get('patient_name', 'Unknown')
625
+ current_insurance_type = data.get('insurance_type', '12')
624
626
  current_insurance_description = insurance_options.get(current_insurance_type, "Unknown")
627
+
625
628
  print("({}) {:<25} | Current Ins. Type: {} - {}".format(
626
- data['patient_id'], data['patient_name'], current_insurance_type, current_insurance_description))
629
+ patient_id, patient_name, current_insurance_type, current_insurance_description))
627
630
 
628
631
  while True:
629
- new_insurance_type = input("Enter new insurance type (or press Enter to keep current): ").upper()
632
+ new_insurance_type = input("Enter new insurance type (or press Enter to keep current): ").strip().upper()
633
+
630
634
  if new_insurance_type == 'LIST':
631
635
  MediLink_UI.display_insurance_options(insurance_options)
632
- elif not new_insurance_type or new_insurance_type in insurance_options:
633
- if new_insurance_type:
634
- data['insurance_type'] = new_insurance_type
636
+ continue
637
+
638
+ elif not new_insurance_type:
639
+ # Keep current insurance type
640
+ break
641
+
642
+ elif new_insurance_type in insurance_options:
643
+ # Valid insurance type from config
644
+ data['insurance_type'] = new_insurance_type
635
645
  break
646
+
636
647
  else:
637
- print("Invalid insurance type. Please enter a valid 2-character code or type 'LIST' to see options.")
648
+ # User wants to use a code not in config - confirm with them
649
+ confirm = input("Code '{}' not found in configuration. Use it anyway? (y/n): ".format(new_insurance_type)).strip().lower()
650
+ if confirm in ['y', 'yes']:
651
+ data['insurance_type'] = new_insurance_type
652
+ break
653
+ else:
654
+ print("Invalid insurance type. Please enter a valid code or type 'LIST' to see options.")
655
+ continue
638
656
 
639
657
  def review_and_confirm_changes(detailed_patient_data, insurance_options):
640
658
  # Review and confirm changes
@@ -102,14 +102,18 @@ def get_new_endpoint_choice():
102
102
 
103
103
  # Function to display full list of insurance options
104
104
  def display_insurance_options(insurance_options=None):
105
-
106
- # Retrieve insurance options with codes and descriptions
107
- config, _ = MediLink_ConfigLoader.load_configuration()
108
- insurance_options = config['MediLink_Config'].get('insurance_options')
105
+ """Display insurance options, loading from config if not provided"""
106
+
107
+ if insurance_options is None:
108
+ config, _ = MediLink_ConfigLoader.load_configuration()
109
+ insurance_options = config.get('MediLink_Config', {}).get('insurance_options', {})
109
110
 
110
- print("\nInsurance Type Options:")
111
+ print("\nInsurance Type Options (SBR09 Codes):")
112
+ print("-" * 50)
111
113
  for code, description in sorted(insurance_options.items()):
112
- print("{}: {}".format(code, description))
114
+ print("{:>3}: {}".format(code, description))
115
+ print("-" * 50)
116
+ print("Note: '12' (PPO) is the default if no selection is made.")
113
117
  print() # Add a blank line for better readability
114
118
 
115
119
  def display_patient_summaries(detailed_patient_data):
@@ -130,10 +134,10 @@ def display_file_summary(index, summary):
130
134
 
131
135
  # Add header row if it's the first index
132
136
  if index == 1:
133
- print("{:<3} {:5} {:<10} {:20} {:15} {:2} {:20}".format(
137
+ print("{:<3} {:5} {:<10} {:20} {:15} {:3} {:20}".format(
134
138
  "No.", "Date", "ID", "Name", "Primary Ins.", "IT", "Current Endpoint"
135
139
  ))
136
- print("-"*80)
140
+ print("-"*82)
137
141
 
138
142
  # Check if insurance_type is available; if not, set a default placeholder (this should already be '12' at this point)
139
143
  insurance_type = summary.get('insurance_type', '--')
@@ -143,14 +147,20 @@ def display_file_summary(index, summary):
143
147
  summary.get('user_preferred_endpoint') or
144
148
  summary.get('suggested_endpoint', 'AVAILITY'))
145
149
 
150
+ # Format insurance type for display - handle both 2 and 3 character codes
151
+ if insurance_type and len(insurance_type) <= 3:
152
+ insurance_display = insurance_type
153
+ else:
154
+ insurance_display = insurance_type[:3] if insurance_type else '--'
155
+
146
156
  # Displays the summary of a file.
147
- print("{:02d}. {:5} ({:<8}) {:20} {:15} {:2} {:20}".format(
157
+ print("{:02d}. {:5} ({:<8}) {:20} {:15} {:3} {:20}".format(
148
158
  index,
149
159
  surgery_date.strftime("%m-%d"),
150
160
  summary['patient_id'],
151
161
  summary['patient_name'][:20],
152
162
  summary['primary_insurance'][:15],
153
- insurance_type[:2],
163
+ insurance_display,
154
164
  effective_endpoint[:20])
155
165
  )
156
166
 
@@ -1,7 +1,12 @@
1
1
  # MediLink_Up.py
2
2
  from datetime import datetime
3
3
  import os, re, subprocess, traceback
4
- from tqdm import tqdm
4
+ try:
5
+ from tqdm import tqdm
6
+ except ImportError:
7
+ # Fallback for when tqdm is not available
8
+ def tqdm(iterable, **kwargs):
9
+ return iterable
5
10
  import MediLink_837p_encoder
6
11
  from MediLink_ConfigLoader import log, load_configuration
7
12
  from MediLink_DataMgmt import operate_winscp