medicafe 0.251014.0__tar.gz → 0.251016.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.
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/__init__.py +1 -1
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/__init__.py +1 -1
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/deductible_utils.py +49 -4
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Deductible.py +89 -13
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Display_Utils.py +33 -2
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/__init__.py +1 -1
- {medicafe-0.251014.0/medicafe.egg-info → medicafe-0.251016.0}/PKG-INFO +1 -1
- {medicafe-0.251014.0 → medicafe-0.251016.0/medicafe.egg-info}/PKG-INFO +1 -1
- {medicafe-0.251014.0 → medicafe-0.251016.0}/setup.py +1 -1
- {medicafe-0.251014.0 → medicafe-0.251016.0}/LICENSE +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MANIFEST.in +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Crosswalk_Library.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Notepad_Utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_Preprocessor_lib.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_UI.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_dataformat_library.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_debug.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/MediBot_smart_import.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/clear_cache.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/crash_diagnostic.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/f_drive_diagnostic.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/full_debug_suite.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/get_medicafe_version.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/process_csvs.bat +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/update_json.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediBot/update_medicafe.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/MediLink_ConfigLoader.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/__main__.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/api_core.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/api_factory.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/api_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/core_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/error_reporter.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/graphql_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/logging_config.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/logging_demo.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/migration_helpers.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/smart_import.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediCafe/submission_index.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/InsuranceTypeService.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_837p_encoder.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_837p_encoder_library.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Charges.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_ClaimStatus.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_DataMgmt.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_PatientProcessor.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_Up.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_insurance_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_main.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/MediLink_smart_import.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/gmail_http_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/gmail_oauth_utils.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/openssl.cnf +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/test.py +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/MediLink/webapp.html +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/README.md +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/entry_points.txt +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.251014.0 → medicafe-0.251016.0}/setup.cfg +0 -0
|
@@ -778,7 +778,17 @@ def convert_eligibility_to_enhanced_format(data, dob, member_id, patient_id="",
|
|
|
778
778
|
'data_source': 'Legacy',
|
|
779
779
|
'is_successful': bool(patient_name and remaining_amount != 'Not Found')
|
|
780
780
|
}
|
|
781
|
-
|
|
781
|
+
|
|
782
|
+
# Provide a diagnostic reason when the patient name couldn't be resolved
|
|
783
|
+
if not patient_name:
|
|
784
|
+
try:
|
|
785
|
+
if not compatibility.get("has_patient_info"):
|
|
786
|
+
result['error_reason'] = 'Legacy API response missing patientInfo'
|
|
787
|
+
else:
|
|
788
|
+
result['error_reason'] = 'Legacy patientInfo present but name fields blank'
|
|
789
|
+
except Exception:
|
|
790
|
+
result['error_reason'] = 'Unable to resolve patient name from Legacy response'
|
|
791
|
+
|
|
782
792
|
if MediLink_ConfigLoader:
|
|
783
793
|
MediLink_ConfigLoader.log("Successfully converted Legacy API response: {}".format(result), level="DEBUG")
|
|
784
794
|
return result
|
|
@@ -815,7 +825,17 @@ def convert_eligibility_to_enhanced_format(data, dob, member_id, patient_id="",
|
|
|
815
825
|
'data_source': 'OptumAI',
|
|
816
826
|
'is_successful': bool(patient_name and remaining_amount != 'Not Found')
|
|
817
827
|
}
|
|
818
|
-
|
|
828
|
+
|
|
829
|
+
# Provide a diagnostic reason when the patient name couldn't be resolved
|
|
830
|
+
if not patient_name:
|
|
831
|
+
try:
|
|
832
|
+
if not compatibility.get("has_patient_info"):
|
|
833
|
+
result['error_reason'] = 'OptumAI response missing member info'
|
|
834
|
+
else:
|
|
835
|
+
result['error_reason'] = 'OptumAI member info present but name fields blank'
|
|
836
|
+
except Exception:
|
|
837
|
+
result['error_reason'] = 'Unable to resolve patient name from OptumAI response'
|
|
838
|
+
|
|
819
839
|
if MediLink_ConfigLoader:
|
|
820
840
|
MediLink_ConfigLoader.log("Successfully converted Super Connector API response: {}".format(result), level="DEBUG")
|
|
821
841
|
return result
|
|
@@ -929,7 +949,8 @@ def merge_responses(optumai_data, legacy_data, dob, member_id):
|
|
|
929
949
|
'policy_status': 'Not Available',
|
|
930
950
|
'remaining_amount': 'Not Found',
|
|
931
951
|
'data_source': 'None',
|
|
932
|
-
'is_successful': False
|
|
952
|
+
'is_successful': False,
|
|
953
|
+
'error_reason': 'No eligibility responses returned from OptumAI or Legacy APIs'
|
|
933
954
|
}
|
|
934
955
|
|
|
935
956
|
# Helper to check if data is valid (not None, has required fields)
|
|
@@ -1047,7 +1068,8 @@ def merge_responses(optumai_data, legacy_data, dob, member_id):
|
|
|
1047
1068
|
'policy_status': 'Not Available',
|
|
1048
1069
|
'remaining_amount': 'Not Found',
|
|
1049
1070
|
'payer_id': '',
|
|
1050
|
-
'data_source': 'None'
|
|
1071
|
+
'data_source': 'None',
|
|
1072
|
+
'error_reason': 'Unable to extract patient name from available eligibility responses'
|
|
1051
1073
|
}
|
|
1052
1074
|
|
|
1053
1075
|
# Intelligently backfill missing fields from Legacy API (secondary)
|
|
@@ -1129,6 +1151,22 @@ def merge_responses(optumai_data, legacy_data, dob, member_id):
|
|
|
1129
1151
|
if 'data_source' not in merged or not merged['data_source']:
|
|
1130
1152
|
merged['data_source'] = 'OptumAI' if primary == optumai_data else 'Legacy' if primary else 'None'
|
|
1131
1153
|
|
|
1154
|
+
# If patient name still missing or unknown, infer a failure reason when possible
|
|
1155
|
+
try:
|
|
1156
|
+
if (not merged.get('patient_name')) or (merged.get('patient_name') == 'Unknown Patient'):
|
|
1157
|
+
if not merged.get('error_reason'):
|
|
1158
|
+
if is_super_connector_response_format(optumai_data or {}):
|
|
1159
|
+
raw = (optumai_data or {}).get('rawGraphQLResponse', {})
|
|
1160
|
+
elig = raw.get('data', {}).get('checkEligibility', {}).get('eligibility', [])
|
|
1161
|
+
merged['error_reason'] = 'OptumAI response contained no eligibility records' if not elig else 'OptumAI eligibility present but member name missing'
|
|
1162
|
+
elif is_legacy_response_format(legacy_data or {}):
|
|
1163
|
+
policies = (legacy_data or {}).get('memberPolicies', [])
|
|
1164
|
+
merged['error_reason'] = 'Legacy API returned no memberPolicies records' if not policies else 'Legacy policy present but patient name missing'
|
|
1165
|
+
else:
|
|
1166
|
+
merged['error_reason'] = 'Patient name not found in responses or CSV backfill'
|
|
1167
|
+
except Exception:
|
|
1168
|
+
pass
|
|
1169
|
+
|
|
1132
1170
|
# Set final success status
|
|
1133
1171
|
merged['is_successful'] = bool(merged.get('patient_name') and
|
|
1134
1172
|
merged.get('patient_name') != 'Unknown Patient' and
|
|
@@ -1214,6 +1252,13 @@ def backfill_enhanced_result(enhanced_result, csv_row=None):
|
|
|
1214
1252
|
csv_name = _format_patient_name_from_csv_row(csv_row)
|
|
1215
1253
|
if csv_name:
|
|
1216
1254
|
result['patient_name'] = csv_name
|
|
1255
|
+
else:
|
|
1256
|
+
# Attach CSV-specific diagnostic if still missing
|
|
1257
|
+
try:
|
|
1258
|
+
if not result.get('error_reason'):
|
|
1259
|
+
result['error_reason'] = 'CSV patient name fields missing or blank'
|
|
1260
|
+
except Exception:
|
|
1261
|
+
pass
|
|
1217
1262
|
|
|
1218
1263
|
# Backfill patient_id
|
|
1219
1264
|
if not result.get('patient_id') and csv_row is not None:
|
|
@@ -375,12 +375,16 @@ def manual_deductible_lookup():
|
|
|
375
375
|
|
|
376
376
|
# Fetch eligibility data
|
|
377
377
|
found_data = False
|
|
378
|
+
printed_messages = set()
|
|
378
379
|
for i, payer_id in enumerate(payer_ids, 1):
|
|
379
380
|
print("Checking Payer ID {} ({}/{}): {}".format(payer_id, i, len(payer_ids), payer_id))
|
|
380
381
|
|
|
381
382
|
# Use the current mode setting for validation
|
|
382
383
|
run_validation = DEBUG_MODE
|
|
383
|
-
eligibility_data = get_eligibility_info(
|
|
384
|
+
eligibility_data = get_eligibility_info(
|
|
385
|
+
client, payer_id, provider_last_name, formatted_dob, member_id, npi,
|
|
386
|
+
run_validation=run_validation, is_manual_lookup=True, printed_messages=printed_messages
|
|
387
|
+
)
|
|
384
388
|
if eligibility_data:
|
|
385
389
|
found_data = True
|
|
386
390
|
|
|
@@ -484,13 +488,28 @@ def manual_deductible_lookup():
|
|
|
484
488
|
enhanced_result['policy_status'][:14],
|
|
485
489
|
enhanced_result['remaining_amount'][:14])
|
|
486
490
|
output_file.write(table_row + "\n")
|
|
491
|
+
|
|
492
|
+
# Persist per-row error diagnostics in a user-friendly way
|
|
493
|
+
try:
|
|
494
|
+
row_reason = _compute_error_reason(enhanced_result)
|
|
495
|
+
row_messages = enhanced_result.get('error_messages', []) or []
|
|
496
|
+
if row_reason or row_messages:
|
|
497
|
+
output_file.write(" >> Errors:" + "\n")
|
|
498
|
+
if row_reason:
|
|
499
|
+
output_file.write(" >> - {}\n".format(row_reason))
|
|
500
|
+
for msg in row_messages:
|
|
501
|
+
# Avoid duplicating the reason message if identical
|
|
502
|
+
if not row_reason or msg.strip() != row_reason.strip():
|
|
503
|
+
output_file.write(" >> - {}\n".format(str(msg)))
|
|
504
|
+
except Exception:
|
|
505
|
+
pass
|
|
487
506
|
else:
|
|
488
507
|
display_eligibility_info(eligibility_data, formatted_dob, member_id, output_file)
|
|
489
508
|
|
|
490
509
|
# Ask if user wants to open the report
|
|
491
510
|
open_report = input("\nEligibility data found! Open the report? (Y/N): ").strip().lower()
|
|
492
511
|
if open_report in ['y', 'yes']:
|
|
493
|
-
os.
|
|
512
|
+
os.startfile(output_file_path)
|
|
494
513
|
print("Manual eligibility report generated: {}\n".format(output_file_path))
|
|
495
514
|
break # Assuming one payer ID per manual lookup
|
|
496
515
|
else:
|
|
@@ -523,6 +542,8 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
523
542
|
if run_validation:
|
|
524
543
|
# Debug mode: Call both APIs and run validation
|
|
525
544
|
MediLink_ConfigLoader.log("Running in DEBUG MODE - calling both APIs", level="INFO")
|
|
545
|
+
# Always initialize row-level error messages for diagnostics
|
|
546
|
+
error_messages_for_row = []
|
|
526
547
|
|
|
527
548
|
# Get legacy response
|
|
528
549
|
MediLink_ConfigLoader.log("Getting legacy get_eligibility_v3 API response", level="INFO")
|
|
@@ -596,6 +617,11 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
596
617
|
if detail_msg not in printed_messages:
|
|
597
618
|
print(detail_msg)
|
|
598
619
|
printed_messages.add(detail_msg)
|
|
620
|
+
# Accumulate per-row messages for persistence in reports
|
|
621
|
+
try:
|
|
622
|
+
error_messages_for_row.append("{} - {}".format(error_code, error_desc))
|
|
623
|
+
except Exception:
|
|
624
|
+
pass
|
|
599
625
|
|
|
600
626
|
# Check for data in error extensions (some APIs return data here)
|
|
601
627
|
extensions = error.get('extensions', {})
|
|
@@ -607,20 +633,25 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
607
633
|
if details:
|
|
608
634
|
first_detail = details[0]
|
|
609
635
|
print(" First detail: {}".format(first_detail))
|
|
636
|
+
# Persist a brief extension note without dumping raw objects
|
|
637
|
+
try:
|
|
638
|
+
error_messages_for_row.append("Extensions include {} detail record(s)".format(len(details)))
|
|
639
|
+
except Exception:
|
|
640
|
+
pass
|
|
610
641
|
|
|
611
642
|
# Check status code
|
|
612
643
|
if super_connector_eligibility:
|
|
613
644
|
status_code = super_connector_eligibility.get('statuscode')
|
|
614
|
-
if status_code and status_code != '200':
|
|
645
|
+
if status_code is not None and str(status_code).strip() != '200':
|
|
615
646
|
print("Super Connector API status code: {} (non-200 indicates errors)".format(status_code))
|
|
647
|
+
# Record status code for the row diagnostics
|
|
648
|
+
error_messages_for_row.append("Status code {} from Super Connector".format(status_code))
|
|
616
649
|
|
|
617
650
|
# Open validation report in Notepad (only for manual lookups, not batch processing)
|
|
618
651
|
if validation_file_path and os.path.exists(validation_file_path):
|
|
619
652
|
# Only open in manual mode - batch processing will handle this separately
|
|
620
653
|
if is_manual_lookup: # Check if we're in manual lookup mode
|
|
621
|
-
|
|
622
|
-
if ret != 0:
|
|
623
|
-
print("Failed to open Notepad (exit code: {}). Please open manually: {}".format(ret, validation_file_path))
|
|
654
|
+
os.startfile(validation_file_path)
|
|
624
655
|
elif validation_file_path:
|
|
625
656
|
print("\nValidation report file was not created: {}".format(validation_file_path))
|
|
626
657
|
except Exception as e:
|
|
@@ -628,7 +659,17 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
628
659
|
|
|
629
660
|
# After validation, merge responses
|
|
630
661
|
try:
|
|
631
|
-
|
|
662
|
+
if merge_responses is not None:
|
|
663
|
+
merged_data = merge_responses(super_connector_eligibility, legacy_eligibility, date_of_birth, member_id)
|
|
664
|
+
else:
|
|
665
|
+
MediLink_ConfigLoader.log("merge_responses utility not available; returning raw API response", level="WARNING")
|
|
666
|
+
merged_data = super_connector_eligibility or legacy_eligibility or {}
|
|
667
|
+
# Attach accumulated row-level messages for downstream display/persistence
|
|
668
|
+
try:
|
|
669
|
+
if isinstance(merged_data, dict) and error_messages_for_row:
|
|
670
|
+
merged_data['error_messages'] = error_messages_for_row
|
|
671
|
+
except Exception:
|
|
672
|
+
pass
|
|
632
673
|
return merged_data
|
|
633
674
|
except Exception as e:
|
|
634
675
|
MediLink_ConfigLoader.log("Error in merge_responses: {}".format(e), level="ERROR")
|
|
@@ -713,8 +754,10 @@ def display_eligibility_info(data, dob, member_id, output_file, patient_id="", s
|
|
|
713
754
|
if data is None:
|
|
714
755
|
return
|
|
715
756
|
|
|
716
|
-
# Convert to enhanced format
|
|
717
|
-
enhanced_data =
|
|
757
|
+
# Convert to enhanced format (guard if utility missing)
|
|
758
|
+
enhanced_data = None
|
|
759
|
+
if convert_eligibility_to_enhanced_format is not None:
|
|
760
|
+
enhanced_data = convert_eligibility_to_enhanced_format(data, dob, member_id, patient_id, service_date)
|
|
718
761
|
if enhanced_data:
|
|
719
762
|
# Write to output file in legacy format for compatibility
|
|
720
763
|
table_row = "{:<20} | {:<10} | {:<40} | {:<5} | {:<14} | {:<14}".format(
|
|
@@ -727,6 +770,27 @@ def display_eligibility_info(data, dob, member_id, output_file, patient_id="", s
|
|
|
727
770
|
output_file.write(table_row + "\n")
|
|
728
771
|
print(table_row) # Print to console for progressive display
|
|
729
772
|
|
|
773
|
+
# Helper to compute a user-friendly error explanation for a result row
|
|
774
|
+
def _compute_error_reason(record):
|
|
775
|
+
try:
|
|
776
|
+
if not isinstance(record, dict):
|
|
777
|
+
return ""
|
|
778
|
+
reason = str(record.get('error_reason', '')).strip()
|
|
779
|
+
name_unknown = (not str(record.get('patient_name', '')).strip()) or (record.get('patient_name') == 'Unknown Patient')
|
|
780
|
+
has_error = (str(record.get('status', '')) == 'Error') or (str(record.get('data_source', '')) in ['None', 'Error'])
|
|
781
|
+
amount_missing = (str(record.get('remaining_amount', '')) == 'Not Found')
|
|
782
|
+
|
|
783
|
+
if not reason:
|
|
784
|
+
if name_unknown:
|
|
785
|
+
reason = 'Patient name could not be determined from API responses or CSV backfill'
|
|
786
|
+
elif amount_missing:
|
|
787
|
+
reason = 'Deductible remaining amount not found in eligibility response'
|
|
788
|
+
elif has_error:
|
|
789
|
+
reason = 'Eligibility lookup encountered an error; see logs for details'
|
|
790
|
+
return reason
|
|
791
|
+
except Exception:
|
|
792
|
+
return ""
|
|
793
|
+
|
|
730
794
|
# Global mode flags (will be set in main)
|
|
731
795
|
LEGACY_MODE = False
|
|
732
796
|
DEBUG_MODE = False
|
|
@@ -1042,6 +1106,20 @@ if __name__ == "__main__":
|
|
|
1042
1106
|
result['remaining_amount'][:14])
|
|
1043
1107
|
output_file.write(table_row + "\n")
|
|
1044
1108
|
|
|
1109
|
+
# Persist per-row error diagnostics in a user-friendly way
|
|
1110
|
+
try:
|
|
1111
|
+
row_reason = _compute_error_reason(result)
|
|
1112
|
+
row_messages = result.get('error_messages', []) or []
|
|
1113
|
+
if row_reason or row_messages:
|
|
1114
|
+
output_file.write(" >> Errors:" + "\n")
|
|
1115
|
+
if row_reason:
|
|
1116
|
+
output_file.write(" >> - {}\n".format(row_reason))
|
|
1117
|
+
for msg in row_messages:
|
|
1118
|
+
if not row_reason or msg.strip() != row_reason.strip():
|
|
1119
|
+
output_file.write(" >> - {}\n".format(str(msg)))
|
|
1120
|
+
except Exception:
|
|
1121
|
+
pass
|
|
1122
|
+
|
|
1045
1123
|
# Write enhanced error summary to file
|
|
1046
1124
|
if errors:
|
|
1047
1125
|
error_msg = "\nErrors encountered during API calls:\n"
|
|
@@ -1053,7 +1131,7 @@ if __name__ == "__main__":
|
|
|
1053
1131
|
# Ask if user wants to open the report
|
|
1054
1132
|
open_report = input("\nBatch processing complete! Open the eligibility report? (Y/N): ").strip().lower()
|
|
1055
1133
|
if open_report in ['y', 'yes']:
|
|
1056
|
-
os.
|
|
1134
|
+
os.startfile(output_file_path)
|
|
1057
1135
|
|
|
1058
1136
|
# Print summary of validation reports only in debug mode
|
|
1059
1137
|
if DEBUG_MODE:
|
|
@@ -1072,9 +1150,7 @@ if __name__ == "__main__":
|
|
|
1072
1150
|
if open_validation in ['y', 'yes']:
|
|
1073
1151
|
for file_path in validation_files_created:
|
|
1074
1152
|
print("Opening: {}".format(os.path.basename(file_path)))
|
|
1075
|
-
|
|
1076
|
-
if ret != 0:
|
|
1077
|
-
print("Failed to open Notepad for: {}".format(os.path.basename(file_path)))
|
|
1153
|
+
os.startfile(file_path)
|
|
1078
1154
|
else:
|
|
1079
1155
|
print("No validation reports were generated.")
|
|
1080
1156
|
print("This may be because:")
|
|
@@ -227,7 +227,9 @@ def _normalize_post_api_data(eligibility_result):
|
|
|
227
227
|
'insurance_type': str(eligibility_result.get('insurance_type', '')),
|
|
228
228
|
'policy_status': str(eligibility_result.get('policy_status', '')),
|
|
229
229
|
'remaining_amount': str(eligibility_result.get('remaining_amount', '')),
|
|
230
|
-
'data_source': str(eligibility_result.get('data_source', ''))
|
|
230
|
+
'data_source': str(eligibility_result.get('data_source', '')),
|
|
231
|
+
'error_reason': str(eligibility_result.get('error_reason', '')),
|
|
232
|
+
'is_successful': bool(eligibility_result.get('is_successful', False))
|
|
231
233
|
}
|
|
232
234
|
|
|
233
235
|
# Default unknown patient name when blank
|
|
@@ -440,6 +442,9 @@ def _display_primary_line(record, line_number, col_widths, context):
|
|
|
440
442
|
str(record.get('data_source', ''))[:col_widths['data_source']], col_widths['data_source']
|
|
441
443
|
))
|
|
442
444
|
|
|
445
|
+
# After primary line in post-API view, display an explanatory error row when appropriate
|
|
446
|
+
_maybe_display_error_row(record, context)
|
|
447
|
+
|
|
443
448
|
def _display_secondary_line(record, col_widths, context):
|
|
444
449
|
"""Display secondary line with dashes for grouped data"""
|
|
445
450
|
patient_id_dashes = '-' * min(len(str(record.get('patient_id', ''))), col_widths['patient_id'])
|
|
@@ -491,4 +496,30 @@ def _display_secondary_line(record, col_widths, context):
|
|
|
491
496
|
policy_status_dashes, col_widths['policy_status'],
|
|
492
497
|
str(record.get('remaining_amount', ''))[:col_widths['remaining_amount']], col_widths['remaining_amount'],
|
|
493
498
|
str(record.get('data_source', ''))[:col_widths['data_source']], col_widths['data_source']
|
|
494
|
-
))
|
|
499
|
+
))
|
|
500
|
+
|
|
501
|
+
# For grouped secondary lines, we do not repeat error rows
|
|
502
|
+
|
|
503
|
+
def _maybe_display_error_row(record, context):
|
|
504
|
+
"""Print an explanatory error row beneath the primary line when name or other lookups failed."""
|
|
505
|
+
try:
|
|
506
|
+
if context != 'post_api':
|
|
507
|
+
return
|
|
508
|
+
name_unknown = (not record.get('patient_name')) or (record.get('patient_name') == 'Unknown Patient')
|
|
509
|
+
has_error = (record.get('status') == 'Error') or (record.get('data_source') in ['None', 'Error'])
|
|
510
|
+
amount_missing = (str(record.get('remaining_amount', '')) == 'Not Found')
|
|
511
|
+
reason = record.get('error_reason', '')
|
|
512
|
+
|
|
513
|
+
if not reason:
|
|
514
|
+
if name_unknown:
|
|
515
|
+
reason = 'Patient name could not be determined from API responses or CSV backfill'
|
|
516
|
+
elif amount_missing:
|
|
517
|
+
reason = 'Deductible remaining amount not found in eligibility response'
|
|
518
|
+
elif has_error:
|
|
519
|
+
reason = 'Eligibility lookup encountered an error; see logs for details'
|
|
520
|
+
|
|
521
|
+
if reason:
|
|
522
|
+
print(" >> Error: {}".format(reason))
|
|
523
|
+
except Exception:
|
|
524
|
+
# Never let diagnostics break table rendering
|
|
525
|
+
pass
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|