medicafe 0.250812.0__tar.gz → 0.250812.2__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.
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot.bat +1 -1
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_encoder.py +44 -3
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_encoder_library.py +13 -5
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Up.py +41 -2
- {medicafe-0.250812.0 → medicafe-0.250812.2}/PKG-INFO +1 -1
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/PKG-INFO +1 -1
- {medicafe-0.250812.0 → medicafe-0.250812.2}/setup.py +1 -1
- {medicafe-0.250812.0 → medicafe-0.250812.2}/LICENSE +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MANIFEST.in +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Crosswalk_Library.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Preprocessor_lib.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_UI.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_dataformat_library.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_smart_import.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/__init__.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/get_medicafe_version.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/update_json.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/update_medicafe.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/MediLink_ConfigLoader.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/__init__.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/__main__.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_core.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_core_backup.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_factory.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/core_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/graphql_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/logging_config.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/logging_demo.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/migration_helpers.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/smart_import.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_ClaimStatus.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_DataMgmt.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Deductible.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Display_Utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_PatientProcessor.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_insurance_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_main.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_smart_import.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/__init__.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/gmail_http_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/gmail_oauth_utils.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/insurance_type_integration_test.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/openssl.cnf +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_cob_library.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_timing.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_validation.py +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/webapp.html +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/entry_points.txt +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.250812.0 → medicafe-0.250812.2}/setup.cfg +0 -0
@@ -509,7 +509,7 @@ echo echo Update process exited with code %%RET%%>> "%_UPD_RUNNER%"
|
|
509
509
|
echo if %%RET%% neq 0 (>> "%_UPD_RUNNER%"
|
510
510
|
echo echo Update failed. Press any key to close...>> "%_UPD_RUNNER%"
|
511
511
|
echo pause ^>nul>> "%_UPD_RUNNER%"
|
512
|
-
echo exit
|
512
|
+
echo exit %%RET%%>> "%_UPD_RUNNER%"
|
513
513
|
echo ) else (>> "%_UPD_RUNNER%"
|
514
514
|
echo rem Relaunch MediBot on success>> "%_UPD_RUNNER%"
|
515
515
|
echo if exist "%%MEDIBOT_PATH%%" start "MediBot" "%%MEDIBOT_PATH%%" >> "%_UPD_RUNNER%"
|
@@ -499,11 +499,32 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
|
|
499
499
|
- Future implementation may include progress tracking using tools like `tqdm`.
|
500
500
|
"""
|
501
501
|
|
502
|
+
# Ensure crosswalk is a dictionary to avoid NoneType access downstream
|
503
|
+
if not isinstance(crosswalk, dict):
|
504
|
+
try:
|
505
|
+
MediLink_ConfigLoader.log("[convert_files_for_submission] crosswalk is not a dict; type={}".format(type(crosswalk)), config, level="WARNING")
|
506
|
+
except Exception:
|
507
|
+
pass
|
508
|
+
crosswalk = {}
|
509
|
+
|
502
510
|
# Initialize a dictionary to hold patient data segregated by confirmed endpoints
|
503
511
|
data_by_endpoint = {}
|
504
512
|
|
513
|
+
# Sanitize input: filter out None or non-dict entries to avoid NoneType.get errors
|
514
|
+
if detailed_patient_data is None:
|
515
|
+
detailed_patient_data = []
|
516
|
+
safe_records = []
|
517
|
+
for record in detailed_patient_data:
|
518
|
+
if isinstance(record, dict):
|
519
|
+
safe_records.append(record)
|
520
|
+
else:
|
521
|
+
try:
|
522
|
+
MediLink_ConfigLoader.log("Skipping invalid patient record of type {}".format(type(record)), config, level="WARNING")
|
523
|
+
except Exception:
|
524
|
+
pass
|
525
|
+
|
505
526
|
# Group patient data by endpoint
|
506
|
-
for data in
|
527
|
+
for data in safe_records:
|
507
528
|
endpoint = data.get('confirmed_endpoint')
|
508
529
|
if endpoint:
|
509
530
|
if endpoint not in data_by_endpoint:
|
@@ -515,9 +536,20 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
|
|
515
536
|
|
516
537
|
# Iterate over each endpoint and process its corresponding patient data
|
517
538
|
for endpoint, patient_data_list in data_by_endpoint.items():
|
539
|
+
try:
|
540
|
+
MediLink_ConfigLoader.log("[convert_files_for_submission] Endpoint {} has {} records".format(endpoint, len(patient_data_list)), config, level="INFO")
|
541
|
+
except Exception:
|
542
|
+
pass
|
518
543
|
# Retrieve submission type from config; default to "batch" if not specified
|
519
544
|
medi = extract_medilink_config(config)
|
520
|
-
|
545
|
+
endpoint_cfg = medi.get('endpoints', {}).get(endpoint)
|
546
|
+
if not isinstance(endpoint_cfg, dict):
|
547
|
+
endpoint_cfg = {}
|
548
|
+
submission_type = endpoint_cfg.get('submission_type', 'batch')
|
549
|
+
try:
|
550
|
+
MediLink_ConfigLoader.log("[convert_files_for_submission] submission_type for {}: {}".format(endpoint, submission_type), config, level="DEBUG")
|
551
|
+
except Exception:
|
552
|
+
pass
|
521
553
|
|
522
554
|
if submission_type == 'single':
|
523
555
|
# Process each patient's data individually for single-patient submissions
|
@@ -531,7 +563,16 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
|
|
531
563
|
converted_files_paths.append(converted_path)
|
532
564
|
else:
|
533
565
|
# Process all patient data together for batch submissions
|
534
|
-
|
566
|
+
try:
|
567
|
+
converted_path = process_claim(config, endpoint, patient_data_list, crosswalk, client)
|
568
|
+
except Exception as e:
|
569
|
+
import traceback as _tb
|
570
|
+
tb_s = _tb.format_exc()
|
571
|
+
try:
|
572
|
+
MediLink_ConfigLoader.log("[convert_files_for_submission] process_claim failed for {}: {}\nTraceback: {}".format(endpoint, e, tb_s), config, level="ERROR")
|
573
|
+
except Exception:
|
574
|
+
pass
|
575
|
+
raise
|
535
576
|
if converted_path:
|
536
577
|
converted_files_paths.append(converted_path)
|
537
578
|
|
@@ -252,7 +252,7 @@ def create_1000A_submitter_name_segment(patient_data, config, endpoint):
|
|
252
252
|
Returns:
|
253
253
|
list: A list containing the NM1 segment for the submitter name and the PER segment for contact information.
|
254
254
|
"""
|
255
|
-
endpoint_config = config
|
255
|
+
endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
|
256
256
|
submitter_id_qualifier = endpoint_config.get('submitter_id_qualifier', '46') # '46' for ETIN or 'XX' for NPI
|
257
257
|
submitter_name = endpoint_config.get('nm_103_value', 'DEFAULT NAME') # Default name if not in config
|
258
258
|
|
@@ -779,7 +779,12 @@ def _original_insurance_type_selection_logic(parsed_data):
|
|
779
779
|
|
780
780
|
# Retrieve insurance options with codes and descriptions
|
781
781
|
config, _ = MediLink_ConfigLoader.load_configuration()
|
782
|
-
|
782
|
+
try:
|
783
|
+
from MediCafe.core_utils import extract_medilink_config
|
784
|
+
medi = extract_medilink_config(config)
|
785
|
+
insurance_options = medi.get('insurance_options', {})
|
786
|
+
except Exception:
|
787
|
+
insurance_options = {}
|
783
788
|
|
784
789
|
# If COB library is available, augment options with Medicare codes (MB/MA/MC)
|
785
790
|
if COB is not None:
|
@@ -835,7 +840,7 @@ def _original_insurance_type_selection_logic(parsed_data):
|
|
835
840
|
# Constructs the NM1 segment for subscriber based on parsed data and configuration.
|
836
841
|
def create_nm1_subscriber_segment(config, parsed_data, endpoint):
|
837
842
|
if endpoint.lower() == 'optumedi':
|
838
|
-
entity_identifier_code = config
|
843
|
+
entity_identifier_code = config.get('endpoints', {}).get('OPTUMEDI', {}).get('subscriber_entity_code', 'IL')
|
839
844
|
else:
|
840
845
|
entity_identifier_code = 'IL' # Default value if endpoint is not 'optumedi'
|
841
846
|
|
@@ -928,6 +933,9 @@ def create_clm_and_related_segments(parsed_data, config, crosswalk):
|
|
928
933
|
"""
|
929
934
|
|
930
935
|
# FINAL LINE OF DEFENSE: Validate all claim data before creating segments
|
936
|
+
# Ensure crosswalk is a dict to prevent NoneType.get errors downstream
|
937
|
+
if not isinstance(crosswalk, dict):
|
938
|
+
crosswalk = {}
|
931
939
|
validated_data = validate_claim_data_for_837p(parsed_data, config, crosswalk)
|
932
940
|
|
933
941
|
segments = []
|
@@ -1019,7 +1027,7 @@ def get_endpoint_config(config, endpoint):
|
|
1019
1027
|
Returns:
|
1020
1028
|
dict: The configuration for the specified endpoint, or raises an error if not found.
|
1021
1029
|
"""
|
1022
|
-
endpoint_config = config
|
1030
|
+
endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), None)
|
1023
1031
|
|
1024
1032
|
if endpoint_config is None:
|
1025
1033
|
error_message = "Endpoint configuration for '{}' not found.".format(endpoint)
|
@@ -1071,7 +1079,7 @@ def create_interchange_header(config, endpoint, isa13):
|
|
1071
1079
|
Returns:
|
1072
1080
|
- Tuple containing the ISA segment, GS segment, and group control number (gs06).
|
1073
1081
|
"""
|
1074
|
-
endpoint_config = config
|
1082
|
+
endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
|
1075
1083
|
|
1076
1084
|
# Set defaults for ISA segment values
|
1077
1085
|
isa02 = isa04 = " " # Default value for ISA02 and ISA04
|
@@ -92,13 +92,27 @@ def submit_claims(detailed_patient_data_grouped_by_endpoint, config, crosswalk):
|
|
92
92
|
|
93
93
|
# Iterate through each endpoint and submit claims
|
94
94
|
for endpoint, patients_data in tqdm(detailed_patient_data_grouped_by_endpoint.items(), desc="Progress", unit="endpoint"):
|
95
|
+
# Debug context to trace NoneType.get issues early
|
96
|
+
try:
|
97
|
+
log("[submit_claims] Starting endpoint: {}".format(endpoint), level="INFO")
|
98
|
+
if patients_data is None:
|
99
|
+
log("[submit_claims] Warning: patients_data is None for endpoint {}".format(endpoint), level="WARNING")
|
100
|
+
else:
|
101
|
+
try:
|
102
|
+
log("[submit_claims] patients_data count: {}".format(len(patients_data)), level="DEBUG")
|
103
|
+
except Exception:
|
104
|
+
log("[submit_claims] patients_data length unavailable (type: {})".format(type(patients_data)), level="DEBUG")
|
105
|
+
except Exception:
|
106
|
+
pass
|
107
|
+
|
95
108
|
if not patients_data:
|
96
109
|
continue
|
97
110
|
|
98
111
|
# Determine the submission method (e.g., "winscp" or "api")
|
99
112
|
try:
|
100
113
|
method = cfg.get('endpoints', {}).get(endpoint, {}).get('submission_method', 'winscp')
|
101
|
-
except Exception:
|
114
|
+
except Exception as e:
|
115
|
+
log("[submit_claims] Error deriving submission method for endpoint {}: {}".format(endpoint, e), level="ERROR")
|
102
116
|
# Absolute fallback if cfg was unexpectedly not a dict
|
103
117
|
method = 'winscp'
|
104
118
|
|
@@ -116,7 +130,32 @@ def submit_claims(detailed_patient_data_grouped_by_endpoint, config, crosswalk):
|
|
116
130
|
print("Error: Unable to create API client")
|
117
131
|
continue
|
118
132
|
# Process files per endpoint
|
119
|
-
|
133
|
+
try:
|
134
|
+
# Sanitize patient data structure before conversion
|
135
|
+
safe_patients = []
|
136
|
+
if isinstance(patients_data, list):
|
137
|
+
safe_patients = [item for item in patients_data if isinstance(item, dict)]
|
138
|
+
elif isinstance(patients_data, dict):
|
139
|
+
safe_patients = [patients_data]
|
140
|
+
else:
|
141
|
+
log("[submit_claims] Unexpected patients_data type for {}: {}".format(endpoint, type(patients_data)), level="ERROR")
|
142
|
+
safe_patients = []
|
143
|
+
|
144
|
+
converted_files = MediLink_837p_encoder.convert_files_for_submission(safe_patients, config, crosswalk, client)
|
145
|
+
except Exception as e:
|
146
|
+
tb = traceback.format_exc()
|
147
|
+
# Log via logger (may fail if logger expects config); also print to stderr to guarantee visibility
|
148
|
+
try:
|
149
|
+
log("[submit_claims] convert_files_for_submission failed for endpoint {}: {}\nTraceback: {}".format(endpoint, e, tb), level="ERROR")
|
150
|
+
except Exception:
|
151
|
+
pass
|
152
|
+
try:
|
153
|
+
import sys as _sys
|
154
|
+
_sys.stderr.write("[submit_claims] convert_files_for_submission failed for endpoint {}: {}\n".format(endpoint, e))
|
155
|
+
_sys.stderr.write(tb + "\n")
|
156
|
+
except Exception:
|
157
|
+
pass
|
158
|
+
raise
|
120
159
|
if converted_files:
|
121
160
|
if method == 'winscp':
|
122
161
|
# Transmit files via WinSCP
|
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
|