medicafe 0.250711.1__py3-none-any.whl → 0.250720.1__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.

@@ -52,6 +52,11 @@ try:
52
52
  except ImportError:
53
53
  import MediLink_ConfigLoader
54
54
 
55
+ try:
56
+ from MediLink import MediLink_Deductible_Validator
57
+ except ImportError:
58
+ import MediLink_Deductible_Validator
59
+
55
60
  project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
56
61
  if project_dir not in sys.path:
57
62
  sys.path.append(project_dir)
@@ -122,26 +127,39 @@ patients = [
122
127
  # Function to handle manual patient deductible lookup
123
128
  def manual_deductible_lookup():
124
129
  print("\n--- Manual Patient Deductible Lookup ---")
130
+ print("Available Payer IDs: {}".format(", ".join(payer_ids)))
131
+ print("Enter 'quit' at any time to return to main menu.\n")
132
+
125
133
  while True:
126
- member_id = input("Enter the Member ID of the subscriber (or press Enter to skip): ").strip()
127
- if not member_id:
128
- print("No Member ID entered. Skipping manual lookup.\n")
134
+ member_id = input("Enter the Member ID of the subscriber (or 'quit' to exit): ").strip()
135
+ if member_id.lower() == 'quit':
136
+ print("Returning to main menu.\n")
129
137
  break
138
+ if not member_id:
139
+ print("No Member ID entered. Please try again.\n")
140
+ continue
130
141
 
131
142
  dob_input = input("Enter the Date of Birth (YYYY-MM-DD): ").strip()
143
+ if dob_input.lower() == 'quit':
144
+ print("Returning to main menu.\n")
145
+ break
146
+
132
147
  formatted_dob = validate_and_format_date(dob_input)
133
148
  if not formatted_dob:
134
149
  print("Invalid DOB format. Please enter in YYYY-MM-DD format.\n")
135
150
  continue
136
151
 
137
- # Create a temporary list for single patient
138
- single_patient = [(formatted_dob, member_id)]
139
- print("Processing manual lookup for Member ID: {}, DOB: {}".format(member_id, formatted_dob))
152
+ print("\nProcessing manual lookup for Member ID: {}, DOB: {}".format(member_id, formatted_dob))
153
+ print("Checking {} payer IDs...".format(len(payer_ids)))
140
154
 
141
155
  # Fetch eligibility data
142
- for payer_id in payer_ids:
143
- eligibility_data = get_eligibility_info(client, payer_id, provider_last_name, formatted_dob, member_id, npi)
156
+ found_data = False
157
+ for i, payer_id in enumerate(payer_ids, 1):
158
+ print("Checking Payer ID {} ({}/{}): {}".format(payer_id, i, len(payer_ids), payer_id))
159
+
160
+ eligibility_data = get_eligibility_info(client, payer_id, provider_last_name, formatted_dob, member_id, npi, run_validation=True)
144
161
  if eligibility_data:
162
+ found_data = True
145
163
  # Generate unique output file for manual request
146
164
  output_file_name = "eligibility_report_manual_{}_{}.txt".format(member_id, formatted_dob)
147
165
  output_file_path = os.path.join(os.getenv('TEMP'), output_file_name)
@@ -153,26 +171,27 @@ def manual_deductible_lookup():
153
171
  print(table_header)
154
172
  print("-" * len(table_header))
155
173
  display_eligibility_info(eligibility_data, formatted_dob, member_id, output_file)
156
- # Open the generated file in Notepad
157
- os.system('notepad.exe "{}"'.format(output_file_path))
174
+
175
+ # Ask if user wants to open the report
176
+ open_report = input("\nEligibility data found! Open the report? (Y/N): ").strip().lower()
177
+ if open_report in ['y', 'yes']:
178
+ os.system('notepad.exe "{}"'.format(output_file_path))
158
179
  print("Manual eligibility report generated: {}\n".format(output_file_path))
159
180
  break # Assuming one payer ID per manual lookup
160
181
  else:
161
182
  print("No eligibility data found for Payer ID: {}".format(payer_id))
162
183
 
184
+ if not found_data:
185
+ print("\nNo eligibility data found for any Payer ID.")
186
+
163
187
  # Ask if the user wants to perform another manual lookup
164
188
  continue_choice = input("\nDo you want to perform another manual lookup? (Y/N): ").strip().lower()
165
189
  if continue_choice in ['n', 'no']:
166
190
  break
167
191
 
168
- # Display available Payer IDs as a note
169
- print("\nNOTE: The tool can only look up the following Payer IDs:")
170
- print(", ".join(payer_ids))
171
- print("-------------------------------------------------\n")
172
-
173
192
 
174
193
  # Function to get eligibility information
175
- def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, member_id, npi):
194
+ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, member_id, npi, run_validation=False):
176
195
  try:
177
196
  # Log the parameters being sent to the function
178
197
  MediLink_ConfigLoader.log("Calling eligibility check with parameters:", level="DEBUG")
@@ -184,20 +203,64 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
184
203
 
185
204
  # Configuration flag to control which API to use
186
205
  # Set to False to use the new Super Connector API, True to use the legacy v3 API
187
- USE_LEGACY_API = False
206
+ USE_LEGACY_API = True # Changed to True to use legacy as primary
207
+
208
+ # Always get legacy response first
209
+ MediLink_ConfigLoader.log("Getting legacy get_eligibility_v3 API response", level="INFO")
210
+ legacy_eligibility = MediLink_API_v3.get_eligibility_v3(
211
+ client, payer_id, provider_last_name, 'MemberIDDateOfBirth', date_of_birth, member_id, npi
212
+ )
188
213
 
189
- if USE_LEGACY_API:
190
- # Use the legacy get_eligibility_v3 function as primary
191
- MediLink_ConfigLoader.log("Using legacy get_eligibility_v3 API", level="INFO")
192
- eligibility = MediLink_API_v3.get_eligibility_v3(
214
+ # Also get Super Connector response for comparison
215
+ MediLink_ConfigLoader.log("Getting new get_eligibility_super_connector API response", level="INFO")
216
+ super_connector_eligibility = None
217
+ try:
218
+ super_connector_eligibility = MediLink_API_v3.get_eligibility_super_connector(
193
219
  client, payer_id, provider_last_name, 'MemberIDDateOfBirth', date_of_birth, member_id, npi
194
220
  )
195
- else:
196
- # Use the new Super Connector API as primary
197
- MediLink_ConfigLoader.log("Using new get_eligibility_super_connector API", level="INFO")
198
- eligibility = MediLink_API_v3.get_eligibility_super_connector(
199
- client, payer_id, provider_last_name, 'MemberIDDateOfBirth', date_of_birth, member_id, npi
221
+ except Exception as e:
222
+ MediLink_ConfigLoader.log("Super Connector API failed: {}".format(e), level="ERROR")
223
+
224
+ # Run validation if requested and we have both responses
225
+ if run_validation and legacy_eligibility and super_connector_eligibility:
226
+ validation_file_path = os.path.join(os.getenv('TEMP'), 'validation_report_{}_{}.txt'.format(member_id, date_of_birth))
227
+ validation_report = MediLink_Deductible_Validator.run_validation_comparison(
228
+ legacy_eligibility, super_connector_eligibility, validation_file_path
200
229
  )
230
+ print("\nValidation report generated: {}".format(validation_file_path))
231
+
232
+ # Log any Super Connector API errors
233
+ if super_connector_eligibility and "rawGraphQLResponse" in super_connector_eligibility:
234
+ raw_response = super_connector_eligibility.get('rawGraphQLResponse', {})
235
+ errors = raw_response.get('errors', [])
236
+ if errors:
237
+ print("Super Connector API returned {} error(s):".format(len(errors)))
238
+ for i, error in enumerate(errors):
239
+ error_code = error.get('code', 'UNKNOWN')
240
+ error_desc = error.get('description', 'No description')
241
+ print(" Error {}: {} - {}".format(i+1, error_code, error_desc))
242
+
243
+ # Check for data in error extensions (some APIs return data here)
244
+ extensions = error.get('extensions', {})
245
+ if extensions and 'details' in extensions:
246
+ details = extensions.get('details', [])
247
+ if details:
248
+ print(" Found {} detail records in error extensions".format(len(details)))
249
+ # Log first detail record for debugging
250
+ if details:
251
+ first_detail = details[0]
252
+ print(" First detail: {}".format(first_detail))
253
+
254
+ # Check status code
255
+ status_code = super_connector_eligibility.get('statuscode')
256
+ if status_code and status_code != '200':
257
+ print("Super Connector API status code: {} (non-200 indicates errors)".format(status_code))
258
+
259
+ # Open validation report in Notepad
260
+ os.system('notepad.exe "{}"'.format(validation_file_path))
261
+
262
+ # Return the primary response (legacy for now)
263
+ eligibility = legacy_eligibility if USE_LEGACY_API else super_connector_eligibility
201
264
 
202
265
  # Log the response
203
266
  MediLink_ConfigLoader.log("Eligibility response: {}".format(json.dumps(eligibility, indent=4)), level="DEBUG")
@@ -229,7 +292,41 @@ def extract_super_connector_patient_info(eligibility_data):
229
292
  if not eligibility_data:
230
293
  return {'lastName': '', 'firstName': '', 'middleName': ''}
231
294
 
232
- # The response structure is flat at the top level
295
+ # Handle multiple eligibility records - use the first one with valid data
296
+ if "rawGraphQLResponse" in eligibility_data:
297
+ raw_response = eligibility_data.get('rawGraphQLResponse', {})
298
+ data = raw_response.get('data', {})
299
+ check_eligibility = data.get('checkEligibility', {})
300
+ eligibility_list = check_eligibility.get('eligibility', [])
301
+
302
+ # Try to get from the first eligibility record
303
+ if eligibility_list:
304
+ first_eligibility = eligibility_list[0]
305
+ member_info = first_eligibility.get('eligibilityInfo', {}).get('member', {})
306
+ if member_info:
307
+ return {
308
+ 'lastName': member_info.get("lastName", ""),
309
+ 'firstName': member_info.get("firstName", ""),
310
+ 'middleName': member_info.get("middleName", "")
311
+ }
312
+
313
+ # Check for data in error extensions (some APIs return data here despite errors)
314
+ errors = raw_response.get('errors', [])
315
+ for error in errors:
316
+ extensions = error.get('extensions', {})
317
+ if extensions and 'details' in extensions:
318
+ details = extensions.get('details', [])
319
+ if details:
320
+ # Use the first detail record that has patient info
321
+ for detail in details:
322
+ if detail.get('lastName') or detail.get('firstName'):
323
+ return {
324
+ 'lastName': detail.get("lastName", ""),
325
+ 'firstName': detail.get("firstName", ""),
326
+ 'middleName': detail.get("middleName", "")
327
+ }
328
+
329
+ # Fallback to top-level fields
233
330
  return {
234
331
  'lastName': eligibility_data.get("lastName", ""),
235
332
  'firstName': eligibility_data.get("firstName", ""),
@@ -258,44 +355,95 @@ def extract_super_connector_remaining_amount(eligibility_data):
258
355
  if met_amount is not None:
259
356
  return str(met_amount)
260
357
 
261
- # Navigate to the rawGraphQLResponse structure
262
- raw_response = eligibility_data.get('rawGraphQLResponse', {})
263
- if not raw_response:
264
- return "Not Found"
358
+ # Collect all deductible amounts to find the most relevant one
359
+ all_deductible_amounts = []
265
360
 
266
- data = raw_response.get('data', {})
267
- check_eligibility = data.get('checkEligibility', {})
268
- eligibility_list = check_eligibility.get('eligibility', [])
269
-
270
- if not eligibility_list:
271
- return "Not Found"
272
-
273
- first_eligibility = eligibility_list[0]
274
- service_levels = first_eligibility.get('serviceLevels', [])
275
-
276
- # Look for deductible information in service levels
277
- for service_level in service_levels:
278
- individual_services = service_level.get('individual', [])
279
- for individual in individual_services:
280
- services = individual.get('services', [])
281
- for service in services:
282
- # Look for deductible-related information
283
- if service.get('service') == 'deductible' or 'deductible' in service.get('text', '').lower():
284
- return service.get('remainingAmount', "")
285
-
286
- # Check the message.deductible.text field for deductible information
287
- message = service.get('message', {})
288
- deductible_msg = message.get('deductible', {})
289
- if deductible_msg and deductible_msg.get('text'):
290
- return deductible_msg.get('text', "")
291
-
292
- # If no specific deductible found, try to get from plan levels
293
- plan_levels = first_eligibility.get('eligibilityInfo', {}).get('planLevels', [])
361
+ # Look for deductible information in planLevels (based on validation report)
362
+ plan_levels = eligibility_data.get('planLevels', [])
294
363
  for plan_level in plan_levels:
295
- if plan_level.get('level') == 'deductibleInfo/outOfPocket/coPayMax':
364
+ if plan_level.get('level') == 'deductibleInfo':
365
+ # Collect individual deductible amounts
296
366
  individual_levels = plan_level.get('individual', [])
297
367
  if individual_levels:
298
- return individual_levels[0].get('remainingAmount', "")
368
+ for individual in individual_levels:
369
+ remaining = individual.get('remainingAmount')
370
+ if remaining is not None:
371
+ try:
372
+ amount = float(remaining)
373
+ all_deductible_amounts.append(('individual', amount))
374
+ except (ValueError, TypeError):
375
+ pass
376
+
377
+ # Collect family deductible amounts
378
+ family_levels = plan_level.get('family', [])
379
+ if family_levels:
380
+ for family in family_levels:
381
+ remaining = family.get('remainingAmount')
382
+ if remaining is not None:
383
+ try:
384
+ amount = float(remaining)
385
+ all_deductible_amounts.append(('family', amount))
386
+ except (ValueError, TypeError):
387
+ pass
388
+
389
+ # Navigate to the rawGraphQLResponse structure as fallback
390
+ raw_response = eligibility_data.get('rawGraphQLResponse', {})
391
+ if raw_response:
392
+ data = raw_response.get('data', {})
393
+ check_eligibility = data.get('checkEligibility', {})
394
+ eligibility_list = check_eligibility.get('eligibility', [])
395
+
396
+ # Try all eligibility records for deductible information
397
+ for eligibility in eligibility_list:
398
+ plan_levels = eligibility.get('eligibilityInfo', {}).get('planLevels', [])
399
+ for plan_level in plan_levels:
400
+ if plan_level.get('level') == 'deductibleInfo':
401
+ # Collect individual deductible amounts
402
+ individual_levels = plan_level.get('individual', [])
403
+ if individual_levels:
404
+ for individual in individual_levels:
405
+ remaining = individual.get('remainingAmount')
406
+ if remaining is not None:
407
+ try:
408
+ amount = float(remaining)
409
+ all_deductible_amounts.append(('individual', amount))
410
+ except (ValueError, TypeError):
411
+ pass
412
+
413
+ # Collect family deductible amounts
414
+ family_levels = plan_level.get('family', [])
415
+ if family_levels:
416
+ for family in family_levels:
417
+ remaining = family.get('remainingAmount')
418
+ if remaining is not None:
419
+ try:
420
+ amount = float(remaining)
421
+ all_deductible_amounts.append(('family', amount))
422
+ except (ValueError, TypeError):
423
+ pass
424
+
425
+ # Select the most relevant deductible amount
426
+ if all_deductible_amounts:
427
+ # Strategy: Prefer individual over family, and prefer non-zero amounts
428
+ # First, try to find non-zero individual amounts
429
+ non_zero_individual = [amt for type_, amt in all_deductible_amounts if type_ == 'individual' and amt > 0]
430
+ if non_zero_individual:
431
+ return str(max(non_zero_individual)) # Return highest non-zero individual amount
432
+
433
+ # If no non-zero individual, try non-zero family amounts
434
+ non_zero_family = [amt for type_, amt in all_deductible_amounts if type_ == 'family' and amt > 0]
435
+ if non_zero_family:
436
+ return str(max(non_zero_family)) # Return highest non-zero family amount
437
+
438
+ # If all amounts are zero, return the first individual amount (or family if no individual)
439
+ individual_amounts = [amt for type_, amt in all_deductible_amounts if type_ == 'individual']
440
+ if individual_amounts:
441
+ return str(individual_amounts[0])
442
+
443
+ # Fallback to first family amount
444
+ family_amounts = [amt for type_, amt in all_deductible_amounts if type_ == 'family']
445
+ if family_amounts:
446
+ return str(family_amounts[0])
299
447
 
300
448
  return "Not Found"
301
449
 
@@ -314,12 +462,79 @@ def extract_super_connector_insurance_info(eligibility_data):
314
462
  if not eligibility_data:
315
463
  return {'insuranceType': '', 'insuranceTypeCode': '', 'memberId': '', 'payerId': ''}
316
464
 
317
- # Get plan type description instead of coverage type
465
+ # Handle multiple eligibility records - use the first one with valid data
466
+ if "rawGraphQLResponse" in eligibility_data:
467
+ raw_response = eligibility_data.get('rawGraphQLResponse', {})
468
+ data = raw_response.get('data', {})
469
+ check_eligibility = data.get('checkEligibility', {})
470
+ eligibility_list = check_eligibility.get('eligibility', [])
471
+
472
+ # Try to get from the first eligibility record
473
+ if eligibility_list:
474
+ first_eligibility = eligibility_list[0]
475
+ insurance_info = first_eligibility.get('eligibilityInfo', {}).get('insuranceInfo', {})
476
+ if insurance_info:
477
+ return {
478
+ 'insuranceType': insurance_info.get("planTypeDescription", ""),
479
+ 'insuranceTypeCode': insurance_info.get("productServiceCode", ""),
480
+ 'memberId': insurance_info.get("memberId", ""),
481
+ 'payerId': insurance_info.get("payerId", "")
482
+ }
483
+
484
+ # Check for data in error extensions (some APIs return data here despite errors)
485
+ errors = raw_response.get('errors', [])
486
+ for error in errors:
487
+ extensions = error.get('extensions', {})
488
+ if extensions and 'details' in extensions:
489
+ details = extensions.get('details', [])
490
+ if details:
491
+ # Use the first detail record that has insurance info
492
+ for detail in details:
493
+ if detail.get('memberId') or detail.get('payerId'):
494
+ # Try to determine insurance type from available data
495
+ insurance_type = detail.get('planType', '')
496
+ if not insurance_type:
497
+ insurance_type = detail.get('productType', '')
498
+
499
+ return {
500
+ 'insuranceType': insurance_type,
501
+ 'insuranceTypeCode': detail.get("productServiceCode", ""),
502
+ 'memberId': detail.get("memberId", ""),
503
+ 'payerId': detail.get("payerId", "")
504
+ }
505
+
506
+ # Fallback to top-level fields
318
507
  insurance_type = eligibility_data.get("planTypeDescription", "")
508
+ if not insurance_type:
509
+ insurance_type = eligibility_data.get("productType", "")
510
+
511
+ # Clean up the insurance type if it's too long (like the LPPO description)
512
+ if insurance_type and len(insurance_type) > 50:
513
+ # Extract just the plan type part
514
+ if "PPO" in insurance_type:
515
+ insurance_type = "Preferred Provider Organization (PPO)"
516
+ elif "HMO" in insurance_type:
517
+ insurance_type = "Health Maintenance Organization (HMO)"
518
+ elif "EPO" in insurance_type:
519
+ insurance_type = "Exclusive Provider Organization (EPO)"
520
+ elif "POS" in insurance_type:
521
+ insurance_type = "Point of Service (POS)"
522
+
523
+ # Get insurance type code from multiple possible locations
524
+ insurance_type_code = eligibility_data.get("productServiceCode", "")
525
+ if not insurance_type_code:
526
+ # Try to get from coverageTypes
527
+ coverage_types = eligibility_data.get("coverageTypes", [])
528
+ if coverage_types:
529
+ insurance_type_code = coverage_types[0].get("typeCode", "")
530
+
531
+ # Note: We're not mapping "M" to "PR" as "M" likely means "Medical"
532
+ # and "PR" should be "12" for PPO according to CMS standards
533
+ # This mapping should be handled by the API developers
319
534
 
320
535
  return {
321
536
  'insuranceType': insurance_type,
322
- 'insuranceTypeCode': eligibility_data.get("productServiceCode", ""),
537
+ 'insuranceTypeCode': insurance_type_code,
323
538
  'memberId': eligibility_data.get("subscriberId", ""),
324
539
  'payerId': eligibility_data.get("payerId", "") # Use payerId instead of legalEntityCode (this should be payer_id from the inputs)
325
540
  }
@@ -334,7 +549,21 @@ def extract_super_connector_policy_status(eligibility_data):
334
549
  if not eligibility_data:
335
550
  return ""
336
551
 
337
- # Policy status is at the top level
552
+ # Handle multiple eligibility records - use the first one with valid data
553
+ if "rawGraphQLResponse" in eligibility_data:
554
+ raw_response = eligibility_data.get('rawGraphQLResponse', {})
555
+ data = raw_response.get('data', {})
556
+ check_eligibility = data.get('checkEligibility', {})
557
+ eligibility_list = check_eligibility.get('eligibility', [])
558
+
559
+ # Try to get from the first eligibility record
560
+ if eligibility_list:
561
+ first_eligibility = eligibility_list[0]
562
+ insurance_info = first_eligibility.get('eligibilityInfo', {}).get('insuranceInfo', {})
563
+ if insurance_info:
564
+ return insurance_info.get("policyStatus", "")
565
+
566
+ # Fallback to top-level field
338
567
  return eligibility_data.get("policyStatus", "")
339
568
 
340
569
  def is_legacy_response_format(data):
@@ -403,47 +632,111 @@ def display_eligibility_info(data, dob, member_id, output_file):
403
632
 
404
633
  # Main Execution Flow
405
634
  if __name__ == "__main__":
406
- # Step 1: Handle Manual Deductible Lookups
407
- manual_deductible_lookup()
408
-
409
- # Step 2: Proceed with Existing CSV Processing
410
- print("--- Starting Batch Eligibility Processing ---")
411
- output_file_path = os.path.join(os.getenv('TEMP'), 'eligibility_report.txt')
412
- with open(output_file_path, 'w') as output_file:
413
- table_header = "{:<20} | {:<10} | {:<40} | {:<5} | {:<14} | {:<14}".format(
414
- "Patient Name", "DOB", "Insurance Type", "PayID", "Policy Status", "Remaining Amt")
415
- output_file.write(table_header + "\n")
416
- output_file.write("-" * len(table_header) + "\n")
417
- print(table_header)
418
- print("-" * len(table_header))
419
-
420
- # Set to keep track of processed patients
421
- processed_patients = set()
422
-
423
- # Loop through each payer_id and patient to call the API, then display the eligibility information
424
- errors = []
425
- for payer_id in payer_ids:
426
- for dob, member_id in patients:
427
- # Skip if this patient has already been processed
428
- if (dob, member_id) in processed_patients:
429
- continue
430
- try:
431
- eligibility_data = get_eligibility_info(client, payer_id, provider_last_name, dob, member_id, npi)
432
- if eligibility_data is not None:
433
- display_eligibility_info(eligibility_data, dob, member_id, output_file) # Display as we get the result
434
- processed_patients.add((dob, member_id)) # Mark this patient as processed
435
- except Exception as e:
436
- errors.append((dob, member_id, str(e)))
437
-
438
- # Display errors if any
439
- if errors:
440
- error_msg = "\nErrors encountered during API calls:\n"
441
- output_file.write(error_msg)
442
- print(error_msg)
443
- for error in errors:
444
- error_details = "DOB: {}, Member ID: {}, Error: {}\n".format(error[0], error[1], error[2])
445
- output_file.write(error_details)
446
- print(error_details)
447
-
448
- # Open the generated file in Notepad
449
- os.system('notepad.exe "{}"'.format(output_file_path))
635
+ print("\n" + "=" * 80)
636
+ print("MEDILINK DEDUCTIBLE LOOKUP TOOL")
637
+ print("=" * 80)
638
+ print("This tool provides manual and batch eligibility lookups with validation.")
639
+ print("Validation reports compare legacy vs Super Connector API responses.")
640
+ print("=" * 80)
641
+
642
+ while True:
643
+ print("\nChoose an option:")
644
+ print("1. Manual Patient Lookup (with validation)")
645
+ print("2. Batch CSV Processing (with validation)")
646
+ print("3. Exit")
647
+
648
+ choice = input("\nEnter your choice (1-3): ").strip()
649
+
650
+ if choice == "1":
651
+ # Step 1: Handle Manual Deductible Lookups
652
+ manual_deductible_lookup()
653
+
654
+ # Ask if user wants to continue
655
+ continue_choice = input("\nDo you want to perform another operation? (Y/N): ").strip().lower()
656
+ if continue_choice in ['n', 'no']:
657
+ print("\nExiting. Thank you for using MediLink Deductible Tool!")
658
+ break
659
+
660
+ elif choice == "2":
661
+ # Step 2: Proceed with Existing CSV Processing
662
+ print("\n--- Starting Batch Eligibility Processing ---")
663
+ print("Processing {} patients from CSV data...".format(len(patients)))
664
+
665
+ # Ask for confirmation before starting batch processing
666
+ confirm = input("Proceed with batch processing? (Y/N): ").strip().lower()
667
+ if confirm not in ['y', 'yes']:
668
+ print("Batch processing cancelled.")
669
+ continue
670
+
671
+ output_file_path = os.path.join(os.getenv('TEMP'), 'eligibility_report.txt')
672
+ with open(output_file_path, 'w') as output_file:
673
+ table_header = "{:<20} | {:<10} | {:<40} | {:<5} | {:<14} | {:<14}".format(
674
+ "Patient Name", "DOB", "Insurance Type", "PayID", "Policy Status", "Remaining Amt")
675
+ output_file.write(table_header + "\n")
676
+ output_file.write("-" * len(table_header) + "\n")
677
+ print(table_header)
678
+ print("-" * len(table_header))
679
+
680
+ # Set to keep track of processed patients
681
+ processed_patients = set()
682
+
683
+ # Loop through each payer_id and patient to call the API, then display the eligibility information
684
+ errors = []
685
+ validation_reports = []
686
+ total_patients = len(patients) * len(payer_ids)
687
+ processed_count = 0
688
+
689
+ for payer_id in payer_ids:
690
+ for dob, member_id in patients:
691
+ # Skip if this patient has already been processed
692
+ if (dob, member_id) in processed_patients:
693
+ continue
694
+ try:
695
+ processed_count += 1
696
+ print("Processing patient {}/{}: Member ID {}, DOB {}".format(
697
+ processed_count, total_patients, member_id, dob))
698
+
699
+ # Run with validation enabled for batch processing
700
+ eligibility_data = get_eligibility_info(client, payer_id, provider_last_name, dob, member_id, npi, run_validation=True)
701
+ if eligibility_data is not None:
702
+ display_eligibility_info(eligibility_data, dob, member_id, output_file) # Display as we get the result
703
+ processed_patients.add((dob, member_id)) # Mark this patient as processed
704
+ except Exception as e:
705
+ errors.append((dob, member_id, str(e)))
706
+
707
+ # Display errors if any
708
+ if errors:
709
+ error_msg = "\nErrors encountered during API calls:\n"
710
+ output_file.write(error_msg)
711
+ print(error_msg)
712
+ for error in errors:
713
+ error_details = "DOB: {}, Member ID: {}, Error: {}\n".format(error[0], error[1], error[2])
714
+ output_file.write(error_details)
715
+ print(error_details)
716
+
717
+ # Ask if user wants to open the report
718
+ open_report = input("\nBatch processing complete! Open the eligibility report? (Y/N): ").strip().lower()
719
+ if open_report in ['y', 'yes']:
720
+ os.system('notepad.exe "{}"'.format(output_file_path))
721
+
722
+ # Print summary of validation reports
723
+ print("\n" + "=" * 80)
724
+ print("VALIDATION SUMMARY")
725
+ print("=" * 80)
726
+ print("Validation reports have been generated for each patient processed.")
727
+ print("Each report compares the legacy API response with the Super Connector API response.")
728
+ print("Check the TEMP directory for validation_report_*.txt files.")
729
+ print("=" * 80)
730
+
731
+ # Ask if user wants to continue
732
+ continue_choice = input("\nDo you want to perform another operation? (Y/N): ").strip().lower()
733
+ if continue_choice in ['n', 'no']:
734
+ print("\nExiting. Thank you for using MediLink Deductible Tool!")
735
+ break
736
+
737
+ elif choice == "3":
738
+ print("\nExiting. Thank you for using MediLink Deductible Tool!")
739
+ break
740
+
741
+ else:
742
+ print("Invalid choice. Please enter 1, 2, or 3.")