medicafe 0.250720.1__tar.gz → 0.250723.0__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 (60) hide show
  1. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot.bat +2 -0
  2. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot.py +75 -2
  3. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_Crosswalk_Library.py +26 -12
  4. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_Preprocessor.py +36 -21
  5. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_Preprocessor_lib.py +795 -742
  6. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_UI.py +43 -9
  7. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_837p_cob_library.py +18 -15
  8. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_837p_encoder.py +27 -3
  9. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_837p_encoder_library.py +48 -14
  10. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_API_v3.py +3 -0
  11. medicafe-0.250723.0/MediLink/MediLink_ClaimStatus.py +273 -0
  12. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_DataMgmt.py +10 -5
  13. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Deductible.py +120 -70
  14. {medicafe-0.250720.1 → medicafe-0.250723.0}/PKG-INFO +1 -1
  15. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/PKG-INFO +1 -1
  16. {medicafe-0.250720.1 → medicafe-0.250723.0}/setup.py +1 -1
  17. medicafe-0.250720.1/MediLink/MediLink_ClaimStatus.py +0 -160
  18. {medicafe-0.250720.1 → medicafe-0.250723.0}/LICENSE +0 -0
  19. {medicafe-0.250720.1 → medicafe-0.250723.0}/MANIFEST.in +0 -0
  20. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_Charges.py +0 -0
  21. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_Post.py +0 -0
  22. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_dataformat_library.py +0 -0
  23. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/MediBot_docx_decoder.py +0 -0
  24. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/PDF_to_CSV_Cleaner.py +0 -0
  25. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/__init__.py +0 -0
  26. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/update_json.py +0 -0
  27. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediBot/update_medicafe.py +0 -0
  28. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink.py +0 -0
  29. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_837p_utilities.py +0 -0
  30. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_API_Generator.py +0 -0
  31. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_API_v2.py +0 -0
  32. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_APIs.py +0 -0
  33. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Azure.py +0 -0
  34. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_ConfigLoader.py +0 -0
  35. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Decoder.py +0 -0
  36. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Deductible_Validator.py +0 -0
  37. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Down.py +0 -0
  38. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Gmail.py +0 -0
  39. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_GraphQL.py +0 -0
  40. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Mailer.py +0 -0
  41. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Parser.py +0 -0
  42. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Scan.py +0 -0
  43. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Scheduler.py +0 -0
  44. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_UI.py +0 -0
  45. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_Up.py +0 -0
  46. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/MediLink_batch.bat +0 -0
  47. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/Soumit_api.py +0 -0
  48. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/__init__.py +0 -0
  49. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/openssl.cnf +0 -0
  50. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/test.py +0 -0
  51. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/test_cob_library.py +0 -0
  52. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/test_validation.py +0 -0
  53. {medicafe-0.250720.1 → medicafe-0.250723.0}/MediLink/webapp.html +0 -0
  54. {medicafe-0.250720.1 → medicafe-0.250723.0}/README.md +0 -0
  55. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/SOURCES.txt +0 -0
  56. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/dependency_links.txt +0 -0
  57. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/not-zip-safe +0 -0
  58. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/requires.txt +0 -0
  59. {medicafe-0.250720.1 → medicafe-0.250723.0}/medicafe.egg-info/top_level.txt +0 -0
  60. {medicafe-0.250720.1 → medicafe-0.250723.0}/setup.cfg +0 -0
@@ -281,9 +281,11 @@ for %%f in ("%target_folder%\!latest_csv!") do set "latest_csv_name=%%~nxf"
281
281
 
282
282
  :: Compare the paths and prompt user if necessary
283
283
  if not "!current_csv_name!"=="!latest_csv_name!" (
284
+ echo.
284
285
  echo ALERT: Config file CSV path differs from the latest CSV. This can happen if a new CSV is downloaded.
285
286
  echo Current CSV: !current_csv_name!
286
287
  echo Latest CSV: !latest_csv_name!
288
+ echo.
287
289
  set /p update_choice="Do you want to update to the latest CSV? (Y/N): "
288
290
  if /i "!update_choice!"=="Y" (
289
291
  echo Updating config file with latest CSV...
@@ -40,8 +40,42 @@ 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
+ if stderr:
70
+ print("AHK Error: {}".format(stderr.decode('utf-8', errors='ignore')))
71
+ return # Success - no file cleanup needed
72
+
73
+ except Exception as e:
74
+ # If stdin method fails, fall back to traditional file method
75
+ print("AHK stdin execution failed, falling back to file method: {}".format(e))
76
+ # Continue to fallback implementation below
77
+
78
+ # Traditional file-based method (fallback or when optimization disabled)
45
79
  temp_script_name = None # Initialize variable to hold the name of the temporary script file
46
80
  try:
47
81
  # Create a temporary AHK script file
@@ -70,6 +104,8 @@ def run_ahk_script(script_content):
70
104
  last_processed_entry = None
71
105
  # Global variable to store temporarily parsed address components
72
106
  parsed_address_components = {}
107
+ # Global variable to store patient context for F11 menu (preserved across patients)
108
+ current_patient_context = None
73
109
 
74
110
  def process_field(medisoft_field, csv_row, parsed_address_components, reverse_mapping, csv_data, fixed_values):
75
111
  global last_processed_entry
@@ -107,8 +143,19 @@ def process_field(medisoft_field, csv_row, parsed_address_components, reverse_ma
107
143
  return handle_error(e, medisoft_field, last_processed_entry, csv_data)
108
144
 
109
145
  def handle_error(error, medisoft_field, last_processed_entry, csv_data):
146
+ global current_patient_context
110
147
  MediLink_ConfigLoader.log("Error in process_field: ", e)
111
148
  print("An error occurred while processing {0}: {1}".format(medisoft_field, error))
149
+
150
+ # Update patient context with current error information for F11 menu
151
+ if current_patient_context is None:
152
+ current_patient_context = {}
153
+ current_patient_context.update({
154
+ 'last_field': medisoft_field,
155
+ 'error_occurred': True,
156
+ 'error_message': str(error)
157
+ })
158
+
112
159
  # Assuming the interaction mode is 'error' in this case
113
160
  interaction_mode = 'error'
114
161
  response = user_interaction(csv_data, interaction_mode, error, reverse_mapping)
@@ -129,7 +176,7 @@ def iterate_fields(csv_row, field_mapping, parsed_address_components, reverse_ma
129
176
  return 0 # Default action to continue
130
177
 
131
178
  def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
132
- global last_processed_entry, parsed_address_components
179
+ global last_processed_entry, parsed_address_components, current_patient_context
133
180
  # last_processed_entry, parsed_address_components = None, {} // BUG should this just be this line rather than the global line above?
134
181
  error_message = '' # Initialize error_message once
135
182
  current_row_index = 0
@@ -137,6 +184,23 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
137
184
  while current_row_index < len(csv_data):
138
185
  row = csv_data[current_row_index]
139
186
 
187
+ # PERFORMANCE FIX: Clear accumulating memory while preserving F11 menu context
188
+ # Store patient context before clearing last_processed_entry for F11 "Retry last entry" functionality
189
+ if last_processed_entry is not None:
190
+ patient_name = row.get(reverse_mapping.get('Patient Name', ''), 'Unknown Patient')
191
+ surgery_date = row.get('Surgery Date', 'Unknown Date')
192
+ current_patient_context = {
193
+ 'patient_name': patient_name,
194
+ 'surgery_date': surgery_date,
195
+ 'last_field': last_processed_entry[0] if last_processed_entry else None,
196
+ 'last_value': last_processed_entry[1] if last_processed_entry else None,
197
+ 'row_index': current_row_index
198
+ }
199
+
200
+ # Clear memory-accumulating structures while preserving F11 context above
201
+ last_processed_entry = None
202
+ parsed_address_components = {}
203
+
140
204
  # Handle script pause at the start of each row (patient record).
141
205
  manage_script_pause(csv_data, error_message, reverse_mapping)
142
206
  error_message = '' # Clear error message for the next iteration
@@ -146,8 +210,9 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
146
210
 
147
211
  # I feel like this is overwriting what would have already been idenfitied in the mapping.
148
212
  # This probably needs to be initialized differently.
213
+ # Note: parsed_address_components is now explicitly cleared above for performance
149
214
  # parsed_address_components = {'City': '', 'State': '', 'Zip Code': ''}
150
- parsed_address_components = {}
215
+ # parsed_address_components = {} # Moved to top of loop for memory management
151
216
 
152
217
  # Process each field in the row
153
218
  action = iterate_fields(row, field_mapping, parsed_address_components, reverse_mapping, csv_data, fixed_values)
@@ -170,6 +235,14 @@ def data_entry_loop(csv_data, field_mapping, reverse_mapping, fixed_values):
170
235
  # Code to handle the end of a patient record
171
236
  # TODO One day this can just not pause...
172
237
  app_control.set_pause_status(True) # Pause at the end of processing each patient record
238
+
239
+ # PERFORMANCE FIX: Explicit cleanup at end of patient processing
240
+ # Clear global state to prevent accumulation over processing sessions
241
+ # Note: current_patient_context is preserved for F11 menu functionality
242
+ if current_row_index != len(csv_data) - 1: # Not the last patient
243
+ last_processed_entry = None
244
+ parsed_address_components.clear()
245
+
173
246
  current_row_index += 1 # Move to the next row by default
174
247
 
175
248
  def open_medisoft(shortcut_path):
@@ -481,13 +481,15 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
481
481
 
482
482
  crosswalk['payer_id'][payer_id] = {
483
483
  'endpoint': selected_endpoint,
484
- 'medisoft_id': set(), # Use set to store multiple IDs
485
- 'medisoft_medicare_id': set()
484
+ 'medisoft_id': [], # PERFORMANCE FIX: Use list instead of set to avoid conversions
485
+ 'medisoft_medicare_id': [] # PERFORMANCE FIX: Use list instead of set to avoid conversions
486
486
  }
487
487
  MediLink_ConfigLoader.log("Initialized payer ID {} in crosswalk with endpoint '{}'.".format(payer_id, selected_endpoint), config, level="DEBUG")
488
488
 
489
- # Add the insurance ID to the payer ID entry
490
- crosswalk['payer_id'][payer_id]['medisoft_id'].add(str(insurance_id)) # Ensure IDs are strings
489
+ # Add the insurance ID to the payer ID entry (PERFORMANCE FIX: Use list operations)
490
+ insurance_id_str = str(insurance_id) # Ensure ID is string
491
+ if insurance_id_str not in crosswalk['payer_id'][payer_id]['medisoft_id']:
492
+ crosswalk['payer_id'][payer_id]['medisoft_id'].append(insurance_id_str) # Avoid duplicates
491
493
  MediLink_ConfigLoader.log(
492
494
  "Added new insurance ID {} to payer ID {}.".format(insurance_id, payer_id),
493
495
  config,
@@ -517,14 +519,26 @@ def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstr
517
519
  fetch_and_store_payer_name(client, payer_id, crosswalk, config)
518
520
  MediLink_ConfigLoader.log("Successfully fetched and stored payer name for unknown payer_id: {}".format(payer_id), config, level="INFO")
519
521
 
520
- # Ensure multiple medisoft_id values are preserved by converting sets to sorted lists
522
+ # PERFORMANCE FIX: Optimized list management - avoid redundant set/list conversions
523
+ # Ensure multiple medisoft_id values are preserved and deduplicated efficiently
521
524
  for payer_id, details in crosswalk.get('payer_id', {}).items():
522
- if isinstance(details.get('medisoft_id'), set):
523
- crosswalk['payer_id'][payer_id]['medisoft_id'] = sorted(list(details['medisoft_id']))
524
- MediLink_ConfigLoader.log("Converted medisoft_id for payer ID {} to sorted list.".format(payer_id), config, level="DEBUG")
525
- if isinstance(details.get('medisoft_medicare_id'), set):
526
- crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = sorted(list(details['medisoft_medicare_id']))
527
- MediLink_ConfigLoader.log("Converted medisoft_medicare_id for payer ID {} to sorted list.".format(payer_id), config, level="DEBUG")
525
+ # Handle medisoft_id - convert sets to lists or deduplicate existing lists
526
+ medisoft_id = details.get('medisoft_id', [])
527
+ if isinstance(medisoft_id, set):
528
+ crosswalk['payer_id'][payer_id]['medisoft_id'] = sorted(list(medisoft_id))
529
+ MediLink_ConfigLoader.log("Converted medisoft_id set to sorted list for payer ID {}.".format(payer_id), config, level="DEBUG")
530
+ elif isinstance(medisoft_id, list) and medisoft_id:
531
+ # Remove duplicates using dict.fromkeys() - preserves order, O(n) performance
532
+ crosswalk['payer_id'][payer_id]['medisoft_id'] = list(dict.fromkeys(medisoft_id))
533
+
534
+ # Handle medisoft_medicare_id - convert sets to lists or deduplicate existing lists
535
+ medicare_id = details.get('medisoft_medicare_id', [])
536
+ if isinstance(medicare_id, set):
537
+ crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = sorted(list(medicare_id))
538
+ MediLink_ConfigLoader.log("Converted medisoft_medicare_id set to sorted list for payer ID {}.".format(payer_id), config, level="DEBUG")
539
+ elif isinstance(medicare_id, list) and medicare_id:
540
+ # Remove duplicates using dict.fromkeys() - preserves order, O(n) performance
541
+ crosswalk['payer_id'][payer_id]['medisoft_medicare_id'] = list(dict.fromkeys(medicare_id))
528
542
 
529
543
  MediLink_ConfigLoader.log("Crosswalk update process completed. Processed {} payer IDs.".format(len(patient_id_to_payer_id)), config, level="INFO")
530
544
  return save_crosswalk(client, config, crosswalk)
@@ -635,7 +649,7 @@ def update_crosswalk_with_new_payer_id(client, insurance_name, payer_id, config,
635
649
  # Ensure the 'payer_id' key exists in the crosswalk
636
650
  crosswalk['payer_id'][payer_id] = {
637
651
  'endpoint': selected_endpoint,
638
- 'medisoft_id': set(),
652
+ 'medisoft_id': [], # PERFORMANCE FIX: Use list instead of set to avoid conversions
639
653
  'medisoft_medicare_id': []
640
654
  }
641
655
  MediLink_ConfigLoader.log("Initialized payer ID {} in crosswalk with endpoint '{}'.".format(payer_id, selected_endpoint), config, level="DEBUG")
@@ -25,7 +25,8 @@ except ImportError as e:
25
25
 
26
26
  # Load configuration
27
27
  # Should this also take args? Path for ./MediLink needed to be added for this to resolve
28
- config, crosswalk = MediLink_ConfigLoader.load_configuration()
28
+ # Use cached configuration to avoid repeated I/O operations
29
+ config, crosswalk = MediBot_Preprocessor_lib.get_cached_configuration()
29
30
 
30
31
  # CSV Preprocessor built for Carol
31
32
  def preprocess_csv_data(csv_data, crosswalk):
@@ -57,9 +58,7 @@ def preprocess_csv_data(csv_data, crosswalk):
57
58
  # and which haven't been yet. So, if the patient 'exists' in the system, the next quetion is about claims/billing status.
58
59
  # Eventually, we really want to get out of Medisoft...
59
60
 
60
- # Convert 'Surgery Date' back to string format if needed for further processing.
61
- # Combine 'Patient First', 'Patient Middle', and 'Patient Last' into a single 'Patient Name' field.
62
- # Combine 'Patient Address1' and 'Patient Address2' into a single 'Patient Street' field.
61
+ # Batch field operations: Convert dates, combine names/addresses, and apply replacements
63
62
  MediLink_ConfigLoader.log("CSV Pre-processor: Constructing Patient Name and Address for Medisoft...", level="INFO")
64
63
  MediBot_Preprocessor_lib.combine_fields(csv_data)
65
64
 
@@ -97,7 +96,8 @@ def preprocess_csv_data(csv_data, crosswalk):
97
96
 
98
97
  def check_existing_patients(selected_patient_ids, MAPAT_MED_PATH):
99
98
  existing_patients = []
100
- patients_to_process = list(selected_patient_ids) # Clone the selected patient IDs list
99
+ # Convert to set for O(1) lookup performance
100
+ selected_patient_ids_set = set(selected_patient_ids)
101
101
 
102
102
  try:
103
103
  with open(MAPAT_MED_PATH, 'r') as file:
@@ -107,17 +107,17 @@ def check_existing_patients(selected_patient_ids, MAPAT_MED_PATH):
107
107
  patient_id = line[194:202].strip() # Extract Patient ID (Columns 195-202)
108
108
  patient_name = line[9:39].strip() # Extract Patient Name (Columns 10-39)
109
109
 
110
- if patient_id in selected_patient_ids:
110
+ if patient_id in selected_patient_ids_set:
111
111
  existing_patients.append((patient_id, patient_name))
112
- # Remove all occurrences of this patient_id from patients_to_process as a filter rather than .remove because
113
- # then it only makes one pass and removes the first instance.
112
+ # Remove from set for O(1) operation
113
+ selected_patient_ids_set.discard(patient_id)
114
114
  except FileNotFoundError:
115
115
  # Handle the case where MAPAT_MED_PATH is not found
116
116
  print("MAPAT.med was not found at location indicated in config file.")
117
117
  print("Skipping existing patient check and continuing...")
118
118
 
119
- # Filter out all instances of existing patient IDs
120
- patients_to_process = [id for id in patients_to_process if id not in [patient[0] for patient in existing_patients]]
119
+ # Convert remaining set back to list for return
120
+ patients_to_process = list(selected_patient_ids_set)
121
121
 
122
122
  return existing_patients, patients_to_process
123
123
 
@@ -129,14 +129,25 @@ def intake_scan(csv_headers, field_mapping):
129
129
  MediLink_ConfigLoader.log("Intake Scan - Field Mapping: {}".format(field_mapping), level="DEBUG")
130
130
  MediLink_ConfigLoader.log("Intake Scan - CSV Headers: {}".format(csv_headers), level="DEBUG")
131
131
 
132
+ # Pre-compile regex patterns for better performance
133
+ compiled_patterns = {}
134
+ for medisoft_field, patterns in field_mapping.items():
135
+ compiled_patterns[medisoft_field] = [re.compile(pattern, re.IGNORECASE) for pattern in patterns]
136
+
137
+ # Pre-compile the alphanumeric regex for policy number validation
138
+ alphanumeric_pattern = re.compile("^[a-zA-Z0-9]*$")
139
+
132
140
  # Iterate over the Medisoft fields defined in field_mapping
133
141
  for medisoft_field in field_mapping.keys():
134
- for pattern in field_mapping[medisoft_field]:
135
- matched_headers = [header for header in csv_headers if re.search(pattern, header, re.IGNORECASE)]
136
- if matched_headers:
137
- # Assuming the first matched header is the desired one
138
- identified_fields[matched_headers[0]] = medisoft_field
139
- # MediLink_ConfigLoader.log("Found Header: {}".format(identified_fields[matched_headers[0]]))
142
+ matched = False
143
+ for pattern in compiled_patterns[medisoft_field]:
144
+ # Use early termination - find first match and break
145
+ for header in csv_headers:
146
+ if pattern.search(header):
147
+ identified_fields[header] = medisoft_field
148
+ matched = True
149
+ break
150
+ if matched:
140
151
  break
141
152
  else:
142
153
  # Check if the missing field is a required field before appending the warning
@@ -160,7 +171,7 @@ def intake_scan(csv_headers, field_mapping):
160
171
  if 'Insurance Policy Number' in field:
161
172
  policy_number = identified_fields.get(header)
162
173
  MediLink_ConfigLoader.log("Checking Insurance Policy Number '{}' for alphanumeric characters.".format(policy_number), level="DEBUG")
163
- if not bool(re.match("^[a-zA-Z0-9]*$", policy_number)):
174
+ if not alphanumeric_pattern.match(policy_number):
164
175
  missing_fields_warnings.append("WARNING: Insurance Policy Number '{}' contains invalid characters.".format(policy_number))
165
176
  MediLink_ConfigLoader.log("Insurance Policy Number '{}' contains invalid characters.".format(policy_number), level="WARNING")
166
177
  # Additional checks can be added as needed for other fields
@@ -187,15 +198,19 @@ def main():
187
198
 
188
199
  args = parser.parse_args()
189
200
 
190
- config, crosswalk = MediLink_ConfigLoader.load_configuration()
191
-
192
- client = APIClient()
193
-
194
201
  # If no arguments provided, print usage instructions
195
202
  if not any(vars(args).values()):
196
203
  parser.print_help()
197
204
  return
198
205
 
206
+ # Load configuration only when needed
207
+ if args.update_crosswalk or args.init_crosswalk or args.load_csv or args.preprocess_csv or args.open_csv:
208
+ config, crosswalk = MediBot_Preprocessor_lib.get_cached_configuration()
209
+
210
+ # Initialize API client only when needed
211
+ if args.update_crosswalk or args.init_crosswalk:
212
+ client = APIClient()
213
+
199
214
  if args.update_crosswalk:
200
215
  print("Updating the crosswalk...")
201
216
  MediBot_Crosswalk_Library.crosswalk_update(client, config, crosswalk)