medicafe 0.250728.8__py3-none-any.whl → 0.250805.0__py3-none-any.whl

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 (58) hide show
  1. MediBot/MediBot.bat +233 -19
  2. MediBot/MediBot.py +138 -46
  3. MediBot/MediBot_Crosswalk_Library.py +127 -623
  4. MediBot/MediBot_Crosswalk_Utils.py +618 -0
  5. MediBot/MediBot_Preprocessor.py +72 -17
  6. MediBot/MediBot_Preprocessor_lib.py +470 -76
  7. MediBot/MediBot_UI.py +32 -17
  8. MediBot/MediBot_dataformat_library.py +68 -20
  9. MediBot/MediBot_docx_decoder.py +120 -19
  10. MediBot/MediBot_smart_import.py +180 -0
  11. MediBot/__init__.py +89 -0
  12. MediBot/get_medicafe_version.py +25 -0
  13. MediBot/update_json.py +35 -6
  14. MediBot/update_medicafe.py +19 -1
  15. MediCafe/MediLink_ConfigLoader.py +160 -0
  16. MediCafe/__init__.py +171 -0
  17. MediCafe/__main__.py +222 -0
  18. MediCafe/api_core.py +1098 -0
  19. MediCafe/api_core_backup.py +427 -0
  20. MediCafe/api_factory.py +306 -0
  21. MediCafe/api_utils.py +356 -0
  22. MediCafe/core_utils.py +450 -0
  23. MediCafe/graphql_utils.py +445 -0
  24. MediCafe/logging_config.py +123 -0
  25. MediCafe/logging_demo.py +61 -0
  26. MediCafe/migration_helpers.py +463 -0
  27. MediCafe/smart_import.py +436 -0
  28. MediLink/MediLink.py +66 -26
  29. MediLink/MediLink_837p_cob_library.py +28 -28
  30. MediLink/MediLink_837p_encoder.py +33 -34
  31. MediLink/MediLink_837p_encoder_library.py +243 -151
  32. MediLink/MediLink_837p_utilities.py +129 -5
  33. MediLink/MediLink_API_Generator.py +83 -60
  34. MediLink/MediLink_API_v3.py +1 -1
  35. MediLink/MediLink_ClaimStatus.py +177 -31
  36. MediLink/MediLink_DataMgmt.py +405 -72
  37. MediLink/MediLink_Decoder.py +20 -1
  38. MediLink/MediLink_Deductible.py +155 -28
  39. MediLink/MediLink_Display_Utils.py +72 -0
  40. MediLink/MediLink_Down.py +127 -5
  41. MediLink/MediLink_Gmail.py +712 -653
  42. MediLink/MediLink_PatientProcessor.py +257 -0
  43. MediLink/MediLink_UI.py +85 -61
  44. MediLink/MediLink_Up.py +28 -4
  45. MediLink/MediLink_insurance_utils.py +227 -264
  46. MediLink/MediLink_main.py +248 -0
  47. MediLink/MediLink_smart_import.py +264 -0
  48. MediLink/__init__.py +93 -0
  49. MediLink/insurance_type_integration_test.py +66 -76
  50. MediLink/test.py +1 -1
  51. MediLink/test_timing.py +59 -0
  52. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/METADATA +1 -1
  53. medicafe-0.250805.0.dist-info/RECORD +81 -0
  54. medicafe-0.250805.0.dist-info/entry_points.txt +2 -0
  55. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/top_level.txt +1 -0
  56. medicafe-0.250728.8.dist-info/RECORD +0 -59
  57. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/LICENSE +0 -0
  58. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/WHEEL +0 -0
@@ -1,78 +1,104 @@
1
- import json, sys, os, threading
2
-
3
- # Set the project directory to the parent directory of the current file
4
- project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
5
- if project_dir not in sys.path:
6
- sys.path.append(project_dir)
7
-
8
- # Attempt to import the MediLink_ConfigLoader module, falling back to an alternative import if necessary
9
- try:
10
- import MediLink_ConfigLoader
11
- except ImportError:
12
- from MediLink import MediLink_ConfigLoader
13
-
14
- # Attempt to import the fetch_payer_name_from_api function from MediLink_API_v3, with a fallback
15
- try:
16
- from MediLink_API_v3 import fetch_payer_name_from_api
17
- except ImportError:
18
- from MediLink import MediLink_API_v3
19
- fetch_payer_name_from_api = MediLink_API_v3.fetch_payer_name_from_api
20
-
21
- # Attempt to import the MediBot_Preprocessor_lib module, with a fallback
22
- try:
23
- from MediBot import MediBot_Preprocessor_lib
24
- except ImportError:
25
- import MediBot_Preprocessor_lib
26
-
1
+ # MediBot_Crosswalk_Library.py
2
+ """
3
+ Core crosswalk library for MediBot
4
+ Handles crosswalk operations and API interactions.
27
5
  """
28
- # TODO This has a bunch of issues that need to be fixed. Notice, the log has repetitive calls to the API that are redundant.
29
-
30
- PS G:\My Drive\Codes\MediCafe> & C:/Python34/python.exe "g:/My Drive/Codes/MediCafe/MediBot/MediBot_Preprocessor.py" --update-crosswalk
31
- Updating the crosswalk...
32
- The 'payer_id' list is empty or missing. Would you like to initialize the crosswalk? (yes/no): yes
33
- No payer found at AVAILITY for ID 60054M. Response: {'limit': 50, 'offset': 0, 'totalCount': 0, 'payers': [], 'count': 0}
34
- All endpoints exhausted for Payer ID 60054M.
35
- WARNING: Invalid Payer ID 60054M (Unknown).
36
- Enter the correct Payer ID for replacement or type 'FORCE' to continue with the unresolved Payer ID: 60054
37
- csv_replacements updated: '60054M' -> '60054'.
38
- No payer found at AVAILITY for ID MCRFL. Response: {'limit': 50, 'offset': 0, 'totalCount': 0, 'payers': [], 'count': 0}
39
- All endpoints exhausted for Payer ID MCRFL.
40
- No payer found at AVAILITY for ID BCSFL. Response: {'limit': 50, 'offset': 0, 'totalCount': 0, 'payers': [], 'count': 0}
41
- All endpoints exhausted for Payer ID BCSFL.
42
- Payer ID '60054M' has been successfully replaced with '60054'.
43
- No payer found at AVAILITY for ID MCRFL. Response: {'limit': 50, 'offset': 0, 'totalCount': 0, 'payers': [], 'count': 0}
44
- All endpoints exhausted for Payer ID MCRFL.
45
- WARNING: Invalid Payer ID MCRFL (Unknown).
46
- Enter the correct Payer ID for replacement or type 'FORCE' to continue with the unresolved Payer ID: force
47
- Payer ID 'MCRFL' has been marked as 'Unknown'.
48
- No payer found at AVAILITY for ID BCSFL. Response: {'limit': 50, 'offset': 0, 'totalCount': 0, 'payers': [], 'count': 0}
49
- All endpoints exhausted for Payer ID BCSFL.
50
- WARNING: Invalid Payer ID BCSFL (Unknown).
51
- Enter the correct Payer ID for replacement or type 'FORCE' to continue with the unresolved Payer ID: 00590
52
- csv_replacements updated: 'BCSFL' -> '00590'.
53
- Payer ID 'BCSFL' has been successfully replaced with '00590'.
54
- Crosswalk initialized with mappings for 5 payers.
6
+
7
+ import os
8
+ import sys
9
+ import csv
10
+ import json
11
+ import re
12
+ from datetime import datetime
13
+
14
+ # Use core utilities for standardized imports
15
+ from MediCafe.core_utils import (
16
+ import_medibot_module,
17
+ get_config_loader_with_fallback
18
+ )
19
+
20
+ # Initialize configuration loader with fallback
21
+ MediLink_ConfigLoader = get_config_loader_with_fallback()
22
+
23
+ # Import MediBot modules using centralized import functions
24
+ MediBot_Preprocessor_lib = import_medibot_module('MediBot_Preprocessor_lib')
25
+
26
+ # Import utility functions from MediBot_Crosswalk_Utils.py using centralized import
27
+ MediBot_Crosswalk_Utils = import_medibot_module('MediBot_Crosswalk_Utils')
28
+ if MediBot_Crosswalk_Utils:
29
+ check_crosswalk_health = getattr(MediBot_Crosswalk_Utils, 'check_crosswalk_health', None)
30
+ prompt_user_for_api_calls = getattr(MediBot_Crosswalk_Utils, 'prompt_user_for_api_calls', None)
31
+ select_endpoint = getattr(MediBot_Crosswalk_Utils, 'select_endpoint', None)
32
+ ensure_full_config_loaded = getattr(MediBot_Crosswalk_Utils, 'ensure_full_config_loaded', None)
33
+ save_crosswalk = getattr(MediBot_Crosswalk_Utils, 'save_crosswalk', None)
34
+ update_crosswalk_with_corrected_payer_id = getattr(MediBot_Crosswalk_Utils, 'update_crosswalk_with_corrected_payer_id', None)
35
+ update_crosswalk_with_new_payer_id = getattr(MediBot_Crosswalk_Utils, 'update_crosswalk_with_new_payer_id', None)
36
+ load_and_parse_z_data = getattr(MediBot_Crosswalk_Utils, 'load_and_parse_z_data', None)
37
+ else:
38
+ # Set all functions to None if import fails completely
39
+ check_crosswalk_health = None
40
+ prompt_user_for_api_calls = None
41
+ select_endpoint = None
42
+ ensure_full_config_loaded = None
43
+ save_crosswalk = None
44
+ update_crosswalk_with_corrected_payer_id = None
45
+ update_crosswalk_with_new_payer_id = None
46
+ load_and_parse_z_data = None
47
+
48
+ # Module-level cache to prevent redundant API calls
49
+ _api_cache = {}
55
50
 
51
+ """
52
+ # RESOLVED: Redundant API calls have been eliminated through module-level caching.
53
+ # The _api_cache prevents duplicate API calls for the same payer_id within a session.
56
54
  """
57
55
 
56
+ # =============================================================================
57
+ # CORE CROSSWALK OPERATIONS - Main Library Functions
58
+ # =============================================================================
59
+ # These functions handle the primary crosswalk operations and are kept in the main
60
+ # library because they are frequently called and contain the core business logic.
61
+ # Utility functions have been moved to MediBot_Crosswalk_Utils.py to reduce
62
+ # the main library size and improve maintainability.
58
63
 
59
- def fetch_and_store_payer_name(client, payer_id, crosswalk, config):
64
+ def fetch_and_store_payer_name(client, payer_id, crosswalk, config, api_cache=None):
60
65
  """
61
66
  Fetches the payer name for a given payer ID and stores it in the crosswalk.
67
+ Now with optional API cache to prevent redundant calls.
62
68
 
63
69
  Args:
64
70
  payer_id (str): The ID of the payer to fetch.
65
71
  crosswalk (dict): The crosswalk dictionary to store the payer name.
66
72
  config (dict): Configuration settings for logging.
73
+ api_cache (dict, optional): Cache to prevent redundant API calls. Uses module-level cache if None.
67
74
 
68
75
  Returns:
69
76
  bool: True if the payer name was fetched and stored successfully, False otherwise.
70
77
  """
71
- MediLink_ConfigLoader.log("Attempting to fetch payer name for Payer ID: {}".format(payer_id), config, level="DEBUG")
72
- try:
73
- # Fetch the payer name from the API
74
- payer_name = fetch_payer_name_from_api(client, payer_id, config, primary_endpoint=None)
75
- MediLink_ConfigLoader.log("Fetched payer name: {} for Payer ID: {}".format(payer_name, payer_id), config, level="DEBUG")
78
+ global _api_cache
79
+
80
+ # Use provided cache or module-level cache
81
+ if api_cache is None:
82
+ api_cache = _api_cache
83
+
84
+ # Check if we already have this payer_id in cache
85
+ if payer_id in api_cache:
86
+ payer_name = api_cache[payer_id]
87
+ MediLink_ConfigLoader.log("Using cached payer name for Payer ID: {}".format(payer_id), config, level="DEBUG")
88
+ else:
89
+ MediLink_ConfigLoader.log("Attempting to fetch payer name for Payer ID: {}".format(payer_id), config, level="DEBUG")
90
+ try:
91
+ # Fetch the payer name from the API
92
+ payer_name = fetch_payer_name_from_api(client, payer_id, config, primary_endpoint=None)
93
+ # Cache the result
94
+ api_cache[payer_id] = payer_name
95
+ MediLink_ConfigLoader.log("Fetched and cached payer name: {} for Payer ID: {}".format(payer_name, payer_id), config, level="DEBUG")
96
+ except Exception as e:
97
+ # Log any errors encountered during the fetching process
98
+ MediLink_ConfigLoader.log("Failed to fetch name for Payer ID {}: {}".format(payer_id, e), config, level="WARNING")
99
+ payer_name = "Unknown"
100
+ api_cache[payer_id] = payer_name
101
+ return False
76
102
 
77
103
  # Ensure the 'payer_id' key exists in the crosswalk
78
104
  if 'payer_id' not in crosswalk:
@@ -90,13 +116,8 @@ def fetch_and_store_payer_name(client, payer_id, crosswalk, config):
90
116
  MediLink_ConfigLoader.log(message, config, level="INFO")
91
117
  print(message)
92
118
  return True
93
- except Exception as e:
94
- # Log any errors encountered during the fetching process
95
- MediLink_ConfigLoader.log("Failed to fetch name for Payer ID {}: {}".format(payer_id, e), config, level="WARNING")
96
- crosswalk['payer_id'][payer_id]['name'] = "Unknown"
97
- return False
98
119
 
99
- def validate_and_correct_payer_ids(client, crosswalk, config, auto_correct=False):
120
+ def validate_and_correct_payer_ids(client, crosswalk, config, auto_correct=False, api_cache=None):
100
121
  """
101
122
  Validates and corrects payer IDs in the crosswalk. If a payer ID is invalid, it prompts the user for correction.
102
123
 
@@ -104,6 +125,7 @@ def validate_and_correct_payer_ids(client, crosswalk, config, auto_correct=False
104
125
  crosswalk (dict): The crosswalk dictionary containing payer IDs.
105
126
  config (dict): Configuration settings for logging.
106
127
  auto_correct (bool): If True, automatically corrects invalid payer IDs to 'Unknown'.
128
+ api_cache (dict, optional): Cache to prevent redundant API calls.
107
129
  """
108
130
  processed_payer_ids = set() # Track processed payer IDs
109
131
  payer_ids = list(crosswalk.get('payer_id', {}).keys()) # Static list to prevent modification issues
@@ -113,7 +135,7 @@ def validate_and_correct_payer_ids(client, crosswalk, config, auto_correct=False
113
135
  continue # Skip already processed payer IDs
114
136
 
115
137
  # Validate the payer ID by fetching its name
116
- is_valid = fetch_and_store_payer_name(client, payer_id, crosswalk, config)
138
+ is_valid = fetch_and_store_payer_name(client, payer_id, crosswalk, config, api_cache)
117
139
 
118
140
  if not is_valid:
119
141
  if auto_correct:
@@ -150,9 +172,9 @@ def validate_and_correct_payer_ids(client, crosswalk, config, auto_correct=False
150
172
 
151
173
  if corrected_payer_id:
152
174
  # Validate the corrected payer ID
153
- if fetch_and_store_payer_name(client, corrected_payer_id, crosswalk, config):
175
+ if fetch_and_store_payer_name(client, corrected_payer_id, crosswalk, config, api_cache):
154
176
  # Replace the old payer ID with the corrected one in the crosswalk
155
- success = update_crosswalk_with_corrected_payer_id(client, payer_id, corrected_payer_id, config, crosswalk)
177
+ success = update_crosswalk_with_corrected_payer_id(client, payer_id, corrected_payer_id, config, crosswalk, api_cache)
156
178
  if success:
157
179
  print("Payer ID '{}' has been successfully replaced with '{}'.".format(
158
180
  payer_id, corrected_payer_id))
@@ -211,184 +233,12 @@ def initialize_crosswalk_from_mapat(client, config, crosswalk):
211
233
 
212
234
  return crosswalk['payer_id']
213
235
 
214
- def load_and_parse_z_data(config):
215
- """
216
- Loads and parses Z data for patient to insurance name mappings from the specified directory.
217
-
218
- Args:
219
- config (dict): Configuration settings for logging.
220
-
221
- Returns:
222
- dict: A mapping of patient IDs to insurance names.
223
- """
224
- patient_id_to_insurance_name = {}
225
- try:
226
- z_dat_path = config['MediLink_Config']['Z_DAT_PATH']
227
- MediLink_ConfigLoader.log("Z_DAT_PATH is set to: {}".format(z_dat_path), config, level="DEBUG")
228
-
229
- # Get the directory of the Z_DAT_PATH
230
- directory = os.path.dirname(z_dat_path)
231
- MediLink_ConfigLoader.log("Looking for .DAT files in directory: {}".format(directory), config, level="DEBUG")
232
-
233
- # List all .DAT files in the directory, case insensitive
234
- dat_files = [f for f in os.listdir(directory) if f.lower().endswith('.dat')]
235
- MediLink_ConfigLoader.log("Found {} .DAT files in the directory.".format(len(dat_files)), config, level="DEBUG")
236
-
237
- # Load processed files tracking
238
- processed_files_path = os.path.join(directory, 'processed_files.txt')
239
- if os.path.exists(processed_files_path):
240
- with open(processed_files_path, 'r') as f:
241
- processed_files = set(line.strip() for line in f)
242
- MediLink_ConfigLoader.log("Loaded processed files: {}.".format(processed_files), config, level="DEBUG")
243
- else:
244
- processed_files = set()
245
- MediLink_ConfigLoader.log("No processed files found, starting fresh.", config, level="DEBUG")
246
-
247
- # Filter for new .DAT files that haven't been processed yet, but always include Z.DAT and ZM.DAT
248
- new_dat_files = [f for f in dat_files if f not in processed_files or f.lower() in ['z.dat', 'zm.dat']]
249
- MediLink_ConfigLoader.log("Identified {} new .DAT files to process.".format(len(new_dat_files)), config, level="INFO")
250
-
251
- for dat_file in new_dat_files:
252
- file_path = os.path.join(directory, dat_file)
253
- MediLink_ConfigLoader.log("Parsing .DAT file: {}".format(file_path), config, level="DEBUG")
254
- # Parse each .DAT file and accumulate results
255
- insurance_name_mapping = MediBot_Preprocessor_lib.parse_z_dat(file_path, config['MediLink_Config'])
256
- if insurance_name_mapping: # Ensure insurance_name_mapping is not empty
257
- patient_id_to_insurance_name.update(insurance_name_mapping)
258
-
259
- # Mark this file as processed
260
- with open(processed_files_path, 'a') as f:
261
- f.write(dat_file + '\n')
262
- MediLink_ConfigLoader.log("Marked file as processed: {}".format(dat_file), config, level="DEBUG")
263
-
264
- if not patient_id_to_insurance_name: # Check if the result is empty
265
- raise ValueError("Parsed Z data is empty, possibly indicating an error in parsing or all files already processed.") # TODO Add differentiator here because this is dumb.
266
- MediLink_ConfigLoader.log("Successfully parsed Z data with {} mappings found.".format(len(patient_id_to_insurance_name)), config, level="INFO")
267
- return patient_id_to_insurance_name # Ensure the function returns the mapping
268
- except Exception as e:
269
- MediLink_ConfigLoader.log("Error loading and parsing Z data: {}".format(e), config, level="ERROR")
270
- return {}
271
-
272
- def check_crosswalk_health(crosswalk):
273
- """
274
- Simple health check for crosswalk - checks if payers have names and at least one medisoft ID.
275
- A payer is considered healthy if it has a name (not "Unknown") and at least one medisoft ID,
276
- which can exist in either 'medisoft_id' OR 'medisoft_medicare_id'. It is NOT required to have both.
277
-
278
- Args:
279
- crosswalk (dict): The crosswalk dictionary to check.
280
-
281
- Returns:
282
- tuple: (is_healthy, missing_names_count, missing_medisoft_ids_count, missing_names_list, missing_medisoft_ids_list)
283
- """
284
- if 'payer_id' not in crosswalk or not crosswalk['payer_id']:
285
- return False, 0, 0, [], []
286
-
287
- missing_names = 0
288
- missing_medisoft_ids = 0
289
- missing_names_list = []
290
- missing_medisoft_ids_list = []
291
-
292
- for payer_id, details in crosswalk['payer_id'].items():
293
- # Check if name is missing or "Unknown"
294
- name = details.get('name', '')
295
- if not name or name == 'Unknown':
296
- missing_names += 1
297
- missing_names_list.append(payer_id)
298
-
299
- # Check if at least one medisoft ID exists in either field
300
- medisoft_id = details.get('medisoft_id', [])
301
- medisoft_medicare_id = details.get('medisoft_medicare_id', [])
302
236
 
303
- # Convert to list if it's a set (for compatibility)
304
- if isinstance(medisoft_id, set):
305
- medisoft_id = list(medisoft_id)
306
- if isinstance(medisoft_medicare_id, set):
307
- medisoft_medicare_id = list(medisoft_medicare_id)
308
-
309
- # If both are empty, count as missing; if either has at least one, it's healthy
310
- if not medisoft_id and not medisoft_medicare_id:
311
- missing_medisoft_ids += 1
312
- missing_medisoft_ids_list.append(payer_id)
313
-
314
- # Consider healthy if no missing names and no missing medisoft IDs
315
- is_healthy = (missing_names == 0 and missing_medisoft_ids == 0)
316
- return is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list
317
-
318
- def prompt_user_for_api_calls(crosswalk, config):
319
- """
320
- Prompts user with a 3-second timeout to skip API calls if crosswalk looks healthy.
321
- Windows XP compatible version using threading instead of select.
322
-
323
- Args:
324
- crosswalk (dict): The crosswalk dictionary to check.
325
- config (dict): Configuration settings for logging.
326
-
327
- Returns:
328
- bool: True if should proceed with API calls, False if should skip
329
- """
330
-
331
- is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list = check_crosswalk_health(crosswalk)
332
- total_payers = len(crosswalk.get('payer_id', {}))
333
-
334
- if is_healthy:
335
- print("\nCrosswalk appears healthy:")
336
- print(" - {} payers found".format(total_payers))
337
- print(" - All payers have names")
338
- print(" - All payers have medisoft IDs")
339
- print("\nPress ENTER to run API validation, or wait 2 seconds to skip...")
340
-
341
- # Use threading for timeout on Windows
342
- user_input = [None] # Use list to store result from thread
343
-
344
- def get_input():
345
- try:
346
- user_input[0] = input()
347
- except (EOFError, KeyboardInterrupt):
348
- user_input[0] = ""
349
-
350
- # Start input thread
351
- input_thread = threading.Thread(target=get_input)
352
- input_thread.daemon = True
353
- input_thread.start()
354
-
355
- # Wait for 2 seconds or until input is received
356
- input_thread.join(timeout=2.0)
357
-
358
- if user_input[0] is not None:
359
- print("Running API validation calls...")
360
- MediLink_ConfigLoader.log("User pressed ENTER - proceeding with API calls", config, level="INFO")
361
- return True
362
- else:
363
- print("Timed out - skipping API calls")
364
- MediLink_ConfigLoader.log("Timeout - skipping API calls", config, level="INFO")
365
- return False
366
- else:
367
- print("\nCrosswalk needs attention:")
368
- print(" - {} payers found".format(total_payers))
369
-
370
- # Show detailed information about missing names
371
- if missing_names > 0:
372
- print(" - {} payers missing names: {}".format(missing_names, ", ".join(missing_names_list)))
373
-
374
- # Show detailed information about missing medisoft IDs
375
- if missing_medisoft_ids > 0:
376
- print(" - {} payers missing medisoft IDs: {}".format(missing_medisoft_ids, ", ".join(missing_medisoft_ids_list)))
377
- # API validation CANNOT resolve missing medisoft IDs
378
- print(" TODO: Need user interface to manually input medisoft IDs for these payers")
379
-
380
- # Only proceed with API calls if there are missing names (API can help with those)
381
- if missing_names > 0:
382
- print("Proceeding with API validation calls to resolve missing names...")
383
- MediLink_ConfigLoader.log("Crosswalk has missing names - proceeding with API calls", config, level="INFO")
384
- return True
385
- else:
386
- print("No missing names to resolve via API. Skipping API validation calls.")
387
- print("TODO: Manual intervention needed for missing medisoft IDs")
388
- MediLink_ConfigLoader.log("Crosswalk has missing medisoft IDs but no missing names - skipping API calls", config, level="INFO")
389
- return False
237
+ # =============================================================================
238
+ # MAIN CROSSWALK UPDATE FUNCTION
239
+ # =============================================================================
390
240
 
391
- def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstream of this is only MediBot_Preprocessor.py and MediBot.py
241
+ def crosswalk_update(client, config, crosswalk): # Upstream of this is only MediBot_Preprocessor.py and MediBot.py
392
242
  """
393
243
  Updates the crosswalk with insurance data and historical mappings.
394
244
  It loads insurance data, historical payer mappings, and updates the crosswalk accordingly.
@@ -396,36 +246,56 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
396
246
  Args:
397
247
  config (dict): Configuration settings for logging.
398
248
  crosswalk (dict): The crosswalk dictionary to update.
399
- skip_known_payers (bool): If True, skips records with 'name' not equal to 'Unknown'.
400
249
 
401
250
  Returns:
402
251
  bool: True if the crosswalk was updated successfully, False otherwise.
403
252
  """
404
253
  MediLink_ConfigLoader.log("Starting crosswalk update process...", config, level="INFO")
405
254
 
406
- # Load insurance data from MAINS
255
+ # Initialize API cache for this session to prevent redundant calls
256
+ api_cache = {}
257
+
258
+ # Load insurance data from MAINS (optional - continue if not available)
259
+ insurance_name_to_id = {}
407
260
  try:
408
261
  MediLink_ConfigLoader.log("Attempting to load insurance data from MAINS...", config, level="DEBUG")
409
262
  insurance_name_to_id = MediBot_Preprocessor_lib.load_insurance_data_from_mains(config)
410
263
  MediLink_ConfigLoader.log("Loaded insurance data from MAINS with {} entries.".format(len(insurance_name_to_id)), config, level="INFO")
411
264
  except Exception as e:
412
- MediLink_ConfigLoader.log("Error loading insurance data from MAINS: {}".format(e), config, level="ERROR")
413
- return False
265
+ MediLink_ConfigLoader.log("Error loading insurance data from MAINS: {}. Continuing without MAINS data.".format(e), config, level="WARNING")
266
+ print("Warning: MAINS data not available. Some crosswalk features may be limited.")
267
+ # Continue without MAINS data - don't return False
414
268
 
415
- # Load historical payer to patient mappings
269
+ # Load historical payer to patient mappings (optional - continue if not available)
270
+ patient_id_to_payer_id = {}
416
271
  try:
417
272
  MediLink_ConfigLoader.log("Attempting to load historical payer to patient mappings...", config, level="DEBUG")
418
273
  patient_id_to_payer_id = MediBot_Preprocessor_lib.load_historical_payer_to_patient_mappings(config)
419
274
  MediLink_ConfigLoader.log("Loaded historical mappings with {} entries.".format(len(patient_id_to_payer_id)), config, level="INFO")
420
275
  except Exception as e:
421
- MediLink_ConfigLoader.log("Error loading historical mappings: {}".format(e), config, level="ERROR")
422
- return False
276
+ MediLink_ConfigLoader.log("Error loading historical mappings: {}. Continuing without historical data.".format(e), config, level="WARNING")
277
+ print("Warning: Historical mappings not available. Some crosswalk features may be limited.")
278
+ # Continue without historical data - don't return False
423
279
 
424
280
  # Parse Z data for patient to insurance name mappings
425
281
  try:
426
- patient_id_to_insurance_name = load_and_parse_z_data(config)
282
+ patient_id_to_insurance_name, z_data_status = load_and_parse_z_data(config)
427
283
  mapping_count = len(patient_id_to_insurance_name) if patient_id_to_insurance_name is not None else 0
428
- MediLink_ConfigLoader.log("Parsed Z data with {} mappings found.".format(mapping_count), config, level="INFO")
284
+ MediLink_ConfigLoader.log("Parsed Z data with {} mappings found. Status: {}".format(mapping_count, z_data_status), config, level="INFO")
285
+
286
+ # Handle different Z data statuses
287
+ if z_data_status == "error":
288
+ MediLink_ConfigLoader.log("Error occurred during Z data parsing.", config, level="ERROR")
289
+ return False
290
+ elif z_data_status == "success_no_new_files":
291
+ MediLink_ConfigLoader.log("No new Z data files to process - this is normal if all files have been processed.", config, level="INFO")
292
+ # Continue with crosswalk update even if no new Z data
293
+ elif z_data_status == "success_empty_files":
294
+ MediLink_ConfigLoader.log("Z data files were processed but contained no valid mappings - this may indicate empty or malformed files.", config, level="WARNING")
295
+ # Continue with crosswalk update even if Z data is empty
296
+ elif z_data_status == "success_with_data":
297
+ MediLink_ConfigLoader.log("Successfully processed Z data with new mappings.", config, level="INFO")
298
+ # Normal case - continue with crosswalk update
429
299
  except Exception as e:
430
300
  MediLink_ConfigLoader.log("Error parsing Z data in crosswalk update: {}".format(e), config, level="ERROR")
431
301
  return False
@@ -454,22 +324,6 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
454
324
  # Continue with existing crosswalk update logic...
455
325
  # Update the crosswalk with new payer IDs and insurance IDs
456
326
  for patient_id, payer_id in patient_id_to_payer_id.items():
457
- """ TODO this needs to be implemented at some point so we can skip known entities.
458
- # Skip known payers if the flag is set
459
- if skip_known_payers:
460
- payer_id_str = next(iter(payer_id)) # Extract the single payer_id from the set
461
- MediLink_ConfigLoader.log("Checking if payer_id '{}' is known...".format(payer_id_str), config, level="DEBUG")
462
- payer_info = crosswalk['payer_id'].get(payer_id_str, {})
463
- payer_name = payer_info.get('name', "Unknown")
464
- MediLink_ConfigLoader.log("Retrieved payer name: '{}' for payer_id '{}'.".format(payer_name, payer_id), config, level="DEBUG")
465
-
466
- if payer_name != "Unknown":
467
- MediLink_ConfigLoader.log("Skipping known payer_id: '{}' as it is already in the crosswalk.".format(payer_id), config, level="DEBUG")
468
- continue # Skip this payer_id
469
- MediLink_ConfigLoader.log("Skipping known payer_id: {} as it is already in the crosswalk.".format(payer_id), config, level="DEBUG")
470
- continue # Skip this payer_id
471
- """
472
-
473
327
  insurance_name = patient_id_to_insurance_name.get(patient_id)
474
328
  if insurance_name and insurance_name in insurance_name_to_id:
475
329
  insurance_id = insurance_name_to_id[insurance_name]
@@ -519,12 +373,12 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
519
373
 
520
374
  # Fetch and store the payer name
521
375
  MediLink_ConfigLoader.log("Fetching and storing payer name for payer_id: {}".format(payer_id), config, level="DEBUG")
522
- fetch_and_store_payer_name(client, payer_id, crosswalk, config)
376
+ fetch_and_store_payer_name(client, payer_id, crosswalk, config, api_cache)
523
377
  MediLink_ConfigLoader.log("Successfully fetched and stored payer name for payer_id: {}".format(payer_id), config, level="INFO")
524
378
 
525
379
  # Validate and correct payer IDs in the crosswalk
526
380
  MediLink_ConfigLoader.log("Validating and correcting payer IDs in the crosswalk.", config, level="DEBUG")
527
- validate_and_correct_payer_ids(client, crosswalk, config)
381
+ validate_and_correct_payer_ids(client, crosswalk, config, api_cache=api_cache)
528
382
 
529
383
  # Check for any entries marked as "Unknown" and validate them
530
384
  unknown_payers = [
@@ -534,7 +388,7 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
534
388
  MediLink_ConfigLoader.log("Found {} unknown payer(s) to validate.".format(len(unknown_payers)), config, level="INFO")
535
389
  for payer_id in unknown_payers:
536
390
  MediLink_ConfigLoader.log("Fetching and storing payer name for unknown payer_id: {}".format(payer_id), config, level="DEBUG")
537
- fetch_and_store_payer_name(client, payer_id, crosswalk, config)
391
+ fetch_and_store_payer_name(client, payer_id, crosswalk, config, api_cache)
538
392
  MediLink_ConfigLoader.log("Successfully fetched and stored payer name for unknown payer_id: {}".format(payer_id), config, level="INFO")
539
393
 
540
394
  # PERFORMANCE FIX: Optimized list management - avoid redundant set/list conversions
@@ -559,359 +413,9 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
559
413
  crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = list(dict.fromkeys(medicare_id))
560
414
 
561
415
  MediLink_ConfigLoader.log("Crosswalk update process completed. Processed {} payer IDs.".format(len(patient_id_to_payer_id)), config, level="INFO")
562
- return save_crosswalk(client, config, crosswalk)
416
+ return save_crosswalk(client, config, crosswalk, api_cache=api_cache)
563
417
 
564
- def update_crosswalk_with_corrected_payer_id(client, old_payer_id, corrected_payer_id, config=None, crosswalk=None):
565
- """
566
- Updates the crosswalk by replacing an old payer ID with a corrected payer ID.
567
-
568
- Args:
569
- old_payer_id (str): The old payer ID to be replaced.
570
- corrected_payer_id (str): The new payer ID to replace the old one.
571
- config (dict, optional): Configuration settings for logging.
572
- crosswalk (dict, optional): The crosswalk dictionary to update.
573
-
574
- Returns:
575
- bool: True if the crosswalk was updated successfully, False otherwise.
576
- """
577
- # Ensure full configuration and crosswalk are loaded
578
- config, crosswalk = ensure_full_config_loaded(config, crosswalk)
579
-
580
- # Convert to a regular dict if crosswalk['payer_id'] is an OrderedDict
581
- if isinstance(crosswalk['payer_id'], dict) and hasattr(crosswalk['payer_id'], 'items'):
582
- crosswalk['payer_id'] = dict(crosswalk['payer_id'])
583
-
584
- MediLink_ConfigLoader.log("Checking if old Payer ID {} exists in crosswalk.".format(old_payer_id), config, level="DEBUG")
585
-
586
- MediLink_ConfigLoader.log("Attempting to replace old Payer ID {} with corrected Payer ID {}.".format(old_payer_id, corrected_payer_id), config, level="DEBUG")
587
-
588
- # Check if the old payer ID exists before attempting to replace
589
- if old_payer_id in crosswalk['payer_id']:
590
- MediLink_ConfigLoader.log("Old Payer ID {} found. Proceeding with replacement.".format(old_payer_id), config, level="DEBUG")
591
-
592
- # Store the details of the old payer ID
593
- old_payer_details = crosswalk['payer_id'][old_payer_id]
594
- MediLink_ConfigLoader.log("Storing details of old Payer ID {}: {}".format(old_payer_id, old_payer_details), config, level="DEBUG")
595
-
596
- # Replace the old payer ID with the corrected one
597
- crosswalk['payer_id'][corrected_payer_id] = old_payer_details
598
- MediLink_ConfigLoader.log("Replaced old Payer ID {} with corrected Payer ID {}.".format(old_payer_id, corrected_payer_id), config, level="INFO")
599
-
600
- # Remove the old payer ID from the crosswalk
601
- del crosswalk['payer_id'][old_payer_id]
602
- MediLink_ConfigLoader.log("Removed old Payer ID {} from crosswalk.".format(old_payer_id), config, level="DEBUG")
603
-
604
- # Fetch and store the payer name for the corrected ID
605
- if fetch_and_store_payer_name(client, corrected_payer_id, crosswalk, config):
606
- MediLink_ConfigLoader.log("Successfully fetched and stored payer name for corrected Payer ID {}.".format(corrected_payer_id), config, level="INFO")
607
- else:
608
- MediLink_ConfigLoader.log("Corrected Payer ID {} updated without a valid name.".format(corrected_payer_id), config, level="WARNING")
609
-
610
- # Update csv_replacements
611
- crosswalk.setdefault('csv_replacements', {})[old_payer_id] = corrected_payer_id
612
- MediLink_ConfigLoader.log("Updated csv_replacements: {} -> {}.".format(old_payer_id, corrected_payer_id), config, level="INFO")
613
- print("csv_replacements updated: '{}' -> '{}'.".format(old_payer_id, corrected_payer_id))
614
-
615
- return save_crosswalk(client, config, crosswalk)
616
- else:
617
- MediLink_ConfigLoader.log("Failed to update crosswalk: old Payer ID {} not found.".format(old_payer_id), config, level="ERROR")
618
- print("Failed to update crosswalk: could not find old Payer ID '{}'.".format(old_payer_id))
619
- return False
620
418
 
621
- def update_crosswalk_with_new_payer_id(client, insurance_name, payer_id, config, crosswalk):
622
- """
623
- Updates the crosswalk with a new payer ID for a given insurance name.
624
-
625
- Args:
626
- insurance_name (str): The name of the insurance to associate with the new payer ID.
627
- payer_id (str): The new payer ID to be added.
628
- config (dict): Configuration settings for logging.
629
- """
630
- # Ensure full configuration and crosswalk are loaded
631
- config, crosswalk = ensure_full_config_loaded(config, crosswalk)
632
-
633
- try:
634
- # Check if 'payer_id' is present in the crosswalk
635
- if 'payer_id' not in crosswalk or not crosswalk['payer_id']:
636
- # Reload the crosswalk if 'payer_id' is missing or empty
637
- _, crosswalk = MediLink_ConfigLoader.load_configuration(None, config.get('crosswalkPath', 'crosswalk.json'))
638
- MediLink_ConfigLoader.log("Reloaded crosswalk configuration from {}.".format(config.get('crosswalkPath', 'crosswalk.json')), config, level="DEBUG")
639
- except KeyError as e: # Handle KeyError for crosswalk
640
- MediLink_ConfigLoader.log("KeyError while checking or reloading crosswalk: {}".format(e), config, level="ERROR")
641
- print("KeyError while checking or reloading crosswalk in update_crosswalk_with_new_payer_id: {}".format(e))
642
- return False
643
- except Exception as e:
644
- MediLink_ConfigLoader.log("Error while checking or reloading crosswalk: {}".format(e), config, level="ERROR")
645
- print("Error while checking or reloading crosswalk in update_crosswalk_with_new_payer_id: {}".format(e))
646
- return False
647
-
648
- # Load the Medisoft ID for the given insurance name
649
- try:
650
- medisoft_id = MediBot_Preprocessor_lib.load_insurance_data_from_mains(config).get(insurance_name)
651
- except KeyError as e: # Handle KeyError for config
652
- MediLink_ConfigLoader.log("KeyError while loading Medisoft ID: {}".format(e), config, level="ERROR")
653
- print("KeyError while loading Medisoft ID for insurance name {}: {}".format(insurance_name, e))
654
- return False
655
419
 
656
- MediLink_ConfigLoader.log("Retrieved Medisoft ID for insurance name {}: {}.".format(insurance_name, medisoft_id), config, level="DEBUG")
657
- # print("DEBUG: Retrieved Medisoft ID for insurance name {}: {}.".format(insurance_name, medisoft_id))
658
-
659
- if medisoft_id:
660
- medisoft_id_str = str(medisoft_id)
661
- MediLink_ConfigLoader.log("Processing to update crosswalk with new payer ID: {} for insurance name: {}.".format(payer_id, insurance_name), config, level="DEBUG")
662
-
663
- # Initialize the payer ID entry if it doesn't exist
664
- if payer_id not in crosswalk['payer_id']:
665
- selected_endpoint = select_endpoint(config) # Use the helper function to select the endpoint
666
-
667
- # Ensure the 'payer_id' key exists in the crosswalk
668
- crosswalk['payer_id'][payer_id] = {
669
- 'endpoint': selected_endpoint,
670
- 'medisoft_id': [], # PERFORMANCE FIX: Use list instead of set to avoid conversions
671
- 'medisoft_medicare_id': []
672
- }
673
- MediLink_ConfigLoader.log("Initialized payer ID {} in crosswalk with endpoint '{}'.".format(payer_id, selected_endpoint), config, level="DEBUG")
674
- else:
675
- # Check if the existing endpoint is valid
676
- current_endpoint = crosswalk['payer_id'][payer_id].get('endpoint', None)
677
- if current_endpoint and current_endpoint not in config['MediLink_Config']['endpoints']:
678
- print("WARNING: The current endpoint '{}' for payer ID '{}' is not valid.".format(current_endpoint, payer_id))
679
- MediLink_ConfigLoader.log("Current endpoint '{}' for payer ID '{}' is not valid. Prompting for selection.".format(current_endpoint, payer_id), config, level="WARNING")
680
- selected_endpoint = select_endpoint(config, current_endpoint) # Prompt user to select a valid endpoint
681
- crosswalk['payer_id'][payer_id]['endpoint'] = selected_endpoint # Update the endpoint in the crosswalk
682
- MediLink_ConfigLoader.log("Updated payer ID {} with new endpoint '{}'.".format(payer_id, selected_endpoint), config, level="INFO")
683
- else:
684
- selected_endpoint = current_endpoint # Use the existing valid endpoint
685
-
686
- # Add the insurance ID to the payer ID entry - with error handling for the .add() operation
687
- try:
688
- if not isinstance(crosswalk['payer_id'][payer_id]['medisoft_id'], set):
689
- # Convert to set if it's not already one
690
- crosswalk['payer_id'][payer_id]['medisoft_id'] = set(crosswalk['payer_id'][payer_id]['medisoft_id'])
691
- MediLink_ConfigLoader.log("Converted medisoft_id to set for payer ID {}.".format(payer_id), config, level="DEBUG")
692
-
693
- crosswalk['payer_id'][payer_id]['medisoft_id'].add(str(medisoft_id_str)) # Ensure IDs are strings
694
- MediLink_ConfigLoader.log(
695
- "Added new insurance ID {} to payer ID {}.".format(medisoft_id_str, payer_id),
696
- config,
697
- level="INFO"
698
- )
699
- except AttributeError as e:
700
- MediLink_ConfigLoader.log("AttributeError while adding medisoft_id: {}".format(e), config, level="ERROR")
701
- print("Error adding medisoft_id for payer ID {}: {}".format(payer_id, e))
702
- return False
703
-
704
- # Fetch and store the payer name for the new payer ID
705
- if fetch_and_store_payer_name(client, payer_id, crosswalk, config):
706
- MediLink_ConfigLoader.log("Successfully fetched and stored payer name for new payer ID {}.".format(payer_id), config, level="INFO")
707
- MediLink_ConfigLoader.log("Updated crosswalk with new payer ID {} for insurance name {}.".format(payer_id, insurance_name), config, level="INFO")
708
- else:
709
- MediLink_ConfigLoader.log("Added new payer ID {} without a valid name for insurance name {}.".format(payer_id, insurance_name), config, level="WARNING")
710
-
711
- # Save the updated crosswalk
712
- save_crosswalk(client, config, crosswalk)
713
- MediLink_ConfigLoader.log("Crosswalk saved successfully after updating payer ID {}.".format(payer_id), config, level="DEBUG")
714
- else:
715
- message = "Failed to update crosswalk: Medisoft ID not found for insurance name {}.".format(insurance_name)
716
- print(message)
717
- MediLink_ConfigLoader.log(message, config, level="ERROR")
718
-
719
- def save_crosswalk(client, config, crosswalk, skip_api_operations=False):
720
- """
721
- Saves the crosswalk to a JSON file. Ensures that all necessary keys are present and logs the outcome.
722
-
723
- Args:
724
- client (APIClient): API client for fetching payer names (ignored if skip_api_operations=True).
725
- config (dict): Configuration settings for logging.
726
- crosswalk (dict): The crosswalk dictionary to save.
727
- skip_api_operations (bool): If True, skips API calls and user prompts for faster saves.
728
-
729
- Returns:
730
- bool: True if the crosswalk was saved successfully, False otherwise.
731
- """
732
- try:
733
- # Determine the path to save the crosswalk
734
- crosswalk_path = config['MediLink_Config']['crosswalkPath']
735
- MediLink_ConfigLoader.log("Determined crosswalk path: {}.".format(crosswalk_path), config, level="DEBUG")
736
- except KeyError:
737
- crosswalk_path = config.get('crosswalkPath', 'crosswalk.json')
738
- MediLink_ConfigLoader.log("Using default crosswalk path: {}.".format(crosswalk_path), config, level="DEBUG")
739
-
740
- # Validate endpoints for each payer ID in the crosswalk
741
- for payer_id, details in crosswalk.get('payer_id', {}).items():
742
- current_endpoint = details.get('endpoint', None)
743
- if current_endpoint and current_endpoint not in config['MediLink_Config']['endpoints']:
744
- if skip_api_operations:
745
- # Log warning but don't prompt user during API-bypass mode
746
- MediLink_ConfigLoader.log("WARNING: Invalid endpoint '{}' for payer ID '{}' - skipping correction due to API bypass mode".format(current_endpoint, payer_id), config, level="WARNING")
747
- else:
748
- print("WARNING: The current endpoint '{}' for payer ID '{}' is not valid.".format(current_endpoint, payer_id))
749
- MediLink_ConfigLoader.log("Current endpoint '{}' for payer ID '{}' is not valid. Prompting for selection.".format(current_endpoint, payer_id), config, level="WARNING")
750
- selected_endpoint = select_endpoint(config, current_endpoint) # Prompt user to select a valid endpoint
751
- crosswalk['payer_id'][payer_id]['endpoint'] = selected_endpoint # Update the endpoint in the crosswalk
752
- MediLink_ConfigLoader.log("Updated payer ID {} with new endpoint '{}'.".format(payer_id, selected_endpoint), config, level="INFO")
753
-
754
- try:
755
- # Log API bypass mode if enabled
756
- if skip_api_operations:
757
- MediLink_ConfigLoader.log("save_crosswalk running in API bypass mode - skipping API calls and user prompts", config, level="INFO")
758
-
759
- # Initialize the 'payer_id' key if it doesn't exist
760
- if 'payer_id' not in crosswalk:
761
- print("save_crosswalk is initializing 'payer_id' key...")
762
- crosswalk['payer_id'] = {}
763
- MediLink_ConfigLoader.log("Initialized 'payer_id' key in crosswalk.", config, level="INFO")
764
-
765
- # Ensure all payer IDs have a name and initialize medisoft_id and medisoft_medicare_id as empty lists if they do not exist
766
- for payer_id in crosswalk['payer_id']:
767
- if 'name' not in crosswalk['payer_id'][payer_id]:
768
- if skip_api_operations:
769
- # Set placeholder name and log for MediBot to handle later
770
- crosswalk['payer_id'][payer_id]['name'] = 'Unknown'
771
- MediLink_ConfigLoader.log("Set placeholder name for payer ID {} - will be resolved by MediBot health check".format(payer_id), config, level="INFO")
772
- else:
773
- fetch_and_store_payer_name(client, payer_id, crosswalk, config)
774
- MediLink_ConfigLoader.log("Fetched and stored payer name for payer ID: {}.".format(payer_id), config, level="DEBUG")
775
-
776
- # Check for the endpoint key
777
- if 'endpoint' not in crosswalk['payer_id'][payer_id]:
778
- if skip_api_operations:
779
- # Set default endpoint and log
780
- crosswalk['payer_id'][payer_id]['endpoint'] = 'AVAILITY'
781
- MediLink_ConfigLoader.log("Set default endpoint for payer ID {} - can be adjusted via MediBot if needed".format(payer_id), config, level="INFO")
782
- else:
783
- crosswalk['payer_id'][payer_id]['endpoint'] = select_endpoint(config) # Use the helper function to set the endpoint
784
- MediLink_ConfigLoader.log("Initialized 'endpoint' for payer ID {}.".format(payer_id), config, level="DEBUG")
785
-
786
- # Initialize medisoft_id and medisoft_medicare_id as empty lists if they do not exist
787
- crosswalk['payer_id'][payer_id].setdefault('medisoft_id', [])
788
- crosswalk['payer_id'][payer_id].setdefault('medisoft_medicare_id', []) # does this work in 3.4.4?
789
- MediLink_ConfigLoader.log("Ensured 'medisoft_id' and 'medisoft_medicare_id' for payer ID {} are initialized.".format(payer_id), config, level="DEBUG")
790
-
791
- # Convert sets to sorted lists for JSON serialization
792
- for payer_id, details in crosswalk.get('payer_id', {}).items():
793
- if isinstance(details.get('medisoft_id'), set):
794
- crosswalk['payer_id'][payer_id]['medisoft_id'] = sorted(list(details['medisoft_id']))
795
- MediLink_ConfigLoader.log("Converted medisoft_id for payer ID {} to sorted list.".format(payer_id), config, level="DEBUG")
796
- if isinstance(details.get('medisoft_medicare_id'), set):
797
- crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = sorted(list(details['medisoft_medicare_id']))
798
- MediLink_ConfigLoader.log("Converted medisoft_medicare_id for payer ID {} to sorted list.".format(payer_id), config, level="DEBUG")
799
-
800
- # Write the crosswalk to the specified file
801
- with open(crosswalk_path, 'w') as file:
802
- json.dump(crosswalk, file, indent=4)
803
-
804
- MediLink_ConfigLoader.log(
805
- "Crosswalk saved successfully to {}.".format(crosswalk_path),
806
- config,
807
- level="INFO"
808
- )
809
- print("Crosswalk saved successfully to {}.".format(crosswalk_path))
810
- return True
811
- except KeyError as e:
812
- print("Key Error: A required key is missing in the crosswalk data - {}.".format(e))
813
- MediLink_ConfigLoader.log("Key Error while saving crosswalk: {}.".format(e), config, level="ERROR")
814
- return False
815
- except TypeError as e:
816
- print("Type Error: There was a type issue with the data being saved in the crosswalk - {}.".format(e))
817
- MediLink_ConfigLoader.log("Type Error while saving crosswalk: {}.".format(e), config, level="ERROR")
818
- return False
819
- except IOError as e:
820
- print("I/O Error: An error occurred while writing to the crosswalk file - {}.".format(e))
821
- MediLink_ConfigLoader.log("I/O Error while saving crosswalk: {}.".format(e), config, level="ERROR")
822
- return False
823
- except Exception as e:
824
- print("Unexpected crosswalk error: {}.".format(e))
825
- MediLink_ConfigLoader.log("Unexpected error while saving crosswalk: {}.".format(e), config, level="ERROR")
826
- return False
827
-
828
- def select_endpoint(config, current_endpoint=None):
829
- # BUG Check upstream for the config. One of these is not being passed correctly so we're having to do this check here.
830
- """
831
- Prompts the user to select an endpoint from the available options or returns the default endpoint.
832
- Validates the current endpoint against the available options.
833
-
834
- Args:
835
- config (dict): Configuration settings for logging. Can be either the full config or config['MediLink_Config'].
836
- current_endpoint (str, optional): The current endpoint to validate.
837
-
838
- Returns:
839
- str: The selected endpoint key.
840
-
841
- Raises:
842
- ValueError: If the config does not contain valid endpoint information.
843
- """
844
- # Determine the effective MediLink_Config
845
- if 'MediLink_Config' in config:
846
- medi_link_config = config['MediLink_Config']
847
- MediLink_ConfigLoader.log("Using 'MediLink_Config' from the provided configuration.", config, level="DEBUG")
848
- else:
849
- medi_link_config = config
850
- MediLink_ConfigLoader.log("Using the provided configuration directly as 'MediLink_Config'.", config, level="DEBUG")
851
-
852
- # Attempt to retrieve endpoint options
853
- try:
854
- endpoint_options = list(medi_link_config['endpoints'].keys())
855
- MediLink_ConfigLoader.log("Successfully retrieved endpoint options.", config, level="DEBUG")
856
- except KeyError:
857
- MediLink_ConfigLoader.log("Failed to retrieve endpoint options due to KeyError.", config, level="ERROR")
858
- raise ValueError("Invalid configuration: 'endpoints' not found in config.")
859
-
860
-
861
- # Ensure there are available endpoints
862
- if not endpoint_options:
863
- MediLink_ConfigLoader.log("No endpoints available in the configuration.", config, level="ERROR")
864
- raise ValueError("No endpoints available in the configuration.")
865
- else:
866
- MediLink_ConfigLoader.log("Available endpoints found in the configuration.", config, level="DEBUG")
867
-
868
- print("Available endpoints:")
869
- for idx, key in enumerate(endpoint_options):
870
- # Safely retrieve the endpoint name
871
- endpoint_name = medi_link_config['endpoints'].get(key, {}).get('name', key)
872
- print("{0}: {1}".format(idx + 1, endpoint_name))
873
-
874
- # Validate the current endpoint if provided
875
- if current_endpoint and current_endpoint not in endpoint_options:
876
- print("WARNING: The current endpoint '{}' is not valid.".format(current_endpoint))
877
- MediLink_ConfigLoader.log("Current endpoint '{}' is not valid. Prompting for selection.".format(current_endpoint), config, level="WARNING")
878
-
879
- user_choice = input("Select an endpoint by number (or press Enter to use the default): ").strip()
880
-
881
- if user_choice.isdigit() and 1 <= int(user_choice) <= len(endpoint_options):
882
- selected_endpoint = endpoint_options[int(user_choice) - 1] # Use the key instead of the name
883
- else:
884
- selected_endpoint = endpoint_options[0] # Default to the first key
885
- MediLink_ConfigLoader.log("User opted for default endpoint: " + selected_endpoint, config, level="INFO")
886
-
887
- return selected_endpoint
888
-
889
- def ensure_full_config_loaded(config=None, crosswalk=None):
890
- """
891
- Ensures that the full base configuration and crosswalk are loaded.
892
- If the base config is not valid or the crosswalk is None, reloads them.
893
-
894
- Args:
895
- config (dict, optional): The current configuration.
896
- crosswalk (dict, optional): The current crosswalk.
897
-
898
- Returns:
899
- tuple: The loaded base configuration and crosswalk.
900
- """
901
- MediLink_ConfigLoader.log("Ensuring full configuration and crosswalk are loaded.", level="DEBUG")
902
-
903
- # Reload configuration if necessary
904
- if config is None or 'MediLink_Config' not in config:
905
- MediLink_ConfigLoader.log("Base config is missing or invalid. Reloading configuration.", level="WARNING")
906
- config, crosswalk = MediLink_ConfigLoader.load_configuration()
907
- MediLink_ConfigLoader.log("Base configuration and crosswalk reloaded.", level="INFO")
908
- else:
909
- MediLink_ConfigLoader.log("Base config was correctly passed.", level="DEBUG")
910
420
 
911
- # Reload crosswalk if necessary
912
- if crosswalk is None:
913
- MediLink_ConfigLoader.log("Crosswalk is None. Reloading crosswalk.", level="WARNING")
914
- _, crosswalk = MediLink_ConfigLoader.load_configuration() # Reloading to get the crosswalk
915
- MediLink_ConfigLoader.log("Crosswalk reloaded.", level="INFO")
916
421
 
917
- return config, crosswalk