medicafe 0.250822.2__py3-none-any.whl → 0.250909.0__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.
- MediBot/MediBot.py +11 -4
- MediBot/MediBot_Crosswalk_Library.py +16 -3
- MediBot/MediBot_Crosswalk_Utils.py +12 -2
- MediBot/MediBot_Preprocessor_lib.py +1821 -1728
- MediBot/MediBot_docx_decoder.py +14 -3
- MediBot/__init__.py +1 -1
- MediCafe/MediLink_ConfigLoader.py +12 -1
- MediCafe/__init__.py +1 -1
- MediCafe/api_core.py +116 -14
- MediCafe/core_utils.py +9 -4
- MediCafe/deductible_utils.py +1233 -0
- MediLink/MediLink_837p_encoder_library.py +123 -39
- MediLink/MediLink_Deductible.py +569 -555
- MediLink/MediLink_Deductible_Validator.py +9 -3
- MediLink/MediLink_Display_Utils.py +364 -2
- MediLink/MediLink_UI.py +20 -2
- MediLink/__init__.py +1 -1
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/METADATA +1 -1
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/RECORD +23 -27
- MediCafe/api_core_backup.py +0 -428
- MediLink/insurance_type_integration_test.py +0 -361
- MediLink/test_cob_library.py +0 -436
- MediLink/test_timing.py +0 -59
- MediLink/test_validation.py +0 -127
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/LICENSE +0 -0
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/WHEEL +0 -0
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/entry_points.txt +0 -0
- {medicafe-0.250822.2.dist-info → medicafe-0.250909.0.dist-info}/top_level.txt +0 -0
MediBot/MediBot_docx_decoder.py
CHANGED
@@ -80,7 +80,7 @@ _DAY_MAP = {
|
|
80
80
|
}
|
81
81
|
|
82
82
|
|
83
|
-
def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
83
|
+
def parse_docx(filepath, surgery_dates, capture_schedule_positions=False): # Accept surgery_dates as a parameter
|
84
84
|
if Document is None:
|
85
85
|
MediLink_ConfigLoader.log("docx module not available, cannot parse .docx files", level="WARNING")
|
86
86
|
return {}
|
@@ -99,6 +99,7 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
99
99
|
return {}
|
100
100
|
|
101
101
|
patient_data = OrderedDict() # Initialize OrderedDict to store data
|
102
|
+
schedule_positions = {} # NEW: Track patient order in schedule
|
102
103
|
MediLink_ConfigLoader.log("Extracting Date of Service from {}".format(filepath), level="DEBUG")
|
103
104
|
|
104
105
|
# TIMING: Start date extraction
|
@@ -134,7 +135,7 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
134
135
|
|
135
136
|
for table in doc.tables: # Iterate over tables in the document
|
136
137
|
tables_processed += 1
|
137
|
-
for row in table.rows:
|
138
|
+
for row_idx, row in enumerate(table.rows):
|
138
139
|
rows_processed += 1
|
139
140
|
cells = [cell.text.strip() for cell in row.cells]
|
140
141
|
if len(cells) > 4 and cells[3].startswith('#'):
|
@@ -152,6 +153,12 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
152
153
|
MediLink_ConfigLoader.log("Duplicate entry for patient ID {} on date {}. Skipping.".format(patient_id, date_of_service), level="WARNING")
|
153
154
|
else:
|
154
155
|
patient_data[patient_id][date_of_service] = [diagnosis_code, left_or_right_eye, femto_yes_or_no]
|
156
|
+
|
157
|
+
# NEW: Store schedule position if requested
|
158
|
+
if capture_schedule_positions:
|
159
|
+
if patient_id not in schedule_positions:
|
160
|
+
schedule_positions[patient_id] = {}
|
161
|
+
schedule_positions[patient_id][date_of_service] = row_idx
|
155
162
|
except Exception as e:
|
156
163
|
MediLink_ConfigLoader.log("Error processing row: {}. Error: {}".format(cells, e), level="ERROR")
|
157
164
|
|
@@ -183,7 +190,11 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
183
190
|
print(" * Validation: {:.3f}s".format(validation_duration))
|
184
191
|
print(" * Total: {:.3f}s".format(total_duration))
|
185
192
|
|
186
|
-
|
193
|
+
# Return both data structures if schedule positions were captured
|
194
|
+
if capture_schedule_positions:
|
195
|
+
return patient_data, schedule_positions
|
196
|
+
else:
|
197
|
+
return patient_data
|
187
198
|
|
188
199
|
|
189
200
|
def validate_unknown_entries(patient_data):
|
MediBot/__init__.py
CHANGED
@@ -32,7 +32,18 @@ def get_default_config():
|
|
32
32
|
'logging': {
|
33
33
|
'level': 'INFO',
|
34
34
|
'console_output': True
|
35
|
-
}
|
35
|
+
},
|
36
|
+
# STRATEGIC NOTE (COB Configuration): COB library is fully implemented and ready
|
37
|
+
# To enable COB functionality, add the following configuration:
|
38
|
+
# 'cob_settings': {
|
39
|
+
# 'enabled': False, # Set to True to activate COB processing
|
40
|
+
# 'medicare_payer_ids': ['00850', 'MEDICARE', 'CMS', 'MCARE'],
|
41
|
+
# 'cob_mode': 'single_payer_only', # or 'multi_payer_supported'
|
42
|
+
# 'validation_level': 3, # SNIP level 3+ recommended for COB
|
43
|
+
# 'medicare_advantage_identifiers': ['MA', 'MC'],
|
44
|
+
# 'default_medicare_type': 'MB',
|
45
|
+
# 'require_835_validation': False # Set True for production
|
46
|
+
# }
|
36
47
|
}
|
37
48
|
}
|
38
49
|
|
MediCafe/__init__.py
CHANGED
MediCafe/api_core.py
CHANGED
@@ -118,10 +118,107 @@ except ImportError:
|
|
118
118
|
class TokenCache:
|
119
119
|
def __init__(self):
|
120
120
|
self.tokens = {}
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
121
|
+
|
122
|
+
# -----------------------------------------------------------------------------
|
123
|
+
# Endpoint-specific payer ID management (crosswalk-backed with hardcoded default)
|
124
|
+
# -----------------------------------------------------------------------------
|
125
|
+
# Intent:
|
126
|
+
# - Validate payer IDs against the endpoint actually being called.
|
127
|
+
# - Persist endpoint-specific payer ID lists into the crosswalk so they can be
|
128
|
+
# updated over time without changing code.
|
129
|
+
# - For OPTUMAI: use the augmented list (includes LIFE1, WELM2, etc.).
|
130
|
+
# - For UHCAPI (including its Super Connector fallback): strictly enforce the
|
131
|
+
# known-good UHC payer IDs only.
|
132
|
+
# - Future: OPTUMAI will expose a dedicated endpoint that returns its current
|
133
|
+
# valid payer list. When available, this function should fetch and refresh the
|
134
|
+
# crosswalk entry automatically (likely weekly/monthly), replacing the
|
135
|
+
# hardcoded default below. The UHCAPI Super Connector will eventually be
|
136
|
+
# deprecated; when removed, cleanup the UHC-specific paths accordingly.
|
137
|
+
|
138
|
+
try:
|
139
|
+
# Prefer using existing crosswalk persistence utilities
|
140
|
+
from MediBot.MediBot_Crosswalk_Utils import ensure_full_config_loaded, save_crosswalk
|
141
|
+
except Exception:
|
142
|
+
ensure_full_config_loaded = None
|
143
|
+
save_crosswalk = None
|
144
|
+
|
145
|
+
def _get_default_endpoint_payer_ids(endpoint_name):
|
146
|
+
"""
|
147
|
+
Return hardcoded default payer IDs for a given endpoint.
|
148
|
+
|
149
|
+
NOTE: Defaults are used when crosswalk does not yet contain a list.
|
150
|
+
"""
|
151
|
+
# UHC-only list – keep STRICT. Do not augment with non-UHC payers.
|
152
|
+
uhc_payer_ids = [
|
153
|
+
"87726", "03432", "96385", "95467", "86050", "86047", "95378", "06111", "37602"
|
154
|
+
]
|
155
|
+
|
156
|
+
# OPTUMAI – augmented list (subject to growth once the API adds a payer-list endpoint)
|
157
|
+
optumai_payer_ids = [
|
158
|
+
"87726", "06111", "25463", "37602", "39026", "74227", "65088", "81400",
|
159
|
+
"03432", "86050", "86047", "95378", "95467", "LIFE1", "WELM2"
|
160
|
+
]
|
161
|
+
|
162
|
+
if endpoint_name == 'OPTUMAI':
|
163
|
+
return optumai_payer_ids
|
164
|
+
# Default to UHCAPI for any other endpoint name
|
165
|
+
return uhc_payer_ids
|
166
|
+
|
167
|
+
def get_valid_payer_ids_for_endpoint(client, endpoint_name):
|
168
|
+
"""
|
169
|
+
Resolve the valid payer IDs for a specific endpoint using crosswalk storage
|
170
|
+
with a safe fallback to hardcoded defaults.
|
171
|
+
|
172
|
+
Behavior:
|
173
|
+
- Attempts to read crosswalk['endpoint_payer_ids'][endpoint_name].
|
174
|
+
- If missing, initializes with hardcoded defaults and persists to crosswalk
|
175
|
+
(non-interactive) so that future sessions use the saved list.
|
176
|
+
- Future: For OPTUMAI, replace the hardcoded default by calling the API's
|
177
|
+
payer-list endpoint once available, then update the crosswalk.
|
178
|
+
"""
|
179
|
+
try:
|
180
|
+
# Load full config + crosswalk (non-destructive)
|
181
|
+
base_config = None
|
182
|
+
crosswalk = None
|
183
|
+
if ensure_full_config_loaded is not None:
|
184
|
+
base_config, crosswalk = ensure_full_config_loaded(
|
185
|
+
getattr(client, 'config', None),
|
186
|
+
getattr(client, 'crosswalk', None)
|
187
|
+
)
|
188
|
+
else:
|
189
|
+
# Fallback: attempt to load via MediLink_ConfigLoader directly
|
190
|
+
# If we reach this fallback, it means ensure_full_config_loaded is not available.
|
191
|
+
# This is unexpected in normal operation and should be alerted.
|
192
|
+
print("Warning: IN api_core, ensure_full_config_loaded is not available; falling back to MediLink_ConfigLoader.load_configuration().")
|
193
|
+
MediLink_ConfigLoader.log(
|
194
|
+
"Fallback: ensure_full_config_loaded not available in get_valid_payer_ids_for_endpoint; using MediLink_ConfigLoader.load_configuration().",
|
195
|
+
level="WARNING"
|
196
|
+
)
|
197
|
+
base_config, crosswalk = MediLink_ConfigLoader.load_configuration()
|
198
|
+
|
199
|
+
# Extract any existing stored list
|
200
|
+
cw_ep = crosswalk.get('endpoint_payer_ids', {}) if isinstance(crosswalk, dict) else {}
|
201
|
+
existing = cw_ep.get(endpoint_name)
|
202
|
+
if isinstance(existing, list) and len(existing) > 0:
|
203
|
+
return existing
|
204
|
+
|
205
|
+
# Initialize from defaults and persist to crosswalk
|
206
|
+
defaults = _get_default_endpoint_payer_ids(endpoint_name)
|
207
|
+
if isinstance(crosswalk, dict):
|
208
|
+
if 'endpoint_payer_ids' not in crosswalk:
|
209
|
+
crosswalk['endpoint_payer_ids'] = {}
|
210
|
+
crosswalk['endpoint_payer_ids'][endpoint_name] = list(defaults)
|
211
|
+
|
212
|
+
# Persist without interactive prompts; ignore errors silently to avoid breaking flows
|
213
|
+
if save_crosswalk is not None:
|
214
|
+
try:
|
215
|
+
save_crosswalk(client, base_config, crosswalk, skip_api_operations=True)
|
216
|
+
except Exception:
|
217
|
+
pass
|
218
|
+
return defaults
|
219
|
+
except Exception:
|
220
|
+
# As a last resort, return a safe default for the endpoint
|
221
|
+
return _get_default_endpoint_payer_ids(endpoint_name)
|
125
222
|
|
126
223
|
class BaseAPIClient:
|
127
224
|
def __init__(self, config):
|
@@ -792,12 +889,14 @@ def get_eligibility_v3(client, payer_id, provider_last_name, search_option, date
|
|
792
889
|
if not all([client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi]):
|
793
890
|
raise ValueError("All required parameters must have values: client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi")
|
794
891
|
|
795
|
-
#
|
796
|
-
valid_payer_ids = ["87726", "06111", "25463", "37602", "39026", "74227", "65088", "81400", "03432", "86050", "86047", "95378", "95467"]
|
797
|
-
if payer_id not in valid_payer_ids:
|
798
|
-
raise ValueError("Invalid payer_id: {}. Must be one of: {}".format(payer_id, ", ".join(valid_payer_ids)))
|
799
|
-
|
892
|
+
# Endpoint is UHCAPI for this v3 REST call
|
800
893
|
endpoint_name = 'UHCAPI'
|
894
|
+
|
895
|
+
# Validate payer_id strictly against UHC list
|
896
|
+
valid_payer_ids = get_valid_payer_ids_for_endpoint(client, endpoint_name)
|
897
|
+
if payer_id not in valid_payer_ids:
|
898
|
+
raise ValueError("Invalid payer_id: {} for endpoint {}. Must be one of: {}".format(
|
899
|
+
payer_id, endpoint_name, ", ".join(valid_payer_ids)))
|
801
900
|
from MediCafe.core_utils import extract_medilink_config
|
802
901
|
medi = extract_medilink_config(client.config)
|
803
902
|
url_extension = medi.get('endpoints', {}).get(endpoint_name, {}).get('additional_endpoints', {}).get('eligibility_v3', '')
|
@@ -856,11 +955,6 @@ def get_eligibility_super_connector(client, payer_id, provider_last_name, search
|
|
856
955
|
if not all([client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi]):
|
857
956
|
raise ValueError("All required parameters must have values: client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi")
|
858
957
|
|
859
|
-
# Validate payer_id
|
860
|
-
valid_payer_ids = ["87726", "06111", "25463", "37602", "39026", "74227", "65088", "81400", "03432", "86050", "86047", "95378", "95467"]
|
861
|
-
if payer_id not in valid_payer_ids:
|
862
|
-
raise ValueError("Invalid payer_id: {}. Must be one of: {}".format(payer_id, ", ".join(valid_payer_ids)))
|
863
|
-
|
864
958
|
# Prefer OPTUMAI endpoint if configured, otherwise fall back to legacy UHCAPI super connector
|
865
959
|
try:
|
866
960
|
endpoints_cfg = client.config['MediLink_Config']['endpoints']
|
@@ -889,6 +983,14 @@ def get_eligibility_super_connector(client, payer_id, provider_last_name, search
|
|
889
983
|
except Exception:
|
890
984
|
url_extension = None
|
891
985
|
|
986
|
+
# Validate payer_id against the selected endpoint's list
|
987
|
+
# - If OPTUMAI is used, allow the augmented list (includes LIFE1, WELM2, etc.).
|
988
|
+
# - If UHCAPI fallback is used, enforce strict UHC list only.
|
989
|
+
valid_payer_ids = get_valid_payer_ids_for_endpoint(client, endpoint_name)
|
990
|
+
if payer_id not in valid_payer_ids:
|
991
|
+
raise ValueError("Invalid payer_id: {} for endpoint {}. Must be one of: {}".format(
|
992
|
+
payer_id, endpoint_name, ", ".join(valid_payer_ids)))
|
993
|
+
|
892
994
|
if not url_extension:
|
893
995
|
raise ValueError("Eligibility endpoint not configured for {}".format(endpoint_name))
|
894
996
|
|
MediCafe/core_utils.py
CHANGED
@@ -499,9 +499,7 @@ def get_api_client_factory():
|
|
499
499
|
"""
|
500
500
|
# Try multiple import paths for factory
|
501
501
|
import_specs = [
|
502
|
-
('MediCafe.api_factory', 'APIClientFactory')
|
503
|
-
('MediLink.MediLink_API_Factory', 'APIClientFactory'), # Legacy fallback
|
504
|
-
('MediLink_API_Factory', 'APIClientFactory') # Legacy fallback
|
502
|
+
('MediCafe.api_factory', 'APIClientFactory')
|
505
503
|
]
|
506
504
|
|
507
505
|
APIClientFactory = import_with_alternatives(import_specs)
|
@@ -728,4 +726,11 @@ def check_ascii_files(paths):
|
|
728
726
|
except Exception:
|
729
727
|
# If a failure occurs mid-scan, return what we have so far
|
730
728
|
return problematic
|
731
|
-
return problematic
|
729
|
+
return problematic
|
730
|
+
|
731
|
+
def sanitize_log(message):
|
732
|
+
# Simple masking: replace DOB-like with ****-**-**, IDs with last4
|
733
|
+
import re
|
734
|
+
message = re.sub(r'\d{4}-\d{2}-\d{2}', '****-**-**', message) # DOB
|
735
|
+
message = re.sub(r'\d{9,}', lambda m: '***' + m.group(0)[-4:], message) # IDs
|
736
|
+
return message
|