medicafe 0.240809.0__py3-none-any.whl → 0.240925.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of medicafe might be problematic. Click here for more details.
- MediBot/MediBot.bat +56 -16
- MediBot/MediBot.py +90 -79
- MediBot/MediBot_Crosswalk_Library.py +496 -194
- MediBot/MediBot_Preprocessor.py +22 -14
- MediBot/MediBot_Preprocessor_lib.py +299 -153
- MediBot/MediBot_UI.py +25 -24
- MediBot/MediBot_dataformat_library.py +17 -25
- MediBot/MediBot_docx_decoder.py +267 -110
- MediBot/update_json.py +26 -1
- MediBot/update_medicafe.py +134 -44
- MediLink/MediLink.py +93 -51
- MediLink/MediLink_837p_encoder.py +23 -23
- MediLink/MediLink_837p_encoder_library.py +141 -96
- MediLink/MediLink_API_Generator.py +1 -7
- MediLink/MediLink_API_v3.py +240 -59
- MediLink/MediLink_APIs.py +1 -2
- MediLink/MediLink_ClaimStatus.py +21 -6
- MediLink/MediLink_ConfigLoader.py +8 -8
- MediLink/MediLink_DataMgmt.py +321 -100
- MediLink/MediLink_Decoder.py +249 -87
- MediLink/MediLink_Deductible.py +7 -8
- MediLink/MediLink_Down.py +115 -120
- MediLink/MediLink_Gmail.py +2 -11
- MediLink/MediLink_Parser.py +63 -36
- MediLink/MediLink_UI.py +29 -24
- MediLink/MediLink_Up.py +12 -8
- {medicafe-0.240809.0.dist-info → medicafe-0.240925.9.dist-info}/METADATA +1 -1
- medicafe-0.240925.9.dist-info/RECORD +47 -0
- {medicafe-0.240809.0.dist-info → medicafe-0.240925.9.dist-info}/WHEEL +1 -1
- medicafe-0.240809.0.dist-info/RECORD +0 -47
- {medicafe-0.240809.0.dist-info → medicafe-0.240925.9.dist-info}/LICENSE +0 -0
- {medicafe-0.240809.0.dist-info → medicafe-0.240925.9.dist-info}/top_level.txt +0 -0
MediLink/MediLink_API_v3.py
CHANGED
|
@@ -1,8 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
import requests
|
|
3
|
-
import yaml
|
|
4
|
-
import json
|
|
5
|
-
import os
|
|
1
|
+
# MediLink_API_v3.py
|
|
2
|
+
import time, requests, yaml, json, os, traceback
|
|
6
3
|
|
|
7
4
|
try:
|
|
8
5
|
from MediLink import MediLink_ConfigLoader
|
|
@@ -64,18 +61,37 @@ class TokenCache:
|
|
|
64
61
|
|
|
65
62
|
def get(self, endpoint_name, current_time):
|
|
66
63
|
token_info = self.tokens.get(endpoint_name, {})
|
|
67
|
-
if token_info
|
|
68
|
-
|
|
64
|
+
if token_info:
|
|
65
|
+
expires_at = token_info['expires_at']
|
|
66
|
+
# Log cache hit and expiration time
|
|
67
|
+
log_message = "Token for {} expires at {}. Current time: {}".format(endpoint_name, expires_at, current_time)
|
|
68
|
+
MediLink_ConfigLoader.log(log_message, level="DEBUG")
|
|
69
|
+
|
|
70
|
+
if expires_at > current_time:
|
|
71
|
+
return token_info['access_token']
|
|
72
|
+
|
|
73
|
+
# Log cache miss
|
|
74
|
+
log_message = "No valid token found for {}".format(endpoint_name)
|
|
75
|
+
MediLink_ConfigLoader.log(log_message, level="INFO")
|
|
76
|
+
|
|
69
77
|
return None
|
|
70
78
|
|
|
71
79
|
def set(self, endpoint_name, access_token, expires_in, current_time):
|
|
72
|
-
# Ensure types are correct
|
|
73
80
|
current_time = ensure_numeric(current_time)
|
|
74
81
|
expires_in = ensure_numeric(expires_in)
|
|
75
|
-
|
|
82
|
+
|
|
83
|
+
# Log the expires_in value to debug
|
|
84
|
+
log_message = "Token expires in: {} seconds for {}".format(expires_in, endpoint_name)
|
|
85
|
+
MediLink_ConfigLoader.log(log_message, level="INFO")
|
|
86
|
+
|
|
87
|
+
# Adjust expiration time by subtracting a buffer of 120 seconds
|
|
88
|
+
expires_at = current_time + expires_in - 120
|
|
89
|
+
log_message = "Setting token for {}. Expires at: {}".format(endpoint_name, expires_at)
|
|
90
|
+
MediLink_ConfigLoader.log(log_message, level="INFO")
|
|
91
|
+
|
|
76
92
|
self.tokens[endpoint_name] = {
|
|
77
93
|
'access_token': access_token,
|
|
78
|
-
'expires_at':
|
|
94
|
+
'expires_at': expires_at
|
|
79
95
|
}
|
|
80
96
|
|
|
81
97
|
class BaseAPIClient:
|
|
@@ -95,12 +111,16 @@ class APIClient(BaseAPIClient):
|
|
|
95
111
|
super().__init__(config)
|
|
96
112
|
|
|
97
113
|
def get_access_token(self, endpoint_name):
|
|
114
|
+
MediLink_ConfigLoader.log("[Get Access Token] Called for {}".format(endpoint_name), level="DEBUG")
|
|
98
115
|
current_time = time.time()
|
|
99
116
|
cached_token = self.token_cache.get(endpoint_name, current_time)
|
|
117
|
+
|
|
100
118
|
if cached_token:
|
|
101
|
-
|
|
119
|
+
expires_at = self.token_cache.tokens[endpoint_name]['expires_at']
|
|
120
|
+
MediLink_ConfigLoader.log("Cached token expires at {}".format(expires_at), level="DEBUG")
|
|
102
121
|
return cached_token
|
|
103
|
-
|
|
122
|
+
|
|
123
|
+
# If no valid token, fetch a new one
|
|
104
124
|
endpoint_config = self.config['MediLink_Config']['endpoints'][endpoint_name]
|
|
105
125
|
token_url = endpoint_config['token_url']
|
|
106
126
|
data = {
|
|
@@ -122,65 +142,225 @@ class APIClient(BaseAPIClient):
|
|
|
122
142
|
expires_in = token_data.get('expires_in', 3600)
|
|
123
143
|
|
|
124
144
|
self.token_cache.set(endpoint_name, access_token, expires_in, current_time)
|
|
125
|
-
MediLink_ConfigLoader.log("Obtained
|
|
145
|
+
MediLink_ConfigLoader.log("Obtained NEW token for endpoint: {}".format(endpoint_name), level="INFO")
|
|
126
146
|
return access_token
|
|
127
147
|
|
|
128
148
|
def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
|
|
129
149
|
token = self.get_access_token(endpoint_name)
|
|
150
|
+
if token:
|
|
151
|
+
MediLink_ConfigLoader.log("[Make API Call] Token found for {}".format(endpoint_name), level="DEBUG")
|
|
152
|
+
else:
|
|
153
|
+
MediLink_ConfigLoader.log("[Make API Call] No token obtained for {}".format(endpoint_name), level="ERROR")
|
|
154
|
+
raise ValueError("No access token available for endpoint: {}".format(endpoint_name))
|
|
155
|
+
|
|
130
156
|
if headers is None:
|
|
131
157
|
headers = {}
|
|
132
158
|
headers.update({'Authorization': 'Bearer {}'.format(token), 'Accept': 'application/json'})
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
159
|
+
base_url = self.config['MediLink_Config']['endpoints'][endpoint_name]['api_url']
|
|
160
|
+
url = base_url + url_extension
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
# Make the API call based on call_type
|
|
164
|
+
if call_type == 'GET':
|
|
165
|
+
response = requests.get(url, headers=headers, params=params)
|
|
166
|
+
elif call_type == 'POST':
|
|
167
|
+
headers['Content-Type'] = 'application/json'
|
|
168
|
+
response = requests.post(url, headers=headers, json=data)
|
|
169
|
+
elif call_type == 'DELETE':
|
|
170
|
+
response = requests.delete(url, headers=headers)
|
|
171
|
+
else:
|
|
172
|
+
raise ValueError("Unsupported call type: {}".format(call_type))
|
|
173
|
+
|
|
174
|
+
masked_headers = headers.copy()
|
|
175
|
+
if 'Authorization' in masked_headers:
|
|
176
|
+
masked_headers['Authorization'] = 'Bearer ***'
|
|
150
177
|
|
|
151
|
-
|
|
152
|
-
error_message = "Error {}: {}".format(response.status_code, response.text)
|
|
153
|
-
MediLink_ConfigLoader.log(error_message, level="ERROR")
|
|
178
|
+
# Raise an HTTPError if the response was unsuccessful
|
|
154
179
|
response.raise_for_status()
|
|
155
180
|
|
|
156
|
-
|
|
181
|
+
return response.json()
|
|
182
|
+
|
|
183
|
+
except requests.exceptions.HTTPError as http_err:
|
|
184
|
+
# If http_err.response is None, handle it separately
|
|
185
|
+
if http_err.response is None:
|
|
186
|
+
log_message = (
|
|
187
|
+
"HTTPError with no response. "
|
|
188
|
+
"URL: {url}, "
|
|
189
|
+
"Method: {method}, "
|
|
190
|
+
"Params: {params}, "
|
|
191
|
+
"Data: {data}, "
|
|
192
|
+
"Headers: {masked_headers}, "
|
|
193
|
+
"Error: {error}"
|
|
194
|
+
).format(
|
|
195
|
+
url=url,
|
|
196
|
+
method=call_type,
|
|
197
|
+
params=params,
|
|
198
|
+
data=data,
|
|
199
|
+
masked_headers=masked_headers,
|
|
200
|
+
error=str(http_err)
|
|
201
|
+
)
|
|
202
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
203
|
+
else:
|
|
204
|
+
# Extract status code and full response content
|
|
205
|
+
status_code = http_err.response.status_code
|
|
206
|
+
try:
|
|
207
|
+
# Try to log the JSON content if available
|
|
208
|
+
response_content = http_err.response.json()
|
|
209
|
+
except ValueError:
|
|
210
|
+
# Fallback to raw text if JSON decoding fails
|
|
211
|
+
response_content = http_err.response.text
|
|
212
|
+
|
|
213
|
+
log_message = (
|
|
214
|
+
"HTTPError: Status Code: {status}, "
|
|
215
|
+
"URL: {url}, "
|
|
216
|
+
"Method: {method}, "
|
|
217
|
+
"Params: {params}, "
|
|
218
|
+
"Data: {data}, "
|
|
219
|
+
"Headers: {masked_headers}, "
|
|
220
|
+
"Response Content: {content}"
|
|
221
|
+
).format(
|
|
222
|
+
status=status_code,
|
|
223
|
+
url=url,
|
|
224
|
+
method=call_type,
|
|
225
|
+
params=params,
|
|
226
|
+
data=data,
|
|
227
|
+
masked_headers=masked_headers,
|
|
228
|
+
content=response_content
|
|
229
|
+
)
|
|
230
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
231
|
+
raise
|
|
232
|
+
|
|
233
|
+
except requests.exceptions.RequestException as req_err:
|
|
234
|
+
# Log connection-related issues or other request exceptions
|
|
235
|
+
log_message = (
|
|
236
|
+
"RequestException: No response received. "
|
|
237
|
+
"URL: {url}, "
|
|
238
|
+
"Method: {method}, "
|
|
239
|
+
"Params: {params}, "
|
|
240
|
+
"Data: {data}, "
|
|
241
|
+
"Headers: {masked_headers}, "
|
|
242
|
+
"Error: {error}"
|
|
243
|
+
).format(
|
|
244
|
+
url=url,
|
|
245
|
+
method=call_type,
|
|
246
|
+
params=params,
|
|
247
|
+
data=data,
|
|
248
|
+
masked_headers=masked_headers,
|
|
249
|
+
error=str(req_err)
|
|
250
|
+
)
|
|
251
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
252
|
+
raise
|
|
157
253
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
254
|
+
except Exception as e:
|
|
255
|
+
# Capture traceback for unexpected exceptions
|
|
256
|
+
tb = traceback.format_exc()
|
|
257
|
+
log_message = (
|
|
258
|
+
"Unexpected error: {error}. "
|
|
259
|
+
"URL: {url}, "
|
|
260
|
+
"Method: {method}, "
|
|
261
|
+
"Params: {params}, "
|
|
262
|
+
"Data: {data}, "
|
|
263
|
+
"Headers: {masked_headers}. "
|
|
264
|
+
"Traceback: {traceback}"
|
|
265
|
+
).format(
|
|
266
|
+
error=str(e),
|
|
267
|
+
url=url,
|
|
268
|
+
method=call_type,
|
|
269
|
+
params=params,
|
|
270
|
+
data=data,
|
|
271
|
+
masked_headers=masked_headers,
|
|
272
|
+
traceback=tb
|
|
273
|
+
)
|
|
274
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
275
|
+
raise
|
|
276
|
+
|
|
277
|
+
def fetch_payer_name_from_api(client, payer_id, config, primary_endpoint='AVAILITY'):
|
|
278
|
+
"""
|
|
279
|
+
Fetches the payer name using the provided APIClient instance.
|
|
162
280
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
281
|
+
:param client: An instance of APIClient
|
|
282
|
+
:param payer_id: The payer ID to fetch
|
|
283
|
+
:param primary_endpoint: The primary endpoint to use
|
|
284
|
+
:return: The payer name if found
|
|
285
|
+
"""
|
|
286
|
+
# Ensure client is an instance of APIClient
|
|
287
|
+
if not isinstance(client, APIClient):
|
|
288
|
+
error_message = "Invalid client provided. Expected an instance of APIClient."
|
|
289
|
+
print(error_message)
|
|
290
|
+
MediLink_ConfigLoader.log(error_message, level="ERROR")
|
|
291
|
+
exit(1) # Exit the script
|
|
292
|
+
|
|
293
|
+
# BUG Force AVAILITY until I can get a payer-list api from any of the other endpoints.
|
|
294
|
+
MediLink_ConfigLoader.log("[Fetch payer name from API] Overriding {} with AVAILITY.".format(primary_endpoint), level="DEBUG")
|
|
295
|
+
primary_endpoint = 'AVAILITY'
|
|
296
|
+
|
|
297
|
+
try:
|
|
298
|
+
endpoints = config['MediLink_Config']['endpoints']
|
|
299
|
+
except KeyError as e:
|
|
300
|
+
error_message = "Configuration loading error: Missing key {0}".format(e)
|
|
301
|
+
print(error_message)
|
|
302
|
+
MediLink_ConfigLoader.log(error_message, level="CRITICAL")
|
|
303
|
+
# Attempt to reload configuration if key is missing
|
|
304
|
+
config, _ = MediLink_ConfigLoader.load_configuration()
|
|
305
|
+
endpoints = config['MediLink_Config']['endpoints'] # Re-attempt to access endpoints
|
|
306
|
+
|
|
307
|
+
# Define endpoint rotation logic
|
|
308
|
+
endpoint_order = ([primary_endpoint] +
|
|
309
|
+
[endpoint for endpoint in endpoints if endpoint != primary_endpoint]
|
|
310
|
+
if primary_endpoint in endpoints else list(endpoints.keys()))
|
|
167
311
|
|
|
168
312
|
for endpoint_name in endpoint_order:
|
|
169
313
|
try:
|
|
170
|
-
|
|
314
|
+
endpoint_url = endpoints[endpoint_name].get('payer_list_endpoint', '/availity-payer-list')
|
|
315
|
+
response = client.make_api_call(endpoint_name, 'GET', endpoint_url, {'payerId': payer_id})
|
|
316
|
+
|
|
317
|
+
# Check if response exists
|
|
318
|
+
if not response:
|
|
319
|
+
log_message = "No response from {0} for Payer ID {1}".format(endpoint_name, payer_id)
|
|
320
|
+
print(log_message)
|
|
321
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
322
|
+
continue
|
|
323
|
+
|
|
324
|
+
# Check if the status code is not 200
|
|
325
|
+
status_code = response.get('statuscode', 200)
|
|
326
|
+
if status_code != 200:
|
|
327
|
+
log_message = "Invalid response status code {0} from {1} for Payer ID {2}. Message: {3}".format(
|
|
328
|
+
status_code, endpoint_name, payer_id, response.get('message', 'No message'))
|
|
329
|
+
print(log_message)
|
|
330
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
331
|
+
continue
|
|
332
|
+
|
|
333
|
+
# Extract payers and validate the response structure
|
|
171
334
|
payers = response.get('payers', [])
|
|
172
|
-
if payers:
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
335
|
+
if not payers:
|
|
336
|
+
log_message = "No payer found at {0} for ID {1}. Response: {2}".format(endpoint_name, payer_id, response)
|
|
337
|
+
print(log_message)
|
|
338
|
+
MediLink_ConfigLoader.log(log_message, level="INFO")
|
|
339
|
+
continue
|
|
340
|
+
|
|
341
|
+
# Extract the payer name from the first payer in the list
|
|
342
|
+
payer_name = payers[0].get('displayName') or payers[0].get('name')
|
|
343
|
+
if not payer_name:
|
|
344
|
+
log_message = "Payer name not found in the response from {0} for ID {1}. Response: {2}".format(
|
|
345
|
+
endpoint_name, payer_id, response)
|
|
346
|
+
print(log_message)
|
|
347
|
+
MediLink_ConfigLoader.log(log_message, level="ERROR")
|
|
348
|
+
continue
|
|
349
|
+
|
|
350
|
+
# Log successful payer retrieval
|
|
351
|
+
log_message = "Found payer at {0} for ID {1}: {2}".format(endpoint_name, payer_id, payer_name)
|
|
352
|
+
MediLink_ConfigLoader.log(log_message, level="INFO")
|
|
353
|
+
return payer_name
|
|
354
|
+
|
|
178
355
|
except Exception as e:
|
|
179
|
-
|
|
356
|
+
error_message = "Error calling {0} for Payer ID {1}. Exception: {2}".format(endpoint_name, payer_id, e)
|
|
357
|
+
MediLink_ConfigLoader.log(error_message, level="INFO")
|
|
180
358
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
359
|
+
# If all endpoints fail
|
|
360
|
+
final_error_message = "All endpoints exhausted for Payer ID {0}.".format(payer_id)
|
|
361
|
+
print(final_error_message)
|
|
362
|
+
MediLink_ConfigLoader.log(final_error_message, level="CRITICAL")
|
|
363
|
+
raise ValueError(final_error_message)
|
|
184
364
|
|
|
185
365
|
def get_claim_summary_by_provider(client, tin, first_service_date, last_service_date, payer_id, get_standard_error='false'):
|
|
186
366
|
endpoint_name = 'UHCAPI'
|
|
@@ -273,7 +453,8 @@ def is_test_mode(client, body, endpoint_type):
|
|
|
273
453
|
if client.config.get("MediLink_Config", {}).get("TestMode", True):
|
|
274
454
|
print("Test Mode is enabled! API Call not executed.")
|
|
275
455
|
print("\nIntended request body:", body)
|
|
276
|
-
MediLink_ConfigLoader.log("Test Mode is enabled! Simulating API response for {}.".format(endpoint_type), level="INFO")
|
|
456
|
+
MediLink_ConfigLoader.log("Test Mode is enabled! Simulating 1 second delay for API response for {}.".format(endpoint_type), level="INFO")
|
|
457
|
+
time.sleep(1)
|
|
277
458
|
MediLink_ConfigLoader.log("Intended request body: {}".format(body), level="INFO")
|
|
278
459
|
|
|
279
460
|
if endpoint_type == 'claim_submission':
|
|
@@ -350,11 +531,11 @@ if __name__ == "__main__":
|
|
|
350
531
|
|
|
351
532
|
# Define a configuration to enable or disable tests
|
|
352
533
|
test_config = {
|
|
353
|
-
'test_fetch_payer_name':
|
|
534
|
+
'test_fetch_payer_name': True,
|
|
354
535
|
'test_claim_summary': False,
|
|
355
536
|
'test_eligibility': False,
|
|
356
537
|
'test_eligibility_v3': False,
|
|
357
|
-
'test_claim_submission':
|
|
538
|
+
'test_claim_submission': False,
|
|
358
539
|
}
|
|
359
540
|
|
|
360
541
|
try:
|
|
@@ -364,10 +545,10 @@ if __name__ == "__main__":
|
|
|
364
545
|
if test_config.get('test_fetch_payer_name', False):
|
|
365
546
|
try:
|
|
366
547
|
for case in api_test_cases:
|
|
367
|
-
payer_name = fetch_payer_name_from_api(case['payer_id'], client.config)
|
|
368
|
-
print("TEST API: Payer Name: {}".format(payer_name))
|
|
548
|
+
payer_name = fetch_payer_name_from_api(client, case['payer_id'], client.config)
|
|
549
|
+
print("*** TEST API: Payer Name: {}".format(payer_name))
|
|
369
550
|
except Exception as e:
|
|
370
|
-
print("TEST API: Error in Fetch Payer Name Test: {}".format(e))
|
|
551
|
+
print("*** TEST API: Error in Fetch Payer Name Test: {}".format(e))
|
|
371
552
|
|
|
372
553
|
# Test 2: Get Claim Summary
|
|
373
554
|
if test_config.get('test_claim_summary', False):
|
MediLink/MediLink_APIs.py
CHANGED
MediLink/MediLink_ClaimStatus.py
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
# MediLink_ClaimStatus.py
|
|
1
2
|
from datetime import datetime, timedelta
|
|
3
|
+
import os
|
|
2
4
|
import MediLink_API_v3
|
|
3
5
|
|
|
4
6
|
try:
|
|
@@ -19,7 +21,7 @@ start_date_str = start_date.strftime('%m/%d/%Y')
|
|
|
19
21
|
billing_provider_tin = config['MediLink_Config'].get('billing_provider_tin')
|
|
20
22
|
|
|
21
23
|
# Define the list of payer_id's to iterate over
|
|
22
|
-
payer_ids = ['87726'] # Default value
|
|
24
|
+
payer_ids = ['87726'] # Default value, getting a bad request error when trying to get all payer_ids.
|
|
23
25
|
# Allowed payer id's for UHC
|
|
24
26
|
# payer_ids = ['87726', '03432', '96385', '95467', '86050', '86047', '95378', '06111', '37602']
|
|
25
27
|
|
|
@@ -27,19 +29,23 @@ payer_ids = ['87726'] # Default value
|
|
|
27
29
|
client = MediLink_API_v3.APIClient()
|
|
28
30
|
|
|
29
31
|
# Function to process and display the data in a compact, tabular format
|
|
30
|
-
def display_claim_summary(claim_summary, payer_id):
|
|
32
|
+
def display_claim_summary(claim_summary, payer_id, output_file):
|
|
31
33
|
claims = claim_summary.get('claims', [])
|
|
32
34
|
|
|
33
35
|
# Display header
|
|
34
36
|
header = "Payer ID: {} | Start Date: {} | End Date: {}".format(payer_id, start_date_str, end_date_str)
|
|
35
37
|
print(header)
|
|
38
|
+
output_file.write(header + "\n") # Write header to the output file
|
|
36
39
|
print("=" * len(header))
|
|
40
|
+
output_file.write("=" * len(header) + "\n") # Write separator to the output file
|
|
37
41
|
|
|
38
42
|
# Table header
|
|
39
43
|
table_header = "{:<10} | {:<10} | {:<20} | {:<6} | {:<6} | {:<7} | {:<7} | {:<7} | {:<7}".format(
|
|
40
44
|
"Claim #", "Status", "Patient", "Proc.", "Serv.", "Allowed", "Paid", "Pt Resp", "Charged")
|
|
41
45
|
print(table_header)
|
|
46
|
+
output_file.write(table_header + "\n") # Write table header to the output file
|
|
42
47
|
print("-" * len(table_header))
|
|
48
|
+
output_file.write("-" * len(table_header) + "\n") # Write separator to the output file
|
|
43
49
|
|
|
44
50
|
# Process each claim and display it in a compact format
|
|
45
51
|
claims_dict = {}
|
|
@@ -90,6 +96,7 @@ def display_claim_summary(claim_summary, payer_id):
|
|
|
90
96
|
claim_data['total_patient_responsibility_amount'], claim_data['total_charged_amount']
|
|
91
97
|
)
|
|
92
98
|
print(table_row)
|
|
99
|
+
output_file.write(table_row + "\n") # Write each row to the output file
|
|
93
100
|
|
|
94
101
|
if claim_data['total_paid_amount'] == '0.00':
|
|
95
102
|
for xwalk in claim_data['claim_xwalk_data']:
|
|
@@ -109,6 +116,7 @@ def display_claim_summary(claim_summary, payer_id):
|
|
|
109
116
|
claim_data['total_patient_responsibility_amount'], claim_data['total_charged_amount']
|
|
110
117
|
)
|
|
111
118
|
print(table_row + " (Duplicate with different data)")
|
|
119
|
+
output_file.write(table_row + " (Duplicate with different data)\n") # Write each row to the output file
|
|
112
120
|
|
|
113
121
|
if claim_data['total_paid_amount'] == '0.00':
|
|
114
122
|
for xwalk in claim_data['claim_xwalk_data']:
|
|
@@ -128,6 +136,7 @@ def display_claim_summary(claim_summary, payer_id):
|
|
|
128
136
|
claim_data['total_patient_responsibility_amount'], claim_data['total_charged_amount']
|
|
129
137
|
)
|
|
130
138
|
print(table_row)
|
|
139
|
+
output_file.write(table_row + "\n") # Write each row to the output file
|
|
131
140
|
|
|
132
141
|
if claim_data['total_paid_amount'] == '0.00':
|
|
133
142
|
for xwalk in claim_data['claim_xwalk_data']:
|
|
@@ -138,7 +147,13 @@ def display_claim_summary(claim_summary, payer_id):
|
|
|
138
147
|
clmIcnSufxCd = xwalk['clmIcnSufxCd'] # String: e.g., "01"
|
|
139
148
|
print(" 507: {} ({}) | 508: {} ({}) | ICN Suffix: {}".format(clm507Cd, clm507CdDesc, clm508Cd, clm508CdDesc, clmIcnSufxCd))
|
|
140
149
|
|
|
141
|
-
#
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
150
|
+
# Create a temporary file to store the claim summary
|
|
151
|
+
output_file_path = os.path.join(os.getenv('TEMP'), 'claim_summary_report.txt')
|
|
152
|
+
with open(output_file_path, 'w') as output_file:
|
|
153
|
+
# Loop through each payer_id and call the API, then display the claim summary
|
|
154
|
+
for payer_id in payer_ids:
|
|
155
|
+
claim_summary = MediLink_API_v3.get_claim_summary_by_provider(client, billing_provider_tin, start_date_str, end_date_str, payer_id=payer_id)
|
|
156
|
+
display_claim_summary(claim_summary, payer_id, output_file) # Pass output_file to the display function
|
|
157
|
+
|
|
158
|
+
# Open the generated file in Notepad
|
|
159
|
+
os.startfile(output_file_path) # Use os.startfile for better handling
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
|
|
2
|
-
import json
|
|
3
|
-
import logging
|
|
1
|
+
# MediLink_ConfigLoader.py
|
|
2
|
+
import os, json, logging, sys, platform, yaml
|
|
4
3
|
from datetime import datetime
|
|
5
4
|
from collections import OrderedDict
|
|
6
|
-
import sys
|
|
7
|
-
import platform
|
|
8
|
-
import yaml
|
|
9
5
|
|
|
10
6
|
"""
|
|
11
7
|
This function should be generalizable to have a initialization script over all the Medi* functions
|
|
@@ -61,7 +57,7 @@ def load_configuration(config_path=os.path.join(os.path.dirname(__file__), '..',
|
|
|
61
57
|
sys.exit(1) # Exit the script due to a critical error in configuration loading
|
|
62
58
|
|
|
63
59
|
# Logs messages with optional error type and claim data.
|
|
64
|
-
def log(message, config=None, level="INFO", error_type=None, claim=None):
|
|
60
|
+
def log(message, config=None, level="INFO", error_type=None, claim=None, verbose=False):
|
|
65
61
|
|
|
66
62
|
# If config is not provided, load it
|
|
67
63
|
if config is None:
|
|
@@ -72,7 +68,11 @@ def log(message, config=None, level="INFO", error_type=None, claim=None):
|
|
|
72
68
|
local_storage_path = config['MediLink_Config'].get('local_storage_path', '.') if isinstance(config, dict) else '.'
|
|
73
69
|
log_filename = datetime.now().strftime("Log_%m%d%Y.log")
|
|
74
70
|
log_filepath = os.path.join(local_storage_path, log_filename)
|
|
75
|
-
|
|
71
|
+
|
|
72
|
+
# Set logging level based on verbosity
|
|
73
|
+
logging_level = logging.DEBUG if verbose else logging.INFO
|
|
74
|
+
|
|
75
|
+
logging.basicConfig(level=logging_level,
|
|
76
76
|
format='%(asctime)s - %(levelname)s - %(message)s',
|
|
77
77
|
filename=log_filepath,
|
|
78
78
|
filemode='a')
|