medicafe 0.250813.2__py3-none-any.whl → 0.250814.3__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.bat +1 -1
- MediBot/MediBot.py +1 -1
- MediBot/MediBot_Preprocessor_lib.py +21 -5
- MediBot/MediBot_UI.py +13 -1
- MediBot/__init__.py +1 -1
- MediBot/update_medicafe.py +69 -7
- MediCafe/__init__.py +1 -1
- MediLink/__init__.py +1 -1
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/METADATA +1 -1
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/RECORD +14 -27
- MediBot/MediPost.py +0 -5
- MediBot/PDF_to_CSV_Cleaner.py +0 -211
- MediLink/MediLink.py +0 -623
- MediLink/MediLink_277_decoder.py +0 -92
- MediLink/MediLink_API_v2.py +0 -176
- MediLink/MediLink_API_v3.py +0 -910
- MediLink/MediLink_APIs.py +0 -138
- MediLink/MediLink_ConfigLoader.py +0 -87
- MediLink/MediLink_ERA_decoder.py +0 -192
- MediLink/MediLink_GraphQL.py +0 -445
- MediLink/MediLink_StatusCheck.py +0 -0
- MediLink/MediLink_api_utils.py +0 -305
- MediLink/MediLink_batch.bat +0 -7
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/LICENSE +0 -0
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/WHEEL +0 -0
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/entry_points.txt +0 -0
- {medicafe-0.250813.2.dist-info → medicafe-0.250814.3.dist-info}/top_level.txt +0 -0
MediLink/MediLink_API_v3.py
DELETED
@@ -1,910 +0,0 @@
|
|
1
|
-
# api_core.py
|
2
|
-
import time, requests, yaml, json, os, traceback
|
3
|
-
|
4
|
-
try:
|
5
|
-
from MediLink import MediLink_ConfigLoader
|
6
|
-
from MediLink import MediLink_GraphQL
|
7
|
-
except ImportError:
|
8
|
-
import MediLink_ConfigLoader
|
9
|
-
import MediLink_GraphQL
|
10
|
-
|
11
|
-
"""
|
12
|
-
TODO At some point it might make sense to test their acknoledgment endpoint. body is transactionId.
|
13
|
-
This API is used to extract the claim acknowledgement details for the given transactionid which was
|
14
|
-
generated for 837 requests in claim submission process. Claims Acknowledgement (277CA) will provide
|
15
|
-
a status of claim-level acknowledgement of all claims received in the front-end processing system and
|
16
|
-
adjudication system.
|
17
|
-
"""
|
18
|
-
|
19
|
-
class ConfigLoader:
|
20
|
-
@staticmethod
|
21
|
-
def load_configuration(config_path=os.path.join(os.path.dirname(__file__), '..', 'json', 'config.json'),
|
22
|
-
crosswalk_path=os.path.join(os.path.dirname(__file__), '..', 'json', 'crosswalk.json')):
|
23
|
-
return MediLink_ConfigLoader.load_configuration(config_path, crosswalk_path)
|
24
|
-
|
25
|
-
@staticmethod
|
26
|
-
def load_swagger_file(swagger_path):
|
27
|
-
try:
|
28
|
-
print("Attempting to load Swagger file: {}".format(swagger_path))
|
29
|
-
with open(swagger_path, 'r') as swagger_file:
|
30
|
-
if swagger_path.endswith('.yaml') or swagger_path.endswith('.yml'):
|
31
|
-
print("Parsing YAML file: {}".format(swagger_path))
|
32
|
-
swagger_data = yaml.safe_load(swagger_file)
|
33
|
-
elif swagger_path.endswith('.json'):
|
34
|
-
print("Parsing JSON file: {}".format(swagger_path))
|
35
|
-
swagger_data = json.load(swagger_file)
|
36
|
-
else:
|
37
|
-
raise ValueError("Unsupported Swagger file format.")
|
38
|
-
print("Successfully loaded Swagger file: {}".format(swagger_path))
|
39
|
-
return swagger_data
|
40
|
-
except ValueError as e:
|
41
|
-
print("Error parsing Swagger file {}: {}".format(swagger_path, e))
|
42
|
-
MediLink_ConfigLoader.log("Error parsing Swagger file {}: {}".format(swagger_path, e), level="ERROR")
|
43
|
-
except FileNotFoundError:
|
44
|
-
print("Swagger file not found: {}".format(swagger_path))
|
45
|
-
MediLink_ConfigLoader.log("Swagger file not found: {}".format(swagger_path), level="ERROR")
|
46
|
-
except Exception as e:
|
47
|
-
print("Unexpected error loading Swagger file {}: {}".format(swagger_path, e))
|
48
|
-
MediLink_ConfigLoader.log("Unexpected error loading Swagger file {}: {}".format(swagger_path, e), level="ERROR")
|
49
|
-
return None
|
50
|
-
|
51
|
-
# Function to ensure numeric type
|
52
|
-
def ensure_numeric(value):
|
53
|
-
if isinstance(value, str):
|
54
|
-
try:
|
55
|
-
value = float(value)
|
56
|
-
except ValueError:
|
57
|
-
raise ValueError("Cannot convert {} to a numeric type".format(value))
|
58
|
-
return value
|
59
|
-
|
60
|
-
class TokenCache:
|
61
|
-
def __init__(self):
|
62
|
-
self.tokens = {}
|
63
|
-
|
64
|
-
def get(self, endpoint_name, current_time):
|
65
|
-
token_info = self.tokens.get(endpoint_name, {})
|
66
|
-
if token_info:
|
67
|
-
expires_at = token_info['expires_at']
|
68
|
-
# Log cache hit and expiration time
|
69
|
-
log_message = "Token for {} expires at {}. Current time: {}".format(endpoint_name, expires_at, current_time)
|
70
|
-
MediLink_ConfigLoader.log(log_message, level="DEBUG")
|
71
|
-
|
72
|
-
if expires_at > current_time:
|
73
|
-
return token_info['access_token']
|
74
|
-
|
75
|
-
# Log cache miss
|
76
|
-
# Token refresh flow validation has been implemented in get_access_token() to prevent unnecessary token pickup
|
77
|
-
log_message = "No valid token found for {}".format(endpoint_name)
|
78
|
-
MediLink_ConfigLoader.log(log_message, level="INFO")
|
79
|
-
|
80
|
-
return None
|
81
|
-
|
82
|
-
def set(self, endpoint_name, access_token, expires_in, current_time):
|
83
|
-
current_time = ensure_numeric(current_time)
|
84
|
-
expires_in = ensure_numeric(expires_in)
|
85
|
-
|
86
|
-
# Log the expires_in value to debug
|
87
|
-
log_message = "Token expires in: {} seconds for {}".format(expires_in, endpoint_name)
|
88
|
-
MediLink_ConfigLoader.log(log_message, level="INFO")
|
89
|
-
|
90
|
-
# Adjust expiration time by subtracting a buffer of 120 seconds
|
91
|
-
expires_at = current_time + expires_in - 120
|
92
|
-
log_message = "Setting token for {}. Expires at: {}".format(endpoint_name, expires_at)
|
93
|
-
MediLink_ConfigLoader.log(log_message, level="INFO")
|
94
|
-
|
95
|
-
self.tokens[endpoint_name] = {
|
96
|
-
'access_token': access_token,
|
97
|
-
'expires_at': expires_at
|
98
|
-
}
|
99
|
-
|
100
|
-
class BaseAPIClient:
|
101
|
-
def __init__(self, config):
|
102
|
-
self.config = config
|
103
|
-
self.token_cache = TokenCache()
|
104
|
-
|
105
|
-
def get_access_token(self, endpoint_name):
|
106
|
-
raise NotImplementedError("Subclasses should implement this!")
|
107
|
-
|
108
|
-
def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
|
109
|
-
raise NotImplementedError("Subclasses should implement this!")
|
110
|
-
|
111
|
-
class APIClient(BaseAPIClient):
|
112
|
-
def __init__(self):
|
113
|
-
config, _ = MediLink_ConfigLoader.load_configuration()
|
114
|
-
super().__init__(config)
|
115
|
-
|
116
|
-
# Add enhanced features if available
|
117
|
-
try:
|
118
|
-
from MediLink_api_utils import APICircuitBreaker, APICache, APIRateLimiter
|
119
|
-
from MediLink_insurance_utils import get_feature_flag
|
120
|
-
|
121
|
-
# Initialize enhancements if enabled
|
122
|
-
enable_circuit_breaker = get_feature_flag('api_circuit_breaker', default=False)
|
123
|
-
enable_caching = get_feature_flag('api_caching', default=False)
|
124
|
-
enable_rate_limiting = get_feature_flag('api_rate_limiting', default=False)
|
125
|
-
|
126
|
-
self.circuit_breaker = APICircuitBreaker() if enable_circuit_breaker else None
|
127
|
-
self.api_cache = APICache() if enable_caching else None
|
128
|
-
self.rate_limiter = APIRateLimiter() if enable_rate_limiting else None
|
129
|
-
|
130
|
-
if any([enable_circuit_breaker, enable_caching, enable_rate_limiting]):
|
131
|
-
MediLink_ConfigLoader.log("Enhanced API client initialized with circuit_breaker={}, caching={}, rate_limiting={}".format(
|
132
|
-
enable_circuit_breaker, enable_caching, enable_rate_limiting), level="INFO")
|
133
|
-
except ImportError:
|
134
|
-
MediLink_ConfigLoader.log("API enhancements not available, using standard client", level="DEBUG")
|
135
|
-
self.circuit_breaker = None
|
136
|
-
self.api_cache = None
|
137
|
-
self.rate_limiter = None
|
138
|
-
|
139
|
-
def get_access_token(self, endpoint_name):
|
140
|
-
MediLink_ConfigLoader.log("[Get Access Token] Called for {}".format(endpoint_name), level="DEBUG")
|
141
|
-
current_time = time.time()
|
142
|
-
cached_token = self.token_cache.get(endpoint_name, current_time)
|
143
|
-
|
144
|
-
if cached_token:
|
145
|
-
expires_at = self.token_cache.tokens[endpoint_name]['expires_at']
|
146
|
-
MediLink_ConfigLoader.log("Cached token expires at {}".format(expires_at), level="DEBUG")
|
147
|
-
return cached_token
|
148
|
-
|
149
|
-
# Validate that we actually need a token before fetching
|
150
|
-
# Check if the endpoint configuration exists and is valid
|
151
|
-
try:
|
152
|
-
endpoint_config = self.config['MediLink_Config']['endpoints'][endpoint_name]
|
153
|
-
if not endpoint_config:
|
154
|
-
MediLink_ConfigLoader.log("No configuration found for endpoint: {}".format(endpoint_name), level="ERROR")
|
155
|
-
return None
|
156
|
-
|
157
|
-
# Validate required configuration fields
|
158
|
-
required_fields = ['token_url', 'client_id', 'client_secret']
|
159
|
-
missing_fields = [field for field in required_fields if field not in endpoint_config]
|
160
|
-
if missing_fields:
|
161
|
-
MediLink_ConfigLoader.log("Missing required configuration fields for {}: {}".format(endpoint_name, missing_fields), level="ERROR")
|
162
|
-
return None
|
163
|
-
|
164
|
-
except KeyError:
|
165
|
-
MediLink_ConfigLoader.log("Endpoint {} not found in configuration".format(endpoint_name), level="ERROR")
|
166
|
-
return None
|
167
|
-
except Exception as e:
|
168
|
-
MediLink_ConfigLoader.log("Error validating endpoint configuration for {}: {}".format(endpoint_name, str(e)), level="ERROR")
|
169
|
-
return None
|
170
|
-
|
171
|
-
# If no valid token, fetch a new one
|
172
|
-
token_url = endpoint_config['token_url']
|
173
|
-
data = {
|
174
|
-
'grant_type': 'client_credentials',
|
175
|
-
'client_id': endpoint_config['client_id'],
|
176
|
-
'client_secret': endpoint_config['client_secret']
|
177
|
-
}
|
178
|
-
|
179
|
-
# Add scope if specified in the configuration
|
180
|
-
if 'scope' in endpoint_config:
|
181
|
-
data['scope'] = endpoint_config['scope']
|
182
|
-
|
183
|
-
headers = {'Content-Type': 'application/x-www-form-urlencoded'}
|
184
|
-
|
185
|
-
try:
|
186
|
-
response = requests.post(token_url, headers=headers, data=data)
|
187
|
-
response.raise_for_status()
|
188
|
-
token_data = response.json()
|
189
|
-
access_token = token_data['access_token']
|
190
|
-
expires_in = token_data.get('expires_in', 3600)
|
191
|
-
|
192
|
-
self.token_cache.set(endpoint_name, access_token, expires_in, current_time)
|
193
|
-
MediLink_ConfigLoader.log("Obtained NEW token for endpoint: {}".format(endpoint_name), level="INFO")
|
194
|
-
return access_token
|
195
|
-
except requests.exceptions.RequestException as e:
|
196
|
-
MediLink_ConfigLoader.log("Failed to obtain token for {}: {}".format(endpoint_name, str(e)), level="ERROR")
|
197
|
-
return None
|
198
|
-
except (KeyError, ValueError) as e:
|
199
|
-
MediLink_ConfigLoader.log("Invalid token response for {}: {}".format(endpoint_name, str(e)), level="ERROR")
|
200
|
-
return None
|
201
|
-
|
202
|
-
def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
|
203
|
-
# Try enhanced API call if available
|
204
|
-
if hasattr(self, 'circuit_breaker') and self.circuit_breaker:
|
205
|
-
try:
|
206
|
-
return self._make_enhanced_api_call(endpoint_name, call_type, url_extension, params, data, headers)
|
207
|
-
except Exception as e:
|
208
|
-
MediLink_ConfigLoader.log("Enhanced API call failed, falling back to standard: {}".format(str(e)), level="WARNING")
|
209
|
-
|
210
|
-
# Standard API call logic
|
211
|
-
return self._make_standard_api_call(endpoint_name, call_type, url_extension, params, data, headers)
|
212
|
-
|
213
|
-
def _make_enhanced_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
|
214
|
-
"""Enhanced API call with circuit breaker, caching, and rate limiting"""
|
215
|
-
# Check cache first (for GET requests)
|
216
|
-
if self.api_cache and call_type == 'GET':
|
217
|
-
cached_result = self.api_cache.get(endpoint_name, call_type, url_extension, params)
|
218
|
-
if cached_result is not None:
|
219
|
-
MediLink_ConfigLoader.log("Cache hit for {} {} {}".format(call_type, endpoint_name, url_extension), level="DEBUG")
|
220
|
-
return cached_result
|
221
|
-
|
222
|
-
# Check rate limits
|
223
|
-
if self.rate_limiter:
|
224
|
-
self.rate_limiter.wait_if_needed()
|
225
|
-
|
226
|
-
# Make call with circuit breaker protection
|
227
|
-
result = self.circuit_breaker.call_with_breaker(
|
228
|
-
self._make_standard_api_call, endpoint_name, call_type, url_extension, params, data, headers)
|
229
|
-
|
230
|
-
# Record rate limit call
|
231
|
-
if self.rate_limiter:
|
232
|
-
self.rate_limiter.record_call()
|
233
|
-
|
234
|
-
# Cache result (for GET requests)
|
235
|
-
if self.api_cache and call_type == 'GET':
|
236
|
-
self.api_cache.set(result, endpoint_name, call_type, url_extension, params)
|
237
|
-
|
238
|
-
return result
|
239
|
-
|
240
|
-
def _make_standard_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
|
241
|
-
"""Standard API call logic preserved for compatibility"""
|
242
|
-
token = self.get_access_token(endpoint_name)
|
243
|
-
if token:
|
244
|
-
MediLink_ConfigLoader.log("[Make API Call] Token found for {}".format(endpoint_name), level="DEBUG")
|
245
|
-
else:
|
246
|
-
MediLink_ConfigLoader.log("[Make API Call] No token obtained for {}".format(endpoint_name), level="ERROR")
|
247
|
-
raise ValueError("No access token available for endpoint: {}".format(endpoint_name))
|
248
|
-
|
249
|
-
if headers is None:
|
250
|
-
headers = {}
|
251
|
-
headers.update({'Authorization': 'Bearer {}'.format(token), 'Accept': 'application/json'})
|
252
|
-
base_url = self.config['MediLink_Config']['endpoints'][endpoint_name]['api_url']
|
253
|
-
url = base_url + url_extension
|
254
|
-
|
255
|
-
try:
|
256
|
-
masked_headers = headers.copy()
|
257
|
-
if 'Authorization' in masked_headers:
|
258
|
-
masked_headers['Authorization'] = 'Bearer ***'
|
259
|
-
|
260
|
-
def make_request():
|
261
|
-
if call_type == 'GET':
|
262
|
-
return requests.get(url, headers=headers, params=params)
|
263
|
-
elif call_type == 'POST':
|
264
|
-
# Check if there are custom headers (any headers beyond Authorization and Accept)
|
265
|
-
custom_headers = {k: v for k, v in headers.items() if k not in ['Authorization', 'Accept']}
|
266
|
-
|
267
|
-
if custom_headers:
|
268
|
-
# Log that custom headers were detected
|
269
|
-
MediLink_ConfigLoader.log("Custom headers detected: {}".format(custom_headers), level="DEBUG")
|
270
|
-
else:
|
271
|
-
# Set default Content-Type if no custom headers
|
272
|
-
headers['Content-Type'] = 'application/json'
|
273
|
-
return requests.post(url, headers=headers, json=data)
|
274
|
-
elif call_type == 'DELETE':
|
275
|
-
return requests.delete(url, headers=headers)
|
276
|
-
else:
|
277
|
-
raise ValueError("Unsupported call type: {}".format(call_type))
|
278
|
-
|
279
|
-
# Make initial request
|
280
|
-
response = make_request()
|
281
|
-
|
282
|
-
# If we get a 5xx error, wait and retry once
|
283
|
-
if 500 <= response.status_code < 600:
|
284
|
-
error_msg = "Received {} error from server for {} request to {}. Waiting 1 second before retry...".format(
|
285
|
-
response.status_code, call_type, url
|
286
|
-
)
|
287
|
-
MediLink_ConfigLoader.log(error_msg, level="WARNING")
|
288
|
-
|
289
|
-
# Add more verbose logging for 504 errors specifically
|
290
|
-
if response.status_code == 504:
|
291
|
-
MediLink_ConfigLoader.log(
|
292
|
-
"504 Gateway Timeout detected. This usually indicates the server is overloaded or taking too long to respond. "
|
293
|
-
"Retrying after 1 second delay...",
|
294
|
-
level="WARNING"
|
295
|
-
)
|
296
|
-
|
297
|
-
time.sleep(1)
|
298
|
-
response = make_request()
|
299
|
-
|
300
|
-
# Log the retry result
|
301
|
-
if response.status_code == 200:
|
302
|
-
MediLink_ConfigLoader.log(
|
303
|
-
"Retry successful! Request to {} now returned 200 status code.".format(url),
|
304
|
-
level="INFO"
|
305
|
-
)
|
306
|
-
else:
|
307
|
-
MediLink_ConfigLoader.log(
|
308
|
-
"Retry failed. Request to {} still returned {} status code.".format(url, response.status_code),
|
309
|
-
level="ERROR"
|
310
|
-
)
|
311
|
-
|
312
|
-
# Raise an HTTPError if the response was unsuccessful
|
313
|
-
response.raise_for_status()
|
314
|
-
|
315
|
-
return response.json()
|
316
|
-
|
317
|
-
except requests.exceptions.HTTPError as http_err:
|
318
|
-
# If http_err.response is None, handle it separately
|
319
|
-
if http_err.response is None:
|
320
|
-
log_message = (
|
321
|
-
"HTTPError with no response. "
|
322
|
-
"URL: {url}, "
|
323
|
-
"Method: {method}, "
|
324
|
-
"Params: {params}, "
|
325
|
-
"Data: {data}, "
|
326
|
-
"Headers: {masked_headers}, "
|
327
|
-
"Error: {error}"
|
328
|
-
).format(
|
329
|
-
url=url,
|
330
|
-
method=call_type,
|
331
|
-
params=params,
|
332
|
-
data=data,
|
333
|
-
masked_headers=masked_headers,
|
334
|
-
error=str(http_err)
|
335
|
-
)
|
336
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
337
|
-
else:
|
338
|
-
# Extract status code and full response content
|
339
|
-
status_code = http_err.response.status_code
|
340
|
-
try:
|
341
|
-
# Try to log the JSON content if available
|
342
|
-
response_content = http_err.response.json()
|
343
|
-
except ValueError:
|
344
|
-
# Fallback to raw text if JSON decoding fails
|
345
|
-
response_content = http_err.response.text
|
346
|
-
|
347
|
-
log_message = (
|
348
|
-
"HTTPError: Status Code: {status}, "
|
349
|
-
"URL: {url}, "
|
350
|
-
"Method: {method}, "
|
351
|
-
"Params: {params}, "
|
352
|
-
"Data: {data}, "
|
353
|
-
"Headers: {masked_headers}, "
|
354
|
-
"Response Content: {content}"
|
355
|
-
).format(
|
356
|
-
status=status_code,
|
357
|
-
url=url,
|
358
|
-
method=call_type,
|
359
|
-
params=params,
|
360
|
-
data=data,
|
361
|
-
masked_headers=masked_headers,
|
362
|
-
content=response_content
|
363
|
-
)
|
364
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
365
|
-
raise
|
366
|
-
|
367
|
-
except requests.exceptions.RequestException as req_err:
|
368
|
-
# Log connection-related issues or other request exceptions
|
369
|
-
log_message = (
|
370
|
-
"RequestException: No response received. "
|
371
|
-
"URL: {url}, "
|
372
|
-
"Method: {method}, "
|
373
|
-
"Params: {params}, "
|
374
|
-
"Data: {data}, "
|
375
|
-
"Headers: {masked_headers}, "
|
376
|
-
"Error: {error}"
|
377
|
-
).format(
|
378
|
-
url=url,
|
379
|
-
method=call_type,
|
380
|
-
params=params,
|
381
|
-
data=data,
|
382
|
-
masked_headers=masked_headers,
|
383
|
-
error=str(req_err)
|
384
|
-
)
|
385
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
386
|
-
raise
|
387
|
-
|
388
|
-
except Exception as e:
|
389
|
-
# Capture traceback for unexpected exceptions
|
390
|
-
tb = traceback.format_exc()
|
391
|
-
log_message = (
|
392
|
-
"Unexpected error: {error}. "
|
393
|
-
"URL: {url}, "
|
394
|
-
"Method: {method}, "
|
395
|
-
"Params: {params}, "
|
396
|
-
"Data: {data}, "
|
397
|
-
"Headers: {masked_headers}. "
|
398
|
-
"Traceback: {traceback}"
|
399
|
-
).format(
|
400
|
-
error=str(e),
|
401
|
-
url=url,
|
402
|
-
method=call_type,
|
403
|
-
params=params,
|
404
|
-
data=data,
|
405
|
-
masked_headers=masked_headers,
|
406
|
-
traceback=tb
|
407
|
-
)
|
408
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
409
|
-
raise
|
410
|
-
|
411
|
-
def fetch_payer_name_from_api(client, payer_id, config, primary_endpoint='AVAILITY'):
|
412
|
-
"""
|
413
|
-
Fetches the payer name using the provided APIClient instance.
|
414
|
-
|
415
|
-
:param client: An instance of APIClient
|
416
|
-
:param payer_id: The payer ID to fetch
|
417
|
-
:param primary_endpoint: The primary endpoint to use
|
418
|
-
:return: The payer name if found
|
419
|
-
"""
|
420
|
-
# Ensure client is an instance of APIClient
|
421
|
-
if not isinstance(client, APIClient):
|
422
|
-
error_message = "Invalid client provided. Expected an instance of APIClient."
|
423
|
-
print(error_message)
|
424
|
-
MediLink_ConfigLoader.log(error_message, level="ERROR")
|
425
|
-
exit(1) # Exit the script
|
426
|
-
|
427
|
-
# TODO: FUTURE IMPLEMENTATION - Remove AVAILITY default when other endpoints have payer-list APIs
|
428
|
-
# Currently defaulting to AVAILITY as it's the only endpoint with confirmed payer-list functionality
|
429
|
-
# In the future, this should be removed and the system should dynamically detect which endpoints
|
430
|
-
# have payer-list capabilities and use them accordingly.
|
431
|
-
if primary_endpoint != 'AVAILITY':
|
432
|
-
MediLink_ConfigLoader.log("[Fetch payer name from API] Overriding {} with AVAILITY (default until multi-endpoint payer-list support is implemented).".format(primary_endpoint), level="DEBUG")
|
433
|
-
primary_endpoint = 'AVAILITY'
|
434
|
-
|
435
|
-
try:
|
436
|
-
endpoints = config['MediLink_Config']['endpoints']
|
437
|
-
except KeyError as e:
|
438
|
-
error_message = "Configuration loading error in fetch_payer_name_from_api: Missing key {0}... Attempting to reload configuration.".format(e)
|
439
|
-
# print(error_message)
|
440
|
-
MediLink_ConfigLoader.log(error_message, level="ERROR")
|
441
|
-
# Attempt to reload configuration if key is missing
|
442
|
-
config, _ = MediLink_ConfigLoader.load_configuration()
|
443
|
-
endpoints = config['MediLink_Config']['endpoints'] # Re-attempt to access endpoints
|
444
|
-
MediLink_ConfigLoader.log("Re-loaded configuration successfully.", level="INFO")
|
445
|
-
|
446
|
-
# Sanitize and validate payer_id
|
447
|
-
if not isinstance(payer_id, str):
|
448
|
-
payer_id = str(payer_id)
|
449
|
-
|
450
|
-
payer_id = ''.join(char for char in payer_id if char.isalnum())
|
451
|
-
|
452
|
-
if not payer_id:
|
453
|
-
error_message = "Invalid payer_id in API v3: {}. Must contain a string of alphanumeric characters.".format(payer_id)
|
454
|
-
MediLink_ConfigLoader.log(error_message, level="ERROR")
|
455
|
-
print(error_message)
|
456
|
-
|
457
|
-
# FUTURE IMPLEMENTATION: Dynamic endpoint selection based on payer-list availability
|
458
|
-
# This will replace the hardcoded AVAILITY default when other endpoints have payer-list APIs
|
459
|
-
# The logic should:
|
460
|
-
# 1. Check all endpoints for 'payer_list_endpoint' configuration
|
461
|
-
# 2. Prioritize endpoints that have confirmed payer-list functionality
|
462
|
-
# 3. Fall back to endpoints with basic payer lookup if available
|
463
|
-
# 4. Use AVAILITY as final fallback
|
464
|
-
|
465
|
-
# Define endpoint rotation logic with payer-list capability detection
|
466
|
-
available_endpoints = []
|
467
|
-
|
468
|
-
# Check which endpoints have payer-list functionality configured
|
469
|
-
for endpoint_name, endpoint_config in endpoints.items():
|
470
|
-
if 'payer_list_endpoint' in endpoint_config:
|
471
|
-
available_endpoints.append(endpoint_name)
|
472
|
-
MediLink_ConfigLoader.log("Found payer-list endpoint for {}: {}".format(endpoint_name, endpoint_config['payer_list_endpoint']), level="DEBUG")
|
473
|
-
|
474
|
-
# If no endpoints have payer-list configured, fall back to AVAILITY
|
475
|
-
if not available_endpoints:
|
476
|
-
MediLink_ConfigLoader.log("No endpoints with payer-list configuration found, using AVAILITY as fallback", level="INFO")
|
477
|
-
available_endpoints = ['AVAILITY']
|
478
|
-
|
479
|
-
# Prioritize the primary endpoint if it has payer-list capability
|
480
|
-
if primary_endpoint in available_endpoints:
|
481
|
-
endpoint_order = [primary_endpoint] + [ep for ep in available_endpoints if ep != primary_endpoint]
|
482
|
-
else:
|
483
|
-
# If primary endpoint doesn't have payer-list, use available endpoints in order
|
484
|
-
endpoint_order = available_endpoints
|
485
|
-
|
486
|
-
MediLink_ConfigLoader.log("Endpoint order for payer lookup: {}".format(endpoint_order), level="DEBUG")
|
487
|
-
|
488
|
-
for endpoint_name in endpoint_order:
|
489
|
-
try:
|
490
|
-
endpoint_url = endpoints[endpoint_name].get('payer_list_endpoint', '/availity-payer-list')
|
491
|
-
response = client.make_api_call(endpoint_name, 'GET', endpoint_url, {'payerId': payer_id})
|
492
|
-
|
493
|
-
# Check if response exists
|
494
|
-
if not response:
|
495
|
-
log_message = "No response from {0} for Payer ID {1}".format(endpoint_name, payer_id)
|
496
|
-
print(log_message)
|
497
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
498
|
-
continue
|
499
|
-
|
500
|
-
# Check if the status code is not 200
|
501
|
-
status_code = response.get('statuscode', 200)
|
502
|
-
if status_code != 200:
|
503
|
-
log_message = "Invalid response status code {0} from {1} for Payer ID {2}. Message: {3}".format(
|
504
|
-
status_code, endpoint_name, payer_id, response.get('message', 'No message'))
|
505
|
-
print(log_message)
|
506
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
507
|
-
continue
|
508
|
-
|
509
|
-
# Extract payers and validate the response structure
|
510
|
-
payers = response.get('payers', [])
|
511
|
-
if not payers:
|
512
|
-
log_message = "No payer found at {0} for ID {1}. Response: {2}".format(endpoint_name, payer_id, response)
|
513
|
-
print(log_message)
|
514
|
-
MediLink_ConfigLoader.log(log_message, level="INFO")
|
515
|
-
continue
|
516
|
-
|
517
|
-
# Extract the payer name from the first payer in the list
|
518
|
-
payer_name = payers[0].get('displayName') or payers[0].get('name')
|
519
|
-
if not payer_name:
|
520
|
-
log_message = "Payer name not found in the response from {0} for ID {1}. Response: {2}".format(
|
521
|
-
endpoint_name, payer_id, response)
|
522
|
-
print(log_message)
|
523
|
-
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
524
|
-
continue
|
525
|
-
|
526
|
-
# Log successful payer retrieval
|
527
|
-
log_message = "Found payer at {0} for ID {1}: {2}".format(endpoint_name, payer_id, payer_name)
|
528
|
-
MediLink_ConfigLoader.log(log_message, level="INFO")
|
529
|
-
return payer_name
|
530
|
-
|
531
|
-
except Exception as e:
|
532
|
-
error_message = "Error calling {0} for Payer ID {1}. Exception: {2}".format(endpoint_name, payer_id, e)
|
533
|
-
MediLink_ConfigLoader.log(error_message, level="INFO")
|
534
|
-
|
535
|
-
# If all endpoints fail
|
536
|
-
final_error_message = "All endpoints exhausted for Payer ID {0}.".format(payer_id)
|
537
|
-
print(final_error_message)
|
538
|
-
MediLink_ConfigLoader.log(final_error_message, level="CRITICAL")
|
539
|
-
raise ValueError(final_error_message)
|
540
|
-
|
541
|
-
def get_claim_summary_by_provider(client, tin, first_service_date, last_service_date, payer_id, get_standard_error='false'):
|
542
|
-
endpoint_name = 'UHCAPI'
|
543
|
-
url_extension = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['claim_summary_by_provider']
|
544
|
-
headers = {
|
545
|
-
'tin': tin,
|
546
|
-
'firstServiceDt': first_service_date,
|
547
|
-
'lastServiceDt': last_service_date,
|
548
|
-
'payerId': payer_id,
|
549
|
-
'getStandardError': get_standard_error,
|
550
|
-
'Accept': 'application/json'
|
551
|
-
}
|
552
|
-
return client.make_api_call(endpoint_name, 'GET', url_extension, params=None, data=None, headers=headers)
|
553
|
-
|
554
|
-
def get_eligibility(client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi):
|
555
|
-
endpoint_name = 'UHCAPI'
|
556
|
-
url_extension = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['eligibility']
|
557
|
-
url_extension = url_extension + '?payerID={}&providerLastName={}&searchOption={}&dateOfBirth={}&memberId={}&npi={}'.format(
|
558
|
-
payer_id, provider_last_name, search_option, date_of_birth, member_id, npi)
|
559
|
-
return client.make_api_call(endpoint_name, 'GET', url_extension)
|
560
|
-
|
561
|
-
def get_eligibility_v3(client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi,
|
562
|
-
first_name=None, last_name=None, payer_label=None, payer_name=None, service_start=None, service_end=None,
|
563
|
-
middle_name=None, gender=None, ssn=None, city=None, state=None, zip=None, group_number=None,
|
564
|
-
service_type_code=None, provider_first_name=None, tax_id_number=None, provider_name_id=None,
|
565
|
-
corporate_tax_owner_id=None, corporate_tax_owner_name=None, organization_name=None,
|
566
|
-
organization_id=None, identify_service_level_deductible=True):
|
567
|
-
|
568
|
-
# Ensure all required parameters have values
|
569
|
-
if not all([client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi]):
|
570
|
-
raise ValueError("All required parameters must have values: client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi")
|
571
|
-
|
572
|
-
# Validate payer_id
|
573
|
-
valid_payer_ids = ["87726", "06111", "25463", "37602", "39026", "74227", "65088", "81400", "03432", "86050", "86047", "95378", "95467"]
|
574
|
-
if payer_id not in valid_payer_ids:
|
575
|
-
raise ValueError("Invalid payer_id: {}. Must be one of: {}".format(payer_id, ", ".join(valid_payer_ids)))
|
576
|
-
|
577
|
-
endpoint_name = 'UHCAPI'
|
578
|
-
url_extension = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['eligibility_v3']
|
579
|
-
|
580
|
-
# Construct request body
|
581
|
-
body = {
|
582
|
-
"memberId": member_id,
|
583
|
-
"lastName": last_name,
|
584
|
-
"firstName": first_name,
|
585
|
-
"dateOfBirth": date_of_birth,
|
586
|
-
"payerID": payer_id,
|
587
|
-
"payerLabel": payer_label,
|
588
|
-
"payerName": payer_name,
|
589
|
-
"serviceStart": service_start,
|
590
|
-
"serviceEnd": service_end,
|
591
|
-
"middleName": middle_name,
|
592
|
-
"gender": gender,
|
593
|
-
"ssn": ssn,
|
594
|
-
"city": city,
|
595
|
-
"state": state,
|
596
|
-
"zip": zip,
|
597
|
-
"groupNumber": group_number,
|
598
|
-
"serviceTypeCode": service_type_code,
|
599
|
-
"providerLastName": provider_last_name,
|
600
|
-
"providerFirstName": provider_first_name,
|
601
|
-
"taxIdNumber": tax_id_number,
|
602
|
-
"providerNameID": provider_name_id,
|
603
|
-
"npi": npi,
|
604
|
-
"corporateTaxOwnerID": corporate_tax_owner_id,
|
605
|
-
"corporateTaxOwnerName": corporate_tax_owner_name,
|
606
|
-
"organizationName": organization_name,
|
607
|
-
"organizationID": organization_id,
|
608
|
-
"searchOption": search_option,
|
609
|
-
"identifyServiceLevelDeductible": identify_service_level_deductible
|
610
|
-
}
|
611
|
-
|
612
|
-
# Remove None values from the body
|
613
|
-
body = {k: v for k, v in body.items() if v is not None}
|
614
|
-
|
615
|
-
# Log the request body
|
616
|
-
MediLink_ConfigLoader.log("Request body: {}".format(json.dumps(body, indent=4)), level="DEBUG")
|
617
|
-
|
618
|
-
return client.make_api_call(endpoint_name, 'POST', url_extension, params=None, data=body)
|
619
|
-
|
620
|
-
def get_eligibility_super_connector(client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi,
|
621
|
-
first_name=None, last_name=None, payer_label=None, payer_name=None, service_start=None, service_end=None,
|
622
|
-
middle_name=None, gender=None, ssn=None, city=None, state=None, zip=None, group_number=None,
|
623
|
-
service_type_code=None, provider_first_name=None, tax_id_number=None, provider_name_id=None,
|
624
|
-
corporate_tax_owner_id=None, corporate_tax_owner_name=None, organization_name=None,
|
625
|
-
organization_id=None, identify_service_level_deductible=True):
|
626
|
-
"""
|
627
|
-
GraphQL Super Connector version of eligibility check that maps to the same interface as get_eligibility_v3.
|
628
|
-
This function provides a drop-in replacement for the REST API with identical input/output behavior.
|
629
|
-
"""
|
630
|
-
# Ensure all required parameters have values
|
631
|
-
if not all([client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi]):
|
632
|
-
raise ValueError("All required parameters must have values: client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi")
|
633
|
-
|
634
|
-
# Validate payer_id
|
635
|
-
valid_payer_ids = ["87726", "06111", "25463", "37602", "39026", "74227", "65088", "81400", "03432", "86050", "86047", "95378", "95467"]
|
636
|
-
if payer_id not in valid_payer_ids:
|
637
|
-
raise ValueError("Invalid payer_id: {}. Must be one of: {}".format(payer_id, ", ".join(valid_payer_ids)))
|
638
|
-
|
639
|
-
endpoint_name = 'UHCAPI'
|
640
|
-
url_extension = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['eligibility_super_connector']
|
641
|
-
|
642
|
-
# Get provider TIN from config (using existing billing_provider_tin)
|
643
|
-
provider_tin = client.config['MediLink_Config'].get('billing_provider_tin')
|
644
|
-
if not provider_tin:
|
645
|
-
raise ValueError("Provider TIN not found in configuration")
|
646
|
-
|
647
|
-
# Construct GraphQL query variables using the consolidated module
|
648
|
-
graphql_variables = MediLink_GraphQL.build_eligibility_variables(
|
649
|
-
member_id=member_id,
|
650
|
-
date_of_birth=date_of_birth,
|
651
|
-
payer_id=payer_id,
|
652
|
-
provider_last_name=provider_last_name,
|
653
|
-
provider_npi=npi
|
654
|
-
)
|
655
|
-
|
656
|
-
# Validate NPI format (should be 10 digits)
|
657
|
-
if 'providerNPI' in graphql_variables:
|
658
|
-
npi_value = graphql_variables['providerNPI']
|
659
|
-
if not npi_value.isdigit() or len(npi_value) != 10:
|
660
|
-
MediLink_ConfigLoader.log("Warning: NPI '{}' is not 10 digits, but continuing anyway".format(npi_value), level="WARNING")
|
661
|
-
|
662
|
-
# Build GraphQL request using the consolidated module
|
663
|
-
# Hardcoded switch to use sample data for testing
|
664
|
-
USE_SAMPLE_DATA = False # Set to False to use constructed data
|
665
|
-
|
666
|
-
if USE_SAMPLE_DATA:
|
667
|
-
# Use the sample data from swagger documentation
|
668
|
-
graphql_body = MediLink_GraphQL.get_sample_eligibility_request()
|
669
|
-
MediLink_ConfigLoader.log("Using SAMPLE DATA from swagger documentation", level="INFO")
|
670
|
-
else:
|
671
|
-
# Build GraphQL request with actual data using consolidated module
|
672
|
-
graphql_body = MediLink_GraphQL.build_eligibility_request(graphql_variables)
|
673
|
-
MediLink_ConfigLoader.log("Using CONSTRUCTED DATA with consolidated GraphQL module", level="INFO")
|
674
|
-
|
675
|
-
# Compare with sample data for debugging
|
676
|
-
sample_data = MediLink_GraphQL.get_sample_eligibility_request()
|
677
|
-
MediLink_ConfigLoader.log("Sample data structure: {}".format(json.dumps(sample_data, indent=2)), level="DEBUG")
|
678
|
-
MediLink_ConfigLoader.log("Constructed data structure: {}".format(json.dumps(graphql_body, indent=2)), level="DEBUG")
|
679
|
-
|
680
|
-
# Compare key differences
|
681
|
-
sample_vars = sample_data['variables']['input']
|
682
|
-
constructed_vars = graphql_body['variables']['input']
|
683
|
-
|
684
|
-
# Log differences in variables
|
685
|
-
for key in set(sample_vars.keys()) | set(constructed_vars.keys()):
|
686
|
-
sample_val = sample_vars.get(key)
|
687
|
-
constructed_val = constructed_vars.get(key)
|
688
|
-
if sample_val != constructed_val:
|
689
|
-
MediLink_ConfigLoader.log("Variable difference - {}: sample='{}', constructed='{}'".format(
|
690
|
-
key, sample_val, constructed_val), level="DEBUG")
|
691
|
-
|
692
|
-
# Log the GraphQL request
|
693
|
-
MediLink_ConfigLoader.log("GraphQL request body: {}".format(json.dumps(graphql_body, indent=2)), level="DEBUG")
|
694
|
-
MediLink_ConfigLoader.log("GraphQL variables: {}".format(json.dumps(graphql_variables, indent=2)), level="DEBUG")
|
695
|
-
|
696
|
-
# Add required headers for Super Connector
|
697
|
-
headers = {
|
698
|
-
'Content-Type': 'application/json',
|
699
|
-
'Accept': 'application/json',
|
700
|
-
'tin': str(provider_tin) # Ensure TIN is a string
|
701
|
-
}
|
702
|
-
|
703
|
-
# Only add env header when using sample data
|
704
|
-
if USE_SAMPLE_DATA:
|
705
|
-
headers['env'] = 'sandbox'
|
706
|
-
|
707
|
-
# Remove None values from headers
|
708
|
-
headers = {k: v for k, v in headers.items() if v is not None}
|
709
|
-
|
710
|
-
# Log the final headers being sent
|
711
|
-
MediLink_ConfigLoader.log("Final headers being sent: {}".format(json.dumps(headers, indent=2)), level="DEBUG")
|
712
|
-
|
713
|
-
# Make the GraphQL API call
|
714
|
-
response = client.make_api_call(endpoint_name, 'POST', url_extension, params=None, data=graphql_body, headers=headers)
|
715
|
-
|
716
|
-
# Transform GraphQL response to match REST API format
|
717
|
-
# This ensures the calling code doesn't know the difference
|
718
|
-
transformed_response = MediLink_GraphQL.transform_eligibility_response(response)
|
719
|
-
|
720
|
-
return transformed_response
|
721
|
-
|
722
|
-
def is_test_mode(client, body, endpoint_type):
|
723
|
-
"""
|
724
|
-
Checks if Test Mode is enabled in the client's configuration and simulates the response if it is.
|
725
|
-
|
726
|
-
:param client: An instance of APIClient
|
727
|
-
:param body: The intended request body
|
728
|
-
:param endpoint_type: The type of endpoint being accessed ('claim_submission' or 'claim_details')
|
729
|
-
:return: A dummy response simulating the real API call if Test Mode is enabled, otherwise None
|
730
|
-
"""
|
731
|
-
if client.config.get("MediLink_Config", {}).get("TestMode", True):
|
732
|
-
print("Test Mode is enabled! API Call not executed.")
|
733
|
-
print("\nIntended request body:", body)
|
734
|
-
MediLink_ConfigLoader.log("Test Mode is enabled! Simulating 1 second delay for API response for {}.".format(endpoint_type), level="INFO")
|
735
|
-
time.sleep(1)
|
736
|
-
MediLink_ConfigLoader.log("Intended request body: {}".format(body), level="INFO")
|
737
|
-
|
738
|
-
if endpoint_type == 'claim_submission':
|
739
|
-
dummy_response = {
|
740
|
-
"transactionId": "CS07180420240328013411240", # This is the tID for the sandbox Claim Acknowledgement endpoint.
|
741
|
-
"x12ResponseData": "ISA*00* *00* *ZZ*TEST1234567890 *33*TEST *210101*0101*^*00501*000000001*0*P*:~GS*HC*TEST1234567890*TEST*20210101*0101*1*X*005010X222A1~ST*837*000000001*005010X222A1~BHT*0019*00*00001*20210101*0101*CH~NM1*41*2*TEST SUBMITTER*****46*TEST~PER*IC*TEST CONTACT*TE*1234567890~NM1*40*2*TEST RECEIVER*****46*TEST~HL*1**20*1~NM1*85*2*TEST PROVIDER*****XX*1234567890~N3*TEST ADDRESS~N4*TEST CITY*TEST STATE*12345~REF*EI*123456789~PER*IC*TEST PROVIDER*TE*1234567890~NM1*87*2~N3*TEST ADDRESS~N4*TEST CITY*TEST STATE*12345~HL*2*1*22*0~SBR*P*18*TEST GROUP******CI~NM1*IL*1*TEST PATIENT****MI*123456789~N3*TEST ADDRESS~N4*TEST CITY*TEST STATE*12345~DMG*D8*19800101*M~NM1*PR*2*TEST INSURANCE*****PI*12345~CLM*TESTCLAIM*100***12:B:1*Y*A*Y*Y*P~REF*D9*TESTREFERENCE~HI*ABK:TEST~NM1*DN*1*TEST DOCTOR****XX*1234567890~LX*1~SV1*HC:TEST*100*UN*1***1~DTP*472*RD8*20210101-20210101~REF*6R*TESTREFERENCE~SE*30*000000001~GE*1*1~IEA*1*000000001~",
|
742
|
-
"responseType": "dummy_response_837999",
|
743
|
-
"message": "Test Mode: Claim validated and sent for further processing"
|
744
|
-
}
|
745
|
-
elif endpoint_type == 'claim_details':
|
746
|
-
dummy_response = {
|
747
|
-
"responseType": "dummy_response_277CA-CH",
|
748
|
-
"x12ResponseData": "ISA*00* *00* *ZZ*841162764 *ZZ*UB920086 *240318*0921*^*00501*000165687*0*T*:~GS*HN*841162764*UB920086*20240318*0921*0165687*X*005010X214~ST*277*000000006*005010X214~…………….. SE*116*000000006~GE*1*0165687~IEA*1*000165687~",
|
749
|
-
"statuscode": "000",
|
750
|
-
"message:": ""
|
751
|
-
}
|
752
|
-
return dummy_response
|
753
|
-
return None
|
754
|
-
|
755
|
-
def submit_uhc_claim(client, x12_request_data):
|
756
|
-
"""
|
757
|
-
Submits a UHC claim and retrieves the claim acknowledgement details.
|
758
|
-
|
759
|
-
This function first submits the claim using the provided x12 837p data. If the client is in Test Mode,
|
760
|
-
it returns a simulated response. If Test Mode is not enabled, it submits the claim and then retrieves
|
761
|
-
the claim acknowledgement details using the transaction ID from the initial response.
|
762
|
-
|
763
|
-
NOTE: This function uses endpoints that may not be available in the new swagger version:
|
764
|
-
- /Claims/api/claim-submission/v1 (claim submission)
|
765
|
-
- /Claims/api/claim-details/v1 (claim acknowledgement)
|
766
|
-
|
767
|
-
If these endpoints are deprecated in the new swagger, this function will need to be updated
|
768
|
-
to use the new available endpoints.
|
769
|
-
|
770
|
-
:param client: An instance of APIClient
|
771
|
-
:param x12_request_data: The x12 837p data as a string
|
772
|
-
:return: The final response containing the claim acknowledgement details or a dummy response if in Test Mode
|
773
|
-
"""
|
774
|
-
endpoint_name = 'UHCAPI'
|
775
|
-
claim_submission_url = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['claim_submission']
|
776
|
-
claim_details_url = client.config['MediLink_Config']['endpoints'][endpoint_name]['additional_endpoints']['claim_details']
|
777
|
-
|
778
|
-
# Headers for the request
|
779
|
-
headers = {'Content-Type': 'application/json'}
|
780
|
-
|
781
|
-
# Request body for claim submission
|
782
|
-
claim_body = {'x12RequestData': x12_request_data}
|
783
|
-
|
784
|
-
# Check if Test Mode is enabled and return simulated response if so
|
785
|
-
test_mode_response = is_test_mode(client, claim_body, 'claim_submission')
|
786
|
-
if test_mode_response:
|
787
|
-
return test_mode_response
|
788
|
-
|
789
|
-
# Make the API call to submit the claim
|
790
|
-
try:
|
791
|
-
submission_response = client.make_api_call(endpoint_name, 'POST', claim_submission_url, data=claim_body, headers=headers)
|
792
|
-
|
793
|
-
# Extract the transaction ID from the submission response
|
794
|
-
transaction_id = submission_response.get('transactionId')
|
795
|
-
if not transaction_id:
|
796
|
-
raise ValueError("transactionId not found in the submission response")
|
797
|
-
|
798
|
-
# Log the transaction ID for traceability
|
799
|
-
MediLink_ConfigLoader.log("UHCAPI claim submission transactionId: {}".format(transaction_id), level="INFO")
|
800
|
-
|
801
|
-
# Prepare the request body for the claim acknowledgement retrieval
|
802
|
-
acknowledgement_body = {'transactionId': transaction_id}
|
803
|
-
|
804
|
-
# Check if Test Mode is enabled and return simulated response if so
|
805
|
-
test_mode_response = is_test_mode(client, acknowledgement_body, 'claim_details')
|
806
|
-
if test_mode_response:
|
807
|
-
return test_mode_response
|
808
|
-
|
809
|
-
# Make the API call to retrieve the claim acknowledgement details
|
810
|
-
acknowledgement_response = client.make_api_call(endpoint_name, 'POST', claim_details_url, data=acknowledgement_body, headers=headers)
|
811
|
-
return acknowledgement_response
|
812
|
-
|
813
|
-
except Exception as e:
|
814
|
-
print("Error during claim processing: {}".format(e))
|
815
|
-
raise
|
816
|
-
|
817
|
-
if __name__ == "__main__":
|
818
|
-
client = APIClient()
|
819
|
-
|
820
|
-
# Define a configuration to enable or disable tests
|
821
|
-
test_config = {
|
822
|
-
'test_fetch_payer_name': False,
|
823
|
-
'test_claim_summary': False,
|
824
|
-
'test_eligibility': False,
|
825
|
-
'test_eligibility_v3': False,
|
826
|
-
'test_eligibility_super_connector': False,
|
827
|
-
'test_claim_submission': False,
|
828
|
-
}
|
829
|
-
|
830
|
-
try:
|
831
|
-
api_test_cases = client.config['MediLink_Config']['API Test Case']
|
832
|
-
|
833
|
-
# Test 1: Fetch Payer Name
|
834
|
-
if test_config.get('test_fetch_payer_name', False):
|
835
|
-
try:
|
836
|
-
for case in api_test_cases:
|
837
|
-
payer_name = fetch_payer_name_from_api(client, case['payer_id'], client.config)
|
838
|
-
print("*** TEST API: Payer Name: {}".format(payer_name))
|
839
|
-
except Exception as e:
|
840
|
-
print("*** TEST API: Error in Fetch Payer Name Test: {}".format(e))
|
841
|
-
|
842
|
-
# Test 2: Get Claim Summary
|
843
|
-
if test_config.get('test_claim_summary', False):
|
844
|
-
try:
|
845
|
-
for case in api_test_cases:
|
846
|
-
claim_summary = get_claim_summary_by_provider(client, case['provider_tin'], '05/01/2024', '06/23/2024', case['payer_id'])
|
847
|
-
print("TEST API: Claim Summary: {}".format(claim_summary))
|
848
|
-
except Exception as e:
|
849
|
-
print("TEST API: Error in Claim Summary Test: {}".format(e))
|
850
|
-
|
851
|
-
# Test 3: Get Eligibility
|
852
|
-
if test_config.get('test_eligibility', False):
|
853
|
-
try:
|
854
|
-
for case in api_test_cases:
|
855
|
-
eligibility = get_eligibility(client, case['payer_id'], case['provider_last_name'], case['search_option'],
|
856
|
-
case['date_of_birth'], case['member_id'], case['npi'])
|
857
|
-
print("TEST API: Eligibility: {}".format(eligibility))
|
858
|
-
except Exception as e:
|
859
|
-
print("TEST API: Error in Eligibility Test: {}".format(e))
|
860
|
-
|
861
|
-
# Test 4: Get Eligibility v3
|
862
|
-
if test_config.get('test_eligibility_v3', False):
|
863
|
-
try:
|
864
|
-
for case in api_test_cases:
|
865
|
-
eligibility_v3 = get_eligibility_v3(client, payer_id=case['payer_id'], provider_last_name=case['provider_last_name'],
|
866
|
-
search_option=case['search_option'], date_of_birth=case['date_of_birth'],
|
867
|
-
member_id=case['member_id'], npi=case['npi'])
|
868
|
-
print("TEST API: Eligibility v3: {}".format(eligibility_v3))
|
869
|
-
except Exception as e:
|
870
|
-
print("TEST API: Error in Eligibility v3 Test: {}".format(e))
|
871
|
-
|
872
|
-
# Test 5: Get Eligibility Super Connector (GraphQL)
|
873
|
-
if test_config.get('test_eligibility_super_connector', False):
|
874
|
-
try:
|
875
|
-
for case in api_test_cases:
|
876
|
-
eligibility_super_connector = get_eligibility_super_connector(client, payer_id=case['payer_id'], provider_last_name=case['provider_last_name'],
|
877
|
-
search_option=case['search_option'], date_of_birth=case['date_of_birth'],
|
878
|
-
member_id=case['member_id'], npi=case['npi'])
|
879
|
-
print("TEST API: Eligibility Super Connector: {}".format(eligibility_super_connector))
|
880
|
-
except Exception as e:
|
881
|
-
print("TEST API: Error in Eligibility Super Connector Test: {}".format(e))
|
882
|
-
|
883
|
-
"""
|
884
|
-
# Example of iterating over multiple patients (if needed)
|
885
|
-
patients = [
|
886
|
-
{'payer_id': '87726', 'provider_last_name': 'VIDA', 'search_option': 'MemberIDDateOfBirth', 'date_of_birth': '1980-01-01', 'member_id': '123456789', 'npi': '9876543210'},
|
887
|
-
{'payer_id': '87726', 'provider_last_name': 'SMITH', 'search_option': 'MemberIDDateOfBirth', 'date_of_birth': '1970-02-02', 'member_id': '987654321', 'npi': '1234567890'},
|
888
|
-
# Add more patients as needed
|
889
|
-
]
|
890
|
-
|
891
|
-
for patient in patients:
|
892
|
-
try:
|
893
|
-
eligibility = get_eligibility(client, patient['payer_id'], patient['provider_last_name'], patient['search_option'], patient['date_of_birth'], patient['member_id'], patient['npi'])
|
894
|
-
print("Eligibility for {}: {}".format(patient['provider_last_name'], eligibility))
|
895
|
-
except Exception as e:
|
896
|
-
print("Error in getting eligibility for {}: {}".format(patient['provider_last_name'], e))
|
897
|
-
"""
|
898
|
-
# Test 6: UHC Claim Submission
|
899
|
-
if test_config.get('test_claim_submission', False):
|
900
|
-
try:
|
901
|
-
x12_request_data = (
|
902
|
-
"ISA*00* *00* *ZZ*BRT219991205 *33*87726 *170417*1344*^*00501*019160001*0*P*:~GS*HC*BRT219991205*B2BRTA*20170417*134455*19160001*X*005010X222A1~ST*837*000000001*005010X222A1~BHT*0019*00*00001*20170417*134455*CH~NM1*41*2*B00099999819*****46*BB2B~PER*IC*NO NAME*TE*1234567890~NM1*40*2*TIGER*****46*87726~HL*1**20*1~NM1*85*2*XYZ ADDRESS*****XX*1073511762~N3*123 CITY#680~N4*STATE*TG*98765~REF*EI*943319804~PER*IC*XYZ ADDRESS*TE*8008738385*TE*9142862043*FX*1234567890~NM1*87*2~N3*PO BOX 277500~N4*STATE*TS*303847000~HL*2*1*22*0~SBR*P*18*701648******CI~NM1*IL*1*FNAME*LNAME****MI*00123456789~N3*2020 CITY~N4*STATE*TG*80001~DMG*D8*19820220*M~NM1*PR*2*PROVIDER XYZ*****PI*87726~\nCLM*TOSRTA-SPL1*471***12:B:1*Y*A*Y*Y*P~REF*D9*H4HZMH0R4P0104~HI*ABK:Z12~NM1*DN*1*DN*SKO****XX*1255589300~LX*1~SV1*HC:73525*471*UN*1***1~DTP*472*RD8*0190701-20190701~REF*6R*2190476543Z1~SE*30*000000001~GE*1*19160001~IEA*1*019160001~"
|
903
|
-
)
|
904
|
-
response = submit_uhc_claim(client, x12_request_data)
|
905
|
-
print("\nTEST API: Claim Submission Response:\n", response)
|
906
|
-
except Exception as e:
|
907
|
-
print("\nTEST API: Error in Claim Submission Test:\n", e)
|
908
|
-
|
909
|
-
except Exception as e:
|
910
|
-
print("TEST API: Unexpected Error: {}".format(e))
|