medicafe 0.240716.2__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 +100 -78
- MediBot/MediBot_Crosswalk_Library.py +496 -194
- MediBot/MediBot_Preprocessor.py +22 -14
- MediBot/MediBot_Preprocessor_lib.py +301 -143
- 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 +95 -53
- MediLink/MediLink_837p_encoder.py +83 -66
- MediLink/MediLink_837p_encoder_library.py +159 -102
- MediLink/MediLink_API_Generator.py +1 -7
- MediLink/MediLink_API_v3.py +348 -63
- MediLink/MediLink_APIs.py +1 -2
- MediLink/MediLink_ClaimStatus.py +21 -6
- MediLink/MediLink_ConfigLoader.py +9 -9
- MediLink/MediLink_DataMgmt.py +321 -100
- MediLink/MediLink_Decoder.py +249 -87
- MediLink/MediLink_Deductible.py +62 -56
- MediLink/MediLink_Down.py +115 -121
- MediLink/MediLink_Gmail.py +2 -11
- MediLink/MediLink_Parser.py +63 -36
- MediLink/MediLink_UI.py +36 -23
- MediLink/MediLink_Up.py +188 -115
- {medicafe-0.240716.2.dist-info → medicafe-0.240925.9.dist-info}/METADATA +1 -1
- medicafe-0.240925.9.dist-info/RECORD +47 -0
- medicafe-0.240716.2.dist-info/RECORD +0 -47
- {medicafe-0.240716.2.dist-info → medicafe-0.240925.9.dist-info}/LICENSE +0 -0
- {medicafe-0.240716.2.dist-info → medicafe-0.240925.9.dist-info}/WHEEL +0 -0
- {medicafe-0.240716.2.dist-info → medicafe-0.240925.9.dist-info}/top_level.txt +0 -0
MediLink/MediLink_Decoder.py
CHANGED
|
@@ -1,57 +1,93 @@
|
|
|
1
1
|
# MediLink_Decoder.py
|
|
2
|
-
import os
|
|
3
|
-
import sys
|
|
4
|
-
import csv
|
|
2
|
+
import os, sys, csv
|
|
5
3
|
from MediLink_ConfigLoader import load_configuration, log
|
|
6
4
|
from MediLink_Parser import parse_era_content, parse_277_content, parse_277IBR_content, parse_277EBR_content, parse_dpt_content, parse_ebt_content, parse_ibt_content
|
|
7
5
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
# Define new_fieldnames globally
|
|
7
|
+
new_fieldnames = ['Claim #', 'Payer', 'Status', 'Patient', 'Proc.', 'Serv.', 'Allowed', 'Paid', 'Pt Resp', 'Charged']
|
|
8
|
+
|
|
9
|
+
class UnifiedRecord:
|
|
10
|
+
def __init__(self, claim_number='', status='', patient='', payer='', proc_date='', serv_date='', allowed='', paid='', pt_resp='', charged=''):
|
|
11
|
+
self.claim_number = claim_number
|
|
12
|
+
self.payer = payer # Added payer to the constructor
|
|
13
|
+
self.status = status
|
|
14
|
+
self.patient = patient
|
|
15
|
+
self.proc_date = proc_date
|
|
16
|
+
self.serv_date = serv_date
|
|
17
|
+
self.allowed = allowed
|
|
18
|
+
self.paid = paid
|
|
19
|
+
self.pt_resp = pt_resp
|
|
20
|
+
self.charged = charged
|
|
21
|
+
|
|
22
|
+
def to_dict(self):
|
|
23
|
+
return {
|
|
24
|
+
'Claim #': self.claim_number,
|
|
25
|
+
'Payer': self.payer, # Added payer to the dictionary representation
|
|
26
|
+
'Status': self.status,
|
|
27
|
+
'Patient': self.patient,
|
|
28
|
+
'Proc.': self.proc_date,
|
|
29
|
+
'Serv.': self.serv_date,
|
|
30
|
+
'Allowed': self.allowed,
|
|
31
|
+
'Paid': self.paid,
|
|
32
|
+
'Pt Resp': self.pt_resp,
|
|
33
|
+
'Charged': self.charged
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
def __repr__(self):
|
|
37
|
+
return ("UnifiedRecord(claim_number='{0}', status='{1}', patient='{2}', payer='{3}', proc_date='{4}', serv_date='{5}', "
|
|
38
|
+
"allowed='{6}', paid='{7}', pt_resp='{8}', charged='{9}')").format(
|
|
39
|
+
self.claim_number, self.status, self.patient, self.payer, self.proc_date,
|
|
40
|
+
self.serv_date, self.allowed, self.paid, self.pt_resp, self.charged) # Added payer to the repr
|
|
41
|
+
|
|
42
|
+
def process_decoded_file(file_path, output_directory, return_records=False, debug=False): # Renamed from process_file
|
|
43
|
+
os.makedirs(output_directory, exist_ok=True)
|
|
11
44
|
|
|
12
45
|
file_type = determine_file_type(file_path)
|
|
13
46
|
content = read_file(file_path)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
|
|
48
|
+
parse_functions = {
|
|
49
|
+
'ERA': parse_era_content,
|
|
50
|
+
'277': parse_277_content,
|
|
51
|
+
'277IBR': parse_277IBR_content,
|
|
52
|
+
'277EBR': parse_277EBR_content,
|
|
53
|
+
'DPT': parse_dpt_content,
|
|
54
|
+
'EBT': parse_ebt_content,
|
|
55
|
+
'IBT': parse_ibt_content
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
parse_function = parse_functions.get(file_type)
|
|
59
|
+
if parse_function is None:
|
|
26
60
|
log("Unsupported file type: {}".format(file_type))
|
|
27
61
|
return []
|
|
28
62
|
|
|
63
|
+
records = parse_function(content, debug=debug)
|
|
29
64
|
formatted_records = format_records(records, file_type)
|
|
65
|
+
|
|
30
66
|
if not return_records:
|
|
31
|
-
display_table(formatted_records)
|
|
32
|
-
output_file_path = os.path.join(output_directory, os.path.basename(file_path)
|
|
67
|
+
display_table([record.to_dict() for record in formatted_records])
|
|
68
|
+
output_file_path = os.path.join(output_directory, "{}_decoded.csv".format(os.path.basename(file_path)))
|
|
33
69
|
write_records_to_csv(formatted_records, output_file_path)
|
|
34
70
|
log("Decoded data written to {}".format(output_file_path))
|
|
35
|
-
|
|
71
|
+
|
|
72
|
+
return formatted_records # Returns list of UnifiedRecord instances
|
|
36
73
|
|
|
37
74
|
def determine_file_type(file_path):
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return None
|
|
75
|
+
file_extensions = {
|
|
76
|
+
'.era': 'ERA',
|
|
77
|
+
'.277': '277',
|
|
78
|
+
'.277ibr': '277IBR',
|
|
79
|
+
'.277ebr': '277EBR',
|
|
80
|
+
'.dpt': 'DPT',
|
|
81
|
+
'.ebt': 'EBT',
|
|
82
|
+
'.ibt': 'IBT'
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for ext, file_type in file_extensions.items():
|
|
86
|
+
if file_path.endswith(ext):
|
|
87
|
+
return file_type
|
|
88
|
+
|
|
89
|
+
log("Unsupported file type for file: {}".format(file_path))
|
|
90
|
+
return None
|
|
55
91
|
|
|
56
92
|
def read_file(file_path):
|
|
57
93
|
with open(file_path, 'r') as file:
|
|
@@ -59,86 +95,209 @@ def read_file(file_path):
|
|
|
59
95
|
return content
|
|
60
96
|
|
|
61
97
|
def write_records_to_csv(records, output_file_path):
|
|
62
|
-
|
|
98
|
+
if not records:
|
|
99
|
+
log("No records to write.", 'error')
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
# Use the global variable for fieldnames
|
|
103
|
+
fieldnames = new_fieldnames
|
|
104
|
+
|
|
63
105
|
with open(output_file_path, 'w', newline='') as csvfile:
|
|
64
106
|
writer = csv.DictWriter(csvfile, fieldnames=fieldnames)
|
|
65
107
|
writer.writeheader()
|
|
66
108
|
for record in records:
|
|
67
|
-
writer.writerow(record)
|
|
109
|
+
writer.writerow(record.to_dict())
|
|
68
110
|
|
|
69
111
|
def format_records(records, file_type):
|
|
70
112
|
formatted_records = []
|
|
71
|
-
|
|
113
|
+
seen_claim_numbers = set() # Set to track unique claim numbers
|
|
114
|
+
|
|
115
|
+
for i, record in enumerate(records):
|
|
116
|
+
# Determine the claim number based on the file type
|
|
72
117
|
if file_type == 'IBT':
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
'Serv.': format_date(record.get('From Date', '')),
|
|
79
|
-
'Allowed': '',
|
|
80
|
-
'Paid': '',
|
|
81
|
-
'Pt Resp': '',
|
|
82
|
-
'Charged': record.get('Charge', '')
|
|
83
|
-
}
|
|
118
|
+
claim_number = record.get('Patient Control Number', '')
|
|
119
|
+
elif file_type == 'ERA':
|
|
120
|
+
claim_number = record.get('Chart Number', '')
|
|
121
|
+
elif file_type == 'EBT':
|
|
122
|
+
claim_number = record.get('Patient Control Number', '')
|
|
84
123
|
else:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
124
|
+
claim_number = '' # Default to empty if file type is not recognized
|
|
125
|
+
|
|
126
|
+
# Skip records without a claim number
|
|
127
|
+
if not claim_number:
|
|
128
|
+
log("Record {} missing claim_number. Skipping.".format(i + 1), level="WARNING")
|
|
129
|
+
continue
|
|
130
|
+
|
|
131
|
+
# Check for duplicates
|
|
132
|
+
if claim_number in seen_claim_numbers:
|
|
133
|
+
log("Duplicate claim_number {} found at record {}. Skipping.".format(claim_number, i + 1), level="DEBUG")
|
|
134
|
+
continue
|
|
135
|
+
|
|
136
|
+
seen_claim_numbers.add(claim_number) # Add claim number to the set. BUG This isn't successfully removing duplicate records. -- it might be across files and not within them.
|
|
137
|
+
|
|
138
|
+
unified_record = UnifiedRecord()
|
|
139
|
+
|
|
140
|
+
# Populate the unified_record based on the file type
|
|
141
|
+
if file_type == 'IBT':
|
|
142
|
+
unified_record.claim_number = claim_number
|
|
143
|
+
unified_record.status = record.get('Status', '')
|
|
144
|
+
unified_record.patient = record.get('Patient Name', '')
|
|
145
|
+
unified_record.proc_date = format_date(record.get('To Date', ''))
|
|
146
|
+
unified_record.serv_date = format_date(record.get('From Date', ''))
|
|
147
|
+
unified_record.charged = record.get('Charge', '')
|
|
148
|
+
|
|
149
|
+
elif file_type == 'ERA':
|
|
150
|
+
unified_record.claim_number = claim_number
|
|
151
|
+
unified_record.status = record.get('claimStatus', '')
|
|
152
|
+
unified_record.patient = record.get('Patient', '')
|
|
153
|
+
unified_record.proc_date = format_date(record.get('processed_date', ''))
|
|
154
|
+
unified_record.serv_date = format_date(record.get('Date of Service', ''))
|
|
155
|
+
unified_record.allowed = record.get('Allowed Amount', '')
|
|
156
|
+
unified_record.paid = record.get('Amount Paid', '')
|
|
157
|
+
unified_record.pt_resp = record.get('Patient Responsibility', '')
|
|
158
|
+
unified_record.charged = record.get('Charge', '')
|
|
159
|
+
|
|
160
|
+
elif file_type == 'EBT':
|
|
161
|
+
if 'Patient Control Number' in record:
|
|
162
|
+
unified_record.claim_number = claim_number
|
|
163
|
+
message_type = record.get('Message Type', '').upper()
|
|
164
|
+
status_mapping = {
|
|
165
|
+
'A': 'Accepted',
|
|
166
|
+
'R': 'Rejected',
|
|
167
|
+
}
|
|
168
|
+
# unified_record.status = status_mapping.get(message_type, message_type)
|
|
169
|
+
unified_record.status = record.get('Message', '')
|
|
170
|
+
unified_record.payer = record.get('Message Initiator', '')
|
|
171
|
+
unified_record.patient = record.get('Patient Name', '')
|
|
172
|
+
unified_record.proc_date = format_date(record.get('To Date', ''))
|
|
173
|
+
unified_record.serv_date = format_date(record.get('From Date', ''))
|
|
174
|
+
unified_record.allowed = ''
|
|
175
|
+
unified_record.paid = ''
|
|
176
|
+
unified_record.pt_resp = ''
|
|
177
|
+
unified_record.charged = record.get('Charge', '')
|
|
178
|
+
log("Formatted EBT Record {}: {}".format(i + 1, unified_record), level="DEBUG")
|
|
179
|
+
else:
|
|
180
|
+
log("Skipped non-claim EBT Record {}: {}".format(i + 1, record), level="DEBUG")
|
|
181
|
+
continue
|
|
182
|
+
|
|
183
|
+
# Append the unified record to the list
|
|
184
|
+
formatted_records.append(unified_record)
|
|
185
|
+
|
|
97
186
|
return formatted_records
|
|
98
187
|
|
|
99
188
|
def format_date(date_str):
|
|
100
|
-
if date_str and len(date_str) >=
|
|
101
|
-
return date_str[
|
|
189
|
+
if date_str and len(date_str) >= 8:
|
|
190
|
+
return date_str[4:6] + '-' + date_str[6:8] # Adjusted to match sample date format 'YYYYMMDD'
|
|
102
191
|
return ''
|
|
103
192
|
|
|
104
193
|
def display_table(records):
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
col_widths = {field: len(field) for field in new_fieldnames}
|
|
194
|
+
"""
|
|
195
|
+
Display records in a formatted table after deduplication.
|
|
108
196
|
|
|
109
|
-
|
|
197
|
+
Args:
|
|
198
|
+
records (list): List of UnifiedRecord instances.
|
|
199
|
+
"""
|
|
200
|
+
# Deduplicate records before displaying
|
|
201
|
+
records = deduplicate_records(records)
|
|
202
|
+
|
|
203
|
+
if not records:
|
|
204
|
+
print("No records to display.")
|
|
205
|
+
return
|
|
206
|
+
|
|
207
|
+
# Determine which fields have at least one non-empty value
|
|
208
|
+
used_fields = [field for field in new_fieldnames if any(str(record.get(field, '')).strip() for record in records)]
|
|
209
|
+
|
|
210
|
+
if not used_fields:
|
|
211
|
+
print("No data to display.")
|
|
212
|
+
return
|
|
213
|
+
|
|
214
|
+
# Calculate column widths based on the longest item in each used column
|
|
215
|
+
col_widths = {field: len(field) for field in used_fields}
|
|
216
|
+
|
|
110
217
|
for record in records:
|
|
111
|
-
for field in
|
|
218
|
+
for field in used_fields:
|
|
112
219
|
col_widths[field] = max(col_widths[field], len(str(record.get(field, ''))))
|
|
113
|
-
|
|
220
|
+
|
|
114
221
|
# Create table header
|
|
115
|
-
header = " | ".join("{:<{}}".format(field, col_widths[field]) for field in
|
|
222
|
+
header = " | ".join("{:<{}}".format(field, col_widths[field]) for field in used_fields)
|
|
116
223
|
print(header)
|
|
117
224
|
print("-" * len(header))
|
|
118
|
-
|
|
225
|
+
|
|
119
226
|
# Create table rows
|
|
120
227
|
for record in records:
|
|
121
|
-
row = " | ".join("{:<{}}".format(str(record.get(field, '')), col_widths[field]) for field in
|
|
228
|
+
row = " | ".join("{:<{}}".format(str(record.get(field, '')), col_widths[field]) for field in used_fields)
|
|
122
229
|
print(row)
|
|
123
230
|
|
|
124
231
|
def display_consolidated_records(records):
|
|
125
|
-
|
|
232
|
+
"""
|
|
233
|
+
Display the consolidated records in a formatted table.
|
|
234
|
+
Removes any records that are completely empty or only contain whitespace.
|
|
235
|
+
If no valid records are found, displays a message to that effect.
|
|
236
|
+
"""
|
|
237
|
+
# Deduplicate records before displaying
|
|
238
|
+
records = deduplicate_records(records)
|
|
239
|
+
|
|
240
|
+
# If records are UnifiedRecord instances, convert them to dictionaries
|
|
241
|
+
if records and isinstance(records[0], UnifiedRecord):
|
|
242
|
+
dict_records = [record.to_dict() for record in records]
|
|
243
|
+
elif records and isinstance(records[0], dict):
|
|
244
|
+
dict_records = records
|
|
245
|
+
else:
|
|
246
|
+
log("Invalid record format for display.", level="ERROR")
|
|
247
|
+
return
|
|
248
|
+
|
|
249
|
+
# Filter out records that are completely empty or only contain whitespace
|
|
250
|
+
filtered_records = [
|
|
251
|
+
record for record in dict_records
|
|
252
|
+
if any(str(record.get(field, '')).strip() for field in new_fieldnames)
|
|
253
|
+
]
|
|
254
|
+
|
|
255
|
+
if not filtered_records:
|
|
256
|
+
print("No valid records to display after filtering empty rows.")
|
|
126
257
|
return
|
|
127
258
|
|
|
128
|
-
|
|
129
|
-
|
|
259
|
+
# Determine which fields have at least one non-empty value
|
|
260
|
+
used_fields = [field for field in new_fieldnames if any(str(record.get(field, '')).strip() for record in filtered_records)]
|
|
130
261
|
|
|
131
|
-
|
|
132
|
-
|
|
262
|
+
if not used_fields:
|
|
263
|
+
print("No data to display.")
|
|
264
|
+
return
|
|
265
|
+
|
|
266
|
+
# Calculate column widths based on the longest item in each used column
|
|
267
|
+
col_widths = {field: len(field) for field in used_fields}
|
|
268
|
+
|
|
269
|
+
for record in filtered_records:
|
|
270
|
+
for field in used_fields:
|
|
133
271
|
col_widths[field] = max(col_widths[field], len(str(record.get(field, ''))))
|
|
134
|
-
|
|
135
|
-
|
|
272
|
+
|
|
273
|
+
# Print header
|
|
274
|
+
header = " | ".join("{:<{}}".format(field, col_widths[field]) for field in used_fields)
|
|
136
275
|
print(header)
|
|
137
276
|
print("-" * len(header))
|
|
277
|
+
|
|
278
|
+
# Print each row
|
|
279
|
+
for record in filtered_records:
|
|
280
|
+
row = " | ".join("{:<{}}".format(str(record.get(field, '')), col_widths[field]) for field in used_fields)
|
|
281
|
+
print(row)
|
|
138
282
|
|
|
283
|
+
def deduplicate_records(records):
|
|
284
|
+
"""
|
|
285
|
+
Remove duplicate records based on claim_number.
|
|
286
|
+
|
|
287
|
+
Args:
|
|
288
|
+
records (list): List of UnifiedRecord instances.
|
|
289
|
+
|
|
290
|
+
Returns:
|
|
291
|
+
list: List of unique UnifiedRecord instances.
|
|
292
|
+
"""
|
|
293
|
+
unique_records_dict = {}
|
|
139
294
|
for record in records:
|
|
140
|
-
|
|
141
|
-
|
|
295
|
+
if record.claim_number not in unique_records_dict:
|
|
296
|
+
unique_records_dict[record.claim_number] = record
|
|
297
|
+
else:
|
|
298
|
+
log("Duplicate record found for claim_number {}. Skipping.".format(record.claim_number), "DEBUG")
|
|
299
|
+
|
|
300
|
+
return list(unique_records_dict.values())
|
|
142
301
|
|
|
143
302
|
if __name__ == "__main__":
|
|
144
303
|
config, _ = load_configuration()
|
|
@@ -152,14 +311,17 @@ if __name__ == "__main__":
|
|
|
152
311
|
all_records = []
|
|
153
312
|
for file_path in files:
|
|
154
313
|
try:
|
|
155
|
-
records =
|
|
314
|
+
records = process_decoded_file(file_path, output_directory, return_records=True)
|
|
156
315
|
all_records.extend(records)
|
|
157
316
|
except Exception as e:
|
|
158
317
|
log("Failed to process {}: {}".format(file_path, e), 'error')
|
|
159
318
|
|
|
160
|
-
|
|
319
|
+
# Call the deduplication function
|
|
320
|
+
unique_records = deduplicate_records(all_records)
|
|
321
|
+
|
|
322
|
+
display_consolidated_records([record.to_dict() for record in unique_records])
|
|
161
323
|
|
|
162
324
|
if input("Do you want to export the consolidated records to a CSV file? (y/n): ").strip().lower() == 'y':
|
|
163
325
|
consolidated_csv_path = os.path.join(output_directory, "Consolidated_Records.csv")
|
|
164
|
-
write_records_to_csv(
|
|
326
|
+
write_records_to_csv(unique_records, consolidated_csv_path)
|
|
165
327
|
log("Consolidated records written to {}".format(consolidated_csv_path))
|
MediLink/MediLink_Deductible.py
CHANGED
|
@@ -41,12 +41,10 @@ summary = {
|
|
|
41
41
|
# print("\nDebug JSON Summary:")
|
|
42
42
|
# print(json.dumps(summary, indent=2))
|
|
43
43
|
"""
|
|
44
|
+
# MediLink_Deductible.py
|
|
44
45
|
import MediLink_API_v3
|
|
45
|
-
import os
|
|
46
|
-
import sys
|
|
46
|
+
import os, sys, requests, json
|
|
47
47
|
from datetime import datetime
|
|
48
|
-
import requests
|
|
49
|
-
import json
|
|
50
48
|
|
|
51
49
|
try:
|
|
52
50
|
from MediLink import MediLink_ConfigLoader
|
|
@@ -54,7 +52,8 @@ except ImportError:
|
|
|
54
52
|
import MediLink_ConfigLoader
|
|
55
53
|
|
|
56
54
|
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
57
|
-
sys.path
|
|
55
|
+
if project_dir not in sys.path:
|
|
56
|
+
sys.path.append(project_dir)
|
|
58
57
|
|
|
59
58
|
try:
|
|
60
59
|
from MediBot import MediBot_Preprocessor_lib
|
|
@@ -68,11 +67,15 @@ config, _ = MediLink_ConfigLoader.load_configuration()
|
|
|
68
67
|
client = MediLink_API_v3.APIClient()
|
|
69
68
|
|
|
70
69
|
# Get provider_last_name and npi from configuration
|
|
71
|
-
provider_last_name = config['MediLink_Config'].get('default_billing_provider_last_name')
|
|
72
|
-
npi = config['MediLink_Config'].get('default_billing_provider_npi')
|
|
70
|
+
provider_last_name = config['MediLink_Config'].get('default_billing_provider_last_name', 'Unknown')
|
|
71
|
+
npi = config['MediLink_Config'].get('default_billing_provider_npi', 'Unknown')
|
|
72
|
+
|
|
73
|
+
# Check if the provider_last_name is still 'Unknown'
|
|
74
|
+
if provider_last_name == 'Unknown':
|
|
75
|
+
MediLink_ConfigLoader.log("Warning: provider_last_name was not found in the configuration.", level="WARNING")
|
|
73
76
|
|
|
74
77
|
# Define the list of payer_id's to iterate over
|
|
75
|
-
payer_ids = ['87726', '03432', '96385', '95467', '86050', '86047', '95378', '06111', '37602']
|
|
78
|
+
payer_ids = ['87726', '03432', '96385', '95467', '86050', '86047', '95378', '06111', '37602'] # United Healthcare.
|
|
76
79
|
|
|
77
80
|
# Get the latest CSV
|
|
78
81
|
CSV_FILE_PATH = config.get('CSV_FILE_PATH', "")
|
|
@@ -101,12 +104,12 @@ patients = [
|
|
|
101
104
|
def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, member_id, npi):
|
|
102
105
|
try:
|
|
103
106
|
# Log the parameters being sent to the function
|
|
104
|
-
MediLink_ConfigLoader.log("Calling get_eligibility_v3 with parameters:", level="
|
|
105
|
-
MediLink_ConfigLoader.log("payer_id: {}".format(payer_id), level="
|
|
106
|
-
MediLink_ConfigLoader.log("provider_last_name: {}".format(provider_last_name), level="
|
|
107
|
-
MediLink_ConfigLoader.log("date_of_birth: {}".format(date_of_birth), level="
|
|
108
|
-
MediLink_ConfigLoader.log("member_id: {}".format(member_id), level="
|
|
109
|
-
MediLink_ConfigLoader.log("npi: {}".format(npi), level="
|
|
107
|
+
MediLink_ConfigLoader.log("Calling get_eligibility_v3 with parameters:", level="DEBUG")
|
|
108
|
+
MediLink_ConfigLoader.log("payer_id: {}".format(payer_id), level="DEBUG")
|
|
109
|
+
MediLink_ConfigLoader.log("provider_last_name: {}".format(provider_last_name), level="DEBUG")
|
|
110
|
+
MediLink_ConfigLoader.log("date_of_birth: {}".format(date_of_birth), level="DEBUG")
|
|
111
|
+
MediLink_ConfigLoader.log("member_id: {}".format(member_id), level="DEBUG")
|
|
112
|
+
MediLink_ConfigLoader.log("npi: {}".format(npi), level="DEBUG")
|
|
110
113
|
|
|
111
114
|
# Call the get_eligibility_v3 function
|
|
112
115
|
eligibility = MediLink_API_v3.get_eligibility_v3(
|
|
@@ -114,7 +117,7 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
114
117
|
)
|
|
115
118
|
|
|
116
119
|
# Log the response
|
|
117
|
-
MediLink_ConfigLoader.log("Eligibility response: {}".format(json.dumps(eligibility, indent=4)), level="
|
|
120
|
+
MediLink_ConfigLoader.log("Eligibility response: {}".format(json.dumps(eligibility, indent=4)), level="DEBUG")
|
|
118
121
|
|
|
119
122
|
return eligibility
|
|
120
123
|
except requests.exceptions.HTTPError as e:
|
|
@@ -127,7 +130,7 @@ def get_eligibility_info(client, payer_id, provider_last_name, date_of_birth, me
|
|
|
127
130
|
return None
|
|
128
131
|
|
|
129
132
|
# Function to extract required fields and display in a tabular format
|
|
130
|
-
def display_eligibility_info(data, dob, member_id):
|
|
133
|
+
def display_eligibility_info(data, dob, member_id, output_file):
|
|
131
134
|
if data is None:
|
|
132
135
|
return
|
|
133
136
|
|
|
@@ -154,50 +157,53 @@ def display_eligibility_info(data, dob, member_id):
|
|
|
154
157
|
ins_payerID = insurance_info.get("payerId", "")
|
|
155
158
|
|
|
156
159
|
policy_info = policy["policyInfo"]
|
|
157
|
-
eligibilityDates = policy_info.get("eligibilityDates", "")
|
|
158
160
|
policy_status = policy_info.get("policyStatus", "")
|
|
159
161
|
|
|
160
162
|
patient_name = "{} {} {}".format(firstName, middleName, lastName).strip()[:20]
|
|
161
163
|
|
|
162
164
|
# Display patient information in a table row format
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
print(table_row)
|
|
167
|
-
table_row_details = "{:<20} | {:<10} | {:<5} | {:<15} | {:<8} | {:<15} | {:<20}".format(
|
|
168
|
-
"", "", "", ins_insuranceTypeCode, eligibility_end_date[-10:], policy_status, remaining_amount)
|
|
169
|
-
print(table_row_details)
|
|
165
|
+
table_row = "{:<20} | {:<10} | {:<40} | {:<5} | {:<14} | {:<14}".format(
|
|
166
|
+
patient_name, dob, ins_insuranceType, ins_payerID, policy_status, remaining_amount)
|
|
167
|
+
output_file.write(table_row + "\n")
|
|
168
|
+
print(table_row) # Print to console for progressive display
|
|
170
169
|
|
|
171
170
|
# Print the table header once before entering the loop
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
""
|
|
178
|
-
print(
|
|
179
|
-
print("-" * len(
|
|
180
|
-
|
|
181
|
-
# Set to keep track of processed patients
|
|
182
|
-
processed_patients = set()
|
|
183
|
-
|
|
184
|
-
# Loop through each payer_id and patient to call the API, then display the eligibility information
|
|
185
|
-
errors = []
|
|
186
|
-
for payer_id in payer_ids:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
# Display errors if any
|
|
200
|
-
if errors:
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
print(
|
|
171
|
+
output_file_path = os.path.join(os.getenv('TEMP'), 'eligibility_report.txt')
|
|
172
|
+
with open(output_file_path, 'w') as output_file:
|
|
173
|
+
table_header = "{:<20} | {:<10} | {:<40} | {:<5} | {:<14} | {:<14}".format(
|
|
174
|
+
"Patient Name", "DOB", "Insurance Type", "PayID", "Policy Status", "Remaining Amt")
|
|
175
|
+
output_file.write(table_header + "\n")
|
|
176
|
+
output_file.write("-" * len(table_header) + "\n")
|
|
177
|
+
print(table_header)
|
|
178
|
+
print("-" * len(table_header))
|
|
179
|
+
|
|
180
|
+
# Set to keep track of processed patients
|
|
181
|
+
processed_patients = set()
|
|
182
|
+
|
|
183
|
+
# Loop through each payer_id and patient to call the API, then display the eligibility information
|
|
184
|
+
errors = []
|
|
185
|
+
for payer_id in payer_ids:
|
|
186
|
+
for dob, member_id in patients:
|
|
187
|
+
# Skip if this patient has already been processed
|
|
188
|
+
if (dob, member_id) in processed_patients:
|
|
189
|
+
continue
|
|
190
|
+
try:
|
|
191
|
+
eligibility_data = get_eligibility_info(client, payer_id, provider_last_name, dob, member_id, npi)
|
|
192
|
+
if eligibility_data is not None:
|
|
193
|
+
display_eligibility_info(eligibility_data, dob, member_id, output_file) # Display as we get the result
|
|
194
|
+
processed_patients.add((dob, member_id)) # Mark this patient as processed
|
|
195
|
+
except Exception as e:
|
|
196
|
+
errors.append((dob, member_id, str(e)))
|
|
197
|
+
|
|
198
|
+
# Display errors if any
|
|
199
|
+
if errors:
|
|
200
|
+
error_msg = "\nErrors encountered during API calls:\n"
|
|
201
|
+
output_file.write(error_msg)
|
|
202
|
+
print(error_msg)
|
|
203
|
+
for error in errors:
|
|
204
|
+
error_details = "DOB: {}, Member ID: {}, Error: {}\n".format(error[0], error[1], error[2])
|
|
205
|
+
output_file.write(error_details)
|
|
206
|
+
print(error_details)
|
|
207
|
+
|
|
208
|
+
# Open the generated file in Notepad
|
|
209
|
+
os.system('notepad.exe {}'.format(output_file_path))
|