medicafe 0.250722.0__tar.gz → 0.250723.1__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.
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot.py +82 -2
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_Crosswalk_Library.py +53 -21
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_Preprocessor_lib.py +795 -779
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_UI.py +43 -9
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_API_v3.py +3 -0
- medicafe-0.250723.1/MediLink/MediLink_ClaimStatus.py +273 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Deductible.py +120 -70
- {medicafe-0.250722.0 → medicafe-0.250723.1}/PKG-INFO +1 -1
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/PKG-INFO +1 -1
- {medicafe-0.250722.0 → medicafe-0.250723.1}/setup.py +1 -1
- medicafe-0.250722.0/MediLink/MediLink_ClaimStatus.py +0 -160
- {medicafe-0.250722.0 → medicafe-0.250723.1}/LICENSE +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MANIFEST.in +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot.bat +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_dataformat_library.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/PDF_to_CSV_Cleaner.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/__init__.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/update_json.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediBot/update_medicafe.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_837p_encoder.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_837p_encoder_library.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_API_v2.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_APIs.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_ConfigLoader.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_DataMgmt.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_GraphQL.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_Up.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/MediLink_batch.bat +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/__init__.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/openssl.cnf +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/test.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/test_cob_library.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/test_validation.py +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/MediLink/webapp.html +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/README.md +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.250722.0 → medicafe-0.250723.1}/setup.cfg +0 -0
|
@@ -40,8 +40,45 @@ def identify_field(header, field_mapping):
|
|
|
40
40
|
# Add this print to a function that is calling identify_field
|
|
41
41
|
#print("Warning: No matching field found for CSV header '{}'".format(header))
|
|
42
42
|
|
|
43
|
+
# Global flag to control AHK execution method - set to True to use optimized stdin method
|
|
44
|
+
USE_AHK_STDIN_OPTIMIZATION = True
|
|
45
|
+
|
|
43
46
|
# Function to execute an AutoHotkey script
|
|
44
47
|
def run_ahk_script(script_content):
|
|
48
|
+
"""
|
|
49
|
+
Execute an AutoHotkey script using either optimized stdin method or traditional file method.
|
|
50
|
+
Automatically falls back to file method if stdin method fails.
|
|
51
|
+
"""
|
|
52
|
+
if USE_AHK_STDIN_OPTIMIZATION:
|
|
53
|
+
try:
|
|
54
|
+
# Optimized method: Execute AHK script via stdin pipe - eliminates temporary file creation
|
|
55
|
+
# Compatible with Windows XP and AutoHotkey v1.0.48+
|
|
56
|
+
process = subprocess.Popen(
|
|
57
|
+
[AHK_EXECUTABLE, '/f', '*'], # '/f *' tells AHK to read from stdin
|
|
58
|
+
stdin=subprocess.PIPE,
|
|
59
|
+
stdout=subprocess.PIPE,
|
|
60
|
+
stderr=subprocess.PIPE,
|
|
61
|
+
shell=False
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
# Send script content via stdin
|
|
65
|
+
stdout, stderr = process.communicate(input=script_content.encode('utf-8'))
|
|
66
|
+
|
|
67
|
+
if process.returncode != 0:
|
|
68
|
+
print("AHK script failed with exit status: {}".format(process.returncode)) # Log the exit status of the failed script
|
|
69
|
+
MediLink_ConfigLoader.log("AHK script failed with exit status: {}".format(process.returncode), level="ERROR")
|
|
70
|
+
if stderr:
|
|
71
|
+
print("AHK Error: {}".format(stderr.decode('utf-8', errors='ignore')))
|
|
72
|
+
MediLink_ConfigLoader.log("AHK Error: {}".format(stderr.decode('utf-8', errors='ignore')), level="ERROR")
|
|
73
|
+
return # Success - no file cleanup needed
|
|
74
|
+
|
|
75
|
+
except Exception as e:
|
|
76
|
+
# If stdin method fails, fall back to traditional file method
|
|
77
|
+
print("AHK stdin execution failed, falling back to file method: {}".format(e))
|
|
78
|
+
MediLink_ConfigLoader.log("AHK stdin execution failed, falling back to file method: {}".format(e), level="ERROR")
|
|
79
|
+
# Continue to fallback implementation below
|
|
80
|
+
|
|
81
|
+
# Traditional file-based method (fallback or when optimization disabled)
|
|
45
82
|
temp_script_name = None # Initialize variable to hold the name of the temporary script file
|
|
46
83
|
try:
|
|
47
84
|
# Create a temporary AHK script file
|
|
@@ -53,9 +90,12 @@ def run_ahk_script(script_content):
|
|
|
53
90
|
subprocess.check_call([AHK_EXECUTABLE, temp_script_name]) # Execute the AHK script using the AutoHotkey executable
|
|
54
91
|
except subprocess.CalledProcessError as e:
|
|
55
92
|
print("AHK script failed with exit status: {}".format(e.returncode)) # Log the exit status of the failed script
|
|
93
|
+
MediLink_ConfigLoader.log("AHK script failed with exit status: {}".format(e.returncode), level="ERROR")
|
|
56
94
|
print("Output from AHK script: {}".format(e.output)) # Log the output from the failed script
|
|
95
|
+
MediLink_ConfigLoader.log("Output from AHK script: {}".format(e.output), level="ERROR")
|
|
57
96
|
except Exception as e:
|
|
58
97
|
print("An unexpected error occurred while running the AHK script: {}".format(e)) # Log any unexpected errors
|
|
98
|
+
MediLink_ConfigLoader.log("An unexpected error occurred while running the AHK script: {}".format(e), level="ERROR")
|
|
59
99
|
traceback.print_exc() # Print the full traceback for debugging purposes
|
|
60
100
|
finally:
|
|
61
101
|
# Delete the temporary script file
|
|
@@ -64,12 +104,15 @@ def run_ahk_script(script_content):
|
|
|
64
104
|
os.unlink(temp_script_name) # Attempt to delete the temporary script file
|
|
65
105
|
except OSError as e:
|
|
66
106
|
print("Error deleting temporary script file: {}".format(e)) # Log any errors encountered while deleting the file
|
|
107
|
+
MediLink_ConfigLoader.log("Error deleting temporary script file: {}".format(e), level="ERROR")
|
|
67
108
|
# Future Improvement: Implement a cleanup mechanism to handle orphaned temporary files
|
|
68
109
|
|
|
69
110
|
# Global variable to store the last processed entry
|
|
70
111
|
last_processed_entry = None
|
|
71
112
|
# Global variable to store temporarily parsed address components
|
|
72
113
|
parsed_address_components = {}
|
|
114
|
+
# Global variable to store patient context for F11 menu (preserved across patients)
|
|
115
|
+
current_patient_context = None
|
|
73
116
|
|
|
74
117
|
def process_field(medisoft_field, csv_row, parsed_address_components, reverse_mapping, csv_data, fixed_values):
|
|
75
118
|
global last_processed_entry
|
|
@@ -107,8 +150,19 @@ def process_field(medisoft_field, csv_row, parsed_address_components, reverse_ma
|
|
|
107
150
|
return handle_error(e, medisoft_field, last_processed_entry, csv_data)
|
|
108
151
|
|
|
109
152
|
def handle_error(error, medisoft_field, last_processed_entry, csv_data):
|
|
153
|
+
global current_patient_context
|
|
110
154
|
MediLink_ConfigLoader.log("Error in process_field: ", e)
|
|
111
155
|
print("An error occurred while processing {0}: {1}".format(medisoft_field, error))
|
|
156
|
+
|
|
157
|
+
# Update patient context with current error information for F11 menu
|
|
158
|
+
if current_patient_context is None:
|
|
159
|
+
current_patient_context = {}
|
|
160
|
+
current_patient_context.update({
|
|
161
|
+
'last_field': medisoft_field,
|
|
162
|
+
'error_occurred': True,
|
|
163
|
+
'error_message': str(error)
|
|
164
|
+
})
|
|
165
|
+
|
|
112
166
|
# Assuming the interaction mode is 'error' in this case
|
|
113
167
|
interaction_mode = 'error'
|
|
114
168
|
response = user_interaction(csv_data, interaction_mode, error, reverse_mapping)
|
|
@@ -129,7 +183,7 @@ def iterate_fields(csv_row, field_mapping, parsed_address_components, reverse_ma
|
|
|
129
183
|
return 0 # Default action to continue
|
|
130
184
|
|
|
131
185
|
def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
|
|
132
|
-
global last_processed_entry, parsed_address_components
|
|
186
|
+
global last_processed_entry, parsed_address_components, current_patient_context
|
|
133
187
|
# last_processed_entry, parsed_address_components = None, {} // BUG should this just be this line rather than the global line above?
|
|
134
188
|
error_message = '' # Initialize error_message once
|
|
135
189
|
current_row_index = 0
|
|
@@ -137,6 +191,23 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
|
|
|
137
191
|
while current_row_index < len(csv_data):
|
|
138
192
|
row = csv_data[current_row_index]
|
|
139
193
|
|
|
194
|
+
# PERFORMANCE FIX: Clear accumulating memory while preserving F11 menu context
|
|
195
|
+
# Store patient context before clearing last_processed_entry for F11 "Retry last entry" functionality
|
|
196
|
+
if last_processed_entry is not None:
|
|
197
|
+
patient_name = row.get(reverse_mapping.get('Patient Name', ''), 'Unknown Patient')
|
|
198
|
+
surgery_date = row.get('Surgery Date', 'Unknown Date')
|
|
199
|
+
current_patient_context = {
|
|
200
|
+
'patient_name': patient_name,
|
|
201
|
+
'surgery_date': surgery_date,
|
|
202
|
+
'last_field': last_processed_entry[0] if last_processed_entry else None,
|
|
203
|
+
'last_value': last_processed_entry[1] if last_processed_entry else None,
|
|
204
|
+
'row_index': current_row_index
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
# Clear memory-accumulating structures while preserving F11 context above
|
|
208
|
+
last_processed_entry = None
|
|
209
|
+
parsed_address_components = {}
|
|
210
|
+
|
|
140
211
|
# Handle script pause at the start of each row (patient record).
|
|
141
212
|
manage_script_pause(csv_data, error_message, reverse_mapping)
|
|
142
213
|
error_message = '' # Clear error message for the next iteration
|
|
@@ -146,8 +217,9 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
|
|
|
146
217
|
|
|
147
218
|
# I feel like this is overwriting what would have already been idenfitied in the mapping.
|
|
148
219
|
# This probably needs to be initialized differently.
|
|
220
|
+
# Note: parsed_address_components is now explicitly cleared above for performance
|
|
149
221
|
# parsed_address_components = {'City': '', 'State': '', 'Zip Code': ''}
|
|
150
|
-
parsed_address_components = {}
|
|
222
|
+
# parsed_address_components = {} # Moved to top of loop for memory management
|
|
151
223
|
|
|
152
224
|
# Process each field in the row
|
|
153
225
|
action = iterate_fields(row, field_mapping, parsed_address_components, reverse_mapping, csv_data, fixed_values)
|
|
@@ -170,6 +242,14 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
|
|
|
170
242
|
# Code to handle the end of a patient record
|
|
171
243
|
# TODO One day this can just not pause...
|
|
172
244
|
app_control.set_pause_status(True) # Pause at the end of processing each patient record
|
|
245
|
+
|
|
246
|
+
# PERFORMANCE FIX: Explicit cleanup at end of patient processing
|
|
247
|
+
# Clear global state to prevent accumulation over processing sessions
|
|
248
|
+
# Note: current_patient_context is preserved for F11 menu functionality
|
|
249
|
+
if current_row_index != len(csv_data) - 1: # Not the last patient
|
|
250
|
+
last_processed_entry = None
|
|
251
|
+
parsed_address_components.clear()
|
|
252
|
+
|
|
173
253
|
current_row_index += 1 # Move to the next row by default
|
|
174
254
|
|
|
175
255
|
def open_medisoft(shortcut_path):
|
|
@@ -279,19 +279,22 @@ def check_crosswalk_health(crosswalk):
|
|
|
279
279
|
crosswalk (dict): The crosswalk dictionary to check.
|
|
280
280
|
|
|
281
281
|
Returns:
|
|
282
|
-
tuple: (is_healthy, missing_names_count, missing_medisoft_ids_count)
|
|
282
|
+
tuple: (is_healthy, missing_names_count, missing_medisoft_ids_count, missing_names_list, missing_medisoft_ids_list)
|
|
283
283
|
"""
|
|
284
284
|
if 'payer_id' not in crosswalk or not crosswalk['payer_id']:
|
|
285
|
-
return False, 0, 0
|
|
285
|
+
return False, 0, 0, [], []
|
|
286
286
|
|
|
287
287
|
missing_names = 0
|
|
288
288
|
missing_medisoft_ids = 0
|
|
289
|
+
missing_names_list = []
|
|
290
|
+
missing_medisoft_ids_list = []
|
|
289
291
|
|
|
290
292
|
for payer_id, details in crosswalk['payer_id'].items():
|
|
291
293
|
# Check if name is missing or "Unknown"
|
|
292
294
|
name = details.get('name', '')
|
|
293
295
|
if not name or name == 'Unknown':
|
|
294
296
|
missing_names += 1
|
|
297
|
+
missing_names_list.append(payer_id)
|
|
295
298
|
|
|
296
299
|
# Check if at least one medisoft ID exists in either field
|
|
297
300
|
medisoft_id = details.get('medisoft_id', [])
|
|
@@ -306,10 +309,11 @@ def check_crosswalk_health(crosswalk):
|
|
|
306
309
|
# If both are empty, count as missing; if either has at least one, it's healthy
|
|
307
310
|
if not medisoft_id and not medisoft_medicare_id:
|
|
308
311
|
missing_medisoft_ids += 1
|
|
312
|
+
missing_medisoft_ids_list.append(payer_id)
|
|
309
313
|
|
|
310
314
|
# Consider healthy if no missing names and no missing medisoft IDs
|
|
311
315
|
is_healthy = (missing_names == 0 and missing_medisoft_ids == 0)
|
|
312
|
-
return is_healthy, missing_names, missing_medisoft_ids
|
|
316
|
+
return is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list
|
|
313
317
|
|
|
314
318
|
def prompt_user_for_api_calls(crosswalk, config):
|
|
315
319
|
"""
|
|
@@ -324,7 +328,7 @@ def prompt_user_for_api_calls(crosswalk, config):
|
|
|
324
328
|
bool: True if should proceed with API calls, False if should skip
|
|
325
329
|
"""
|
|
326
330
|
|
|
327
|
-
is_healthy, missing_names, missing_medisoft_ids = check_crosswalk_health(crosswalk)
|
|
331
|
+
is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list = check_crosswalk_health(crosswalk)
|
|
328
332
|
total_payers = len(crosswalk.get('payer_id', {}))
|
|
329
333
|
|
|
330
334
|
if is_healthy:
|
|
@@ -362,13 +366,27 @@ def prompt_user_for_api_calls(crosswalk, config):
|
|
|
362
366
|
else:
|
|
363
367
|
print("\nCrosswalk needs attention:")
|
|
364
368
|
print(" - {} payers found".format(total_payers))
|
|
369
|
+
|
|
370
|
+
# Show detailed information about missing names
|
|
365
371
|
if missing_names > 0:
|
|
366
|
-
print(" - {} payers missing names".format(missing_names))
|
|
372
|
+
print(" - {} payers missing names: {}".format(missing_names, ", ".join(missing_names_list)))
|
|
373
|
+
|
|
374
|
+
# Show detailed information about missing medisoft IDs
|
|
367
375
|
if missing_medisoft_ids > 0:
|
|
368
|
-
print(" - {} payers missing medisoft IDs".format(missing_medisoft_ids))
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
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
|
|
372
390
|
|
|
373
391
|
def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstream of this is only MediBot_Preprocessor.py and MediBot.py
|
|
374
392
|
"""
|
|
@@ -481,13 +499,15 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
|
|
|
481
499
|
|
|
482
500
|
crosswalk['payer_id'][payer_id] = {
|
|
483
501
|
'endpoint': selected_endpoint,
|
|
484
|
-
'medisoft_id':
|
|
485
|
-
'medisoft_medicare_id': set
|
|
502
|
+
'medisoft_id': [], # PERFORMANCE FIX: Use list instead of set to avoid conversions
|
|
503
|
+
'medisoft_medicare_id': [] # PERFORMANCE FIX: Use list instead of set to avoid conversions
|
|
486
504
|
}
|
|
487
505
|
MediLink_ConfigLoader.log("Initialized payer ID {} in crosswalk with endpoint '{}'.".format(payer_id, selected_endpoint), config, level="DEBUG")
|
|
488
506
|
|
|
489
|
-
# Add the insurance ID to the payer ID entry
|
|
490
|
-
|
|
507
|
+
# Add the insurance ID to the payer ID entry (PERFORMANCE FIX: Use list operations)
|
|
508
|
+
insurance_id_str = str(insurance_id) # Ensure ID is string
|
|
509
|
+
if insurance_id_str not in crosswalk['payer_id'][payer_id]['medisoft_id']:
|
|
510
|
+
crosswalk['payer_id'][payer_id]['medisoft_id'].append(insurance_id_str) # Avoid duplicates
|
|
491
511
|
MediLink_ConfigLoader.log(
|
|
492
512
|
"Added new insurance ID {} to payer ID {}.".format(insurance_id, payer_id),
|
|
493
513
|
config,
|
|
@@ -517,14 +537,26 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
|
|
|
517
537
|
fetch_and_store_payer_name(client, payer_id, crosswalk, config)
|
|
518
538
|
MediLink_ConfigLoader.log("Successfully fetched and stored payer name for unknown payer_id: {}".format(payer_id), config, level="INFO")
|
|
519
539
|
|
|
520
|
-
#
|
|
540
|
+
# PERFORMANCE FIX: Optimized list management - avoid redundant set/list conversions
|
|
541
|
+
# Ensure multiple medisoft_id values are preserved and deduplicated efficiently
|
|
521
542
|
for payer_id, details in crosswalk.get('payer_id', {}).items():
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
543
|
+
# Handle medisoft_id - convert sets to lists or deduplicate existing lists
|
|
544
|
+
medisoft_id = details.get('medisoft_id', [])
|
|
545
|
+
if isinstance(medisoft_id, set):
|
|
546
|
+
crosswalk['payer_id'][payer_id]['medisoft_id'] = sorted(list(medisoft_id))
|
|
547
|
+
MediLink_ConfigLoader.log("Converted medisoft_id set to sorted list for payer ID {}.".format(payer_id), config, level="DEBUG")
|
|
548
|
+
elif isinstance(medisoft_id, list) and medisoft_id:
|
|
549
|
+
# Remove duplicates using dict.fromkeys() - preserves order, O(n) performance
|
|
550
|
+
crosswalk['payer_id'][payer_id]['medisoft_id'] = list(dict.fromkeys(medisoft_id))
|
|
551
|
+
|
|
552
|
+
# Handle medisoft_medicare_id - convert sets to lists or deduplicate existing lists
|
|
553
|
+
medicare_id = details.get('medisoft_medicare_id', [])
|
|
554
|
+
if isinstance(medicare_id, set):
|
|
555
|
+
crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = sorted(list(medicare_id))
|
|
556
|
+
MediLink_ConfigLoader.log("Converted medisoft_medicare_id set to sorted list for payer ID {}.".format(payer_id), config, level="DEBUG")
|
|
557
|
+
elif isinstance(medicare_id, list) and medicare_id:
|
|
558
|
+
# Remove duplicates using dict.fromkeys() - preserves order, O(n) performance
|
|
559
|
+
crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = list(dict.fromkeys(medicare_id))
|
|
528
560
|
|
|
529
561
|
MediLink_ConfigLoader.log("Crosswalk update process completed. Processed {} payer IDs.".format(len(patient_id_to_payer_id)), config, level="INFO")
|
|
530
562
|
return save_crosswalk(client, config, crosswalk)
|
|
@@ -635,7 +667,7 @@ def update_crosswalk_with_new_payer_id(client, insurance_name, payer_id, config,
|
|
|
635
667
|
# Ensure the 'payer_id' key exists in the crosswalk
|
|
636
668
|
crosswalk['payer_id'][payer_id] = {
|
|
637
669
|
'endpoint': selected_endpoint,
|
|
638
|
-
'medisoft_id': set
|
|
670
|
+
'medisoft_id': [], # PERFORMANCE FIX: Use list instead of set to avoid conversions
|
|
639
671
|
'medisoft_medicare_id': []
|
|
640
672
|
}
|
|
641
673
|
MediLink_ConfigLoader.log("Initialized payer ID {} in crosswalk with endpoint '{}'.".format(payer_id, selected_endpoint), config, level="DEBUG")
|