medicafe 0.240419.2__zip
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.
- medicafe-0.240419.2/LICENSE +21 -0
- medicafe-0.240419.2/MANIFEST.in +2 -0
- medicafe-0.240419.2/MediBot/MediBot.bat +70 -0
- medicafe-0.240419.2/MediBot/MediBot.py +316 -0
- medicafe-0.240419.2/MediBot/MediBot_Charges.py +28 -0
- medicafe-0.240419.2/MediBot/MediBot_Preprocessor.py +283 -0
- medicafe-0.240419.2/MediBot/MediBot_UI.py +190 -0
- medicafe-0.240419.2/MediBot/MediBot_dataformat_library.py +145 -0
- medicafe-0.240419.2/MediBot/MediPost.py +5 -0
- medicafe-0.240419.2/MediBot/PDF_to_CSV_Cleaner.py +211 -0
- medicafe-0.240419.2/MediBot/__init__.py +0 -0
- medicafe-0.240419.2/MediBot/update_json.py +43 -0
- medicafe-0.240419.2/MediBot/update_medicafe.py +19 -0
- medicafe-0.240419.2/MediLink/MediLink.py +277 -0
- medicafe-0.240419.2/MediLink/MediLink_277_decoder.py +92 -0
- medicafe-0.240419.2/MediLink/MediLink_837p_encoder.py +392 -0
- medicafe-0.240419.2/MediLink/MediLink_837p_encoder_library.py +679 -0
- medicafe-0.240419.2/MediLink/MediLink_ConfigLoader.py +69 -0
- medicafe-0.240419.2/MediLink/MediLink_DataMgmt.py +206 -0
- medicafe-0.240419.2/MediLink/MediLink_Down.py +151 -0
- medicafe-0.240419.2/MediLink/MediLink_ERA_decoder.py +192 -0
- medicafe-0.240419.2/MediLink/MediLink_Gmail.py +4 -0
- medicafe-0.240419.2/MediLink/MediLink_Scheduler.py +132 -0
- medicafe-0.240419.2/MediLink/MediLink_StatusCheck.py +4 -0
- medicafe-0.240419.2/MediLink/MediLink_UI.py +116 -0
- medicafe-0.240419.2/MediLink/MediLink_Up.py +117 -0
- medicafe-0.240419.2/MediLink/MediLink_batch.bat +7 -0
- medicafe-0.240419.2/MediLink/Soumit_api.py +22 -0
- medicafe-0.240419.2/MediLink/__init__.py +0 -0
- medicafe-0.240419.2/PKG-INFO +11 -0
- medicafe-0.240419.2/README.md +28 -0
- medicafe-0.240419.2/medicafe.egg-info/PKG-INFO +11 -0
- medicafe-0.240419.2/medicafe.egg-info/SOURCES.txt +37 -0
- medicafe-0.240419.2/medicafe.egg-info/dependency_links.txt +1 -0
- medicafe-0.240419.2/medicafe.egg-info/not-zip-safe +1 -0
- medicafe-0.240419.2/medicafe.egg-info/requires.txt +5 -0
- medicafe-0.240419.2/medicafe.egg-info/top_level.txt +2 -0
- medicafe-0.240419.2/setup.cfg +5 -0
- medicafe-0.240419.2/setup.py +28 -0
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
import re
|
|
2
|
+
from datetime import datetime
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import MediLink_837p_encoder_library
|
|
6
|
+
import MediLink_ConfigLoader
|
|
7
|
+
from MediLink_DataMgmt import parse_fixed_width_data, read_fixed_width_data
|
|
8
|
+
#from tqdm import tqdm
|
|
9
|
+
|
|
10
|
+
"""
|
|
11
|
+
Development Task List:
|
|
12
|
+
|
|
13
|
+
1. File Path Management: Enhance the handling of input paths to efficiently manage both individual files and directories, accommodating a range of file processing scenarios.
|
|
14
|
+
2. User Interface Improvement: Advance the CLI for intuitive user interaction, offering clear options for file processing and real-time progress updates.
|
|
15
|
+
3. Validation and Logging: Strengthen validation processes for input data, incorporating thorough checks against business rules and enhanced detailed logging for improved traceability and troubleshooting.
|
|
16
|
+
4. Batch Processing and Output Handling: Enhance output file management to support efficient batch operations, including systematic naming and organization for output files.
|
|
17
|
+
5. Comprehensive Documentation: Maintain up-to-date and detailed documentation within the codebase, ensuring all functions and complex logic are clearly explained.
|
|
18
|
+
6. De-persisting Intermediate Files.
|
|
19
|
+
"""
|
|
20
|
+
def create_interchange_elements(config, endpoint, transaction_set_control_number):
|
|
21
|
+
"""
|
|
22
|
+
Create interchange headers and trailers for an 837P document.
|
|
23
|
+
|
|
24
|
+
Parameters:
|
|
25
|
+
- config: Configuration settings loaded from a JSON file.
|
|
26
|
+
- endpoint_key: The endpoint for which the data is being processed.
|
|
27
|
+
- transaction_set_control_number: The starting transaction set control number.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
- Tuple containing (ISA header, GS header, GE trailer, IEA trailer).
|
|
31
|
+
"""
|
|
32
|
+
isa_header, gs_header = MediLink_837p_encoder_library.create_interchange_header(config, endpoint)
|
|
33
|
+
ge_trailer, iea_trailer = MediLink_837p_encoder_library.create_interchange_trailer(config, endpoint, transaction_set_control_number)
|
|
34
|
+
return isa_header, gs_header, ge_trailer, iea_trailer
|
|
35
|
+
|
|
36
|
+
def format_single_claim(patient_data, config, endpoint, transaction_set_control_number):
|
|
37
|
+
"""
|
|
38
|
+
Formats a single claim into 837P segments based on the provided patient data and endpoint.
|
|
39
|
+
|
|
40
|
+
Parameters:
|
|
41
|
+
- patient_data: Dictionary containing detailed patient data.
|
|
42
|
+
- config: Configuration settings loaded from a JSON file.
|
|
43
|
+
- endpoint: The endpoint key representing the specific endpoint.
|
|
44
|
+
- transaction_set_control_number: Starting transaction set control number for 837P segments.
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
- String representation of the formatted 837P claim.
|
|
48
|
+
"""
|
|
49
|
+
segments = []
|
|
50
|
+
|
|
51
|
+
# Initialize with standard segments for all claims
|
|
52
|
+
segments.append(MediLink_837p_encoder_library.create_st_segment(transaction_set_control_number))
|
|
53
|
+
segments.append(MediLink_837p_encoder_library.create_bht_segment(patient_data))
|
|
54
|
+
|
|
55
|
+
# Submitter Name Segment and PER Contact Information (1000A Loop)
|
|
56
|
+
segments.extend(MediLink_837p_encoder_library.create_1000A_submitter_name_segment(config, endpoint))
|
|
57
|
+
|
|
58
|
+
# Receiver Name Segment (1000B Loop)
|
|
59
|
+
segments.extend([MediLink_837p_encoder_library.create_1000B_receiver_name_segment(config, endpoint)])
|
|
60
|
+
|
|
61
|
+
# Billing Provider Segments (2010AA Loop)
|
|
62
|
+
segments.extend([MediLink_837p_encoder_library.create_hl_billing_provider_segment()])
|
|
63
|
+
segments.extend(MediLink_837p_encoder_library.create_nm1_billing_provider_segment(config, endpoint))
|
|
64
|
+
|
|
65
|
+
# Pay-To Address Segment (2010AB Loop) if the Pay-To Address differs from the Billing Provider's address
|
|
66
|
+
#segments.extend(MediLink_837p_encoder_library.create_nm1_payto_address_segments(config))
|
|
67
|
+
|
|
68
|
+
# PRV Provider Taxonomy Segment
|
|
69
|
+
#segments.extend([MediLink_837p_encoder_library.create_billing_prv_segment(config, endpoint)])
|
|
70
|
+
|
|
71
|
+
# Subscriber information, possibly including endpoint-specific logic
|
|
72
|
+
segments.extend(MediLink_837p_encoder_library.create_hl_subscriber_segment())
|
|
73
|
+
segments.append(MediLink_837p_encoder_library.create_sbr_segment(config, patient_data, endpoint))
|
|
74
|
+
segments.append(MediLink_837p_encoder_library.create_nm1_subscriber_segment(config, patient_data, endpoint))
|
|
75
|
+
segments.extend(MediLink_837p_encoder_library.create_subscriber_address_segments(patient_data))
|
|
76
|
+
segments.append(MediLink_837p_encoder_library.create_dmg_segment(patient_data))
|
|
77
|
+
|
|
78
|
+
# Payer information (2010BB loop)
|
|
79
|
+
segments.extend([MediLink_837p_encoder_library.create_2010BB_payer_information_segment(patient_data, config, endpoint)])
|
|
80
|
+
#segments.extend(MediLink_837p_encoder_library.create_payer_address_segments(config)) OMITTED
|
|
81
|
+
|
|
82
|
+
# Rendering Provider (2310B Loop)
|
|
83
|
+
segments.extend(MediLink_837p_encoder_library.create_nm1_rendering_provider_segment(config))
|
|
84
|
+
|
|
85
|
+
# Claim information 2300, 2310C Service Facility and 2400 loop segments
|
|
86
|
+
segments.extend(MediLink_837p_encoder_library.create_clm_and_related_segments(patient_data, config))
|
|
87
|
+
|
|
88
|
+
# Placeholder for the SE segment to be updated with actual segment count later
|
|
89
|
+
segments.append("SE**{transaction_set_control_number:04d}~")
|
|
90
|
+
|
|
91
|
+
# Update SE segment with the actual segment count and generate the final formatted 837P string
|
|
92
|
+
formatted_837p = MediLink_837p_encoder_library.generate_segment_counts('\n'.join(filter(None, segments)), transaction_set_control_number)
|
|
93
|
+
|
|
94
|
+
# Optionally, print or log the formatted 837P for debugging or verification
|
|
95
|
+
# BUG Add chart number to this.
|
|
96
|
+
MediLink_837p_encoder_library.log("Formatted 837P for endpoint {}.".format(endpoint), config, level="INFO")
|
|
97
|
+
|
|
98
|
+
return formatted_837p
|
|
99
|
+
|
|
100
|
+
def write_output_file(document_segments, output_directory, endpoint_key, input_file_path, config):
|
|
101
|
+
"""
|
|
102
|
+
Writes formatted 837P document segments to an output file with a dynamically generated name.
|
|
103
|
+
|
|
104
|
+
Development Roadmap:
|
|
105
|
+
- Ensure input `document_segments` is a non-empty list to avoid creating empty files.
|
|
106
|
+
- Verify `output_directory` exists and is writable before proceeding. Create the directory if it does not exist.
|
|
107
|
+
- Consider parameterizing the file naming convention or providing options for customization to accommodate different organizational needs.
|
|
108
|
+
- Implement error handling to gracefully manage file writing failures, potentially returning a status or error message alongside the file path.
|
|
109
|
+
- Incorporate logging directly within the function, accepting an optional `config` or `logger` parameter to facilitate tracking of the file writing process and outcomes.
|
|
110
|
+
- Update the return value to include both the path to the output file and any relevant status information (e.g., success flag, error message) to enhance downstream error handling and user feedback.
|
|
111
|
+
|
|
112
|
+
Parameters:
|
|
113
|
+
- document_segments: List of strings, where each string is a segment of the 837P document to be written.
|
|
114
|
+
- output_directory: String specifying the directory where the output file will be saved.
|
|
115
|
+
- endpoint_key: String specifying the endpoint for which the claim is processed, used in naming the output file.
|
|
116
|
+
- input_file_path: String specifying the path to the input file being processed, used in naming the output file.
|
|
117
|
+
|
|
118
|
+
Returns:
|
|
119
|
+
- String specifying the path to the successfully created output file. Consider returning a tuple (path, status) for enhanced error handling.
|
|
120
|
+
"""
|
|
121
|
+
formatted_837p = '\n'.join(document_segments)
|
|
122
|
+
base_name = os.path.splitext(os.path.basename(input_file_path))[0]
|
|
123
|
+
timestamp = datetime.now().strftime("%m%d%H%M")
|
|
124
|
+
new_output_file_name = "{}_{}_{}.txt".format(base_name, endpoint_key.lower(), timestamp)
|
|
125
|
+
new_output_file_path = os.path.join(output_directory, new_output_file_name)
|
|
126
|
+
|
|
127
|
+
with open(new_output_file_path, 'w') as output_file:
|
|
128
|
+
output_file.write(formatted_837p)
|
|
129
|
+
|
|
130
|
+
# need to pass config here. for what?
|
|
131
|
+
# BUG Validate that the output file path has no spaces in it, Unless this is no issue (skip)
|
|
132
|
+
MediLink_837p_encoder_library.log("Successfully converted and saved to {}".format(new_output_file_path), config, level="INFO")
|
|
133
|
+
|
|
134
|
+
return new_output_file_path
|
|
135
|
+
|
|
136
|
+
def process_file(file_path, config, endpoint_key, transaction_set_control_number):
|
|
137
|
+
"""
|
|
138
|
+
Reads, validates, and formats claim data from a given file into the 837P format.
|
|
139
|
+
|
|
140
|
+
:param file_path: The path to the file containing claim data.
|
|
141
|
+
:param config: Configuration settings loaded from a JSON file.
|
|
142
|
+
:param endpoint_key: The key representing the endpoint for which the claim is being processed.
|
|
143
|
+
:param transaction_set_control_number: The starting transaction set control number for 837P segments.
|
|
144
|
+
:return: A tuple containing the formatted claim segments and the next transaction set control number.
|
|
145
|
+
"""
|
|
146
|
+
formatted_claims = []
|
|
147
|
+
|
|
148
|
+
for personal_info, insurance_info, service_info in read_fixed_width_data(file_path, config):
|
|
149
|
+
# Parse and validate claim data
|
|
150
|
+
parsed_data = parse_fixed_width_data(personal_info, insurance_info, service_info, config)
|
|
151
|
+
|
|
152
|
+
# Assume validate_claim_data is a function that validates the parsed data
|
|
153
|
+
# DISABLED
|
|
154
|
+
#if not validate_claim_data(parsed_data, config):
|
|
155
|
+
# MediLink_837p_encoder_library.log("Validation failed for claim data in file: {}".format(file_path), config, level="ERROR")
|
|
156
|
+
# continue # Skip invalid claims
|
|
157
|
+
|
|
158
|
+
# Format the claim into 837P segments
|
|
159
|
+
formatted_claim = format_single_claim(parsed_data, config, endpoint_key, transaction_set_control_number)
|
|
160
|
+
formatted_claims.append(formatted_claim)
|
|
161
|
+
transaction_set_control_number += 1 # Increment for each successfully processed claim
|
|
162
|
+
|
|
163
|
+
# Combine all formatted claims into a single string to be appended to the document segments
|
|
164
|
+
formatted_claims_str = '\n'.join(formatted_claims)
|
|
165
|
+
|
|
166
|
+
return formatted_claims_str, transaction_set_control_number
|
|
167
|
+
|
|
168
|
+
def winscp_validate_output_directory(output_directory):
|
|
169
|
+
"""
|
|
170
|
+
Validates the output directory path to ensure it has no spaces.
|
|
171
|
+
If spaces are found, prompts the user to input a new path.
|
|
172
|
+
If the directory doesn't exist, creates it.
|
|
173
|
+
"""
|
|
174
|
+
while ' ' in output_directory:
|
|
175
|
+
print("\nWARNING: The output directory path contains spaces, which can cause issues with upload operations.")
|
|
176
|
+
print(" Current proposed path: {}".format(output_directory))
|
|
177
|
+
new_path = input("Please enter a new path for the output directory: ")
|
|
178
|
+
output_directory = new_path.strip() # Remove leading/trailing spaces
|
|
179
|
+
|
|
180
|
+
# Check if the directory exists, if not, create it
|
|
181
|
+
if not os.path.exists(output_directory):
|
|
182
|
+
os.makedirs(output_directory)
|
|
183
|
+
print("INFO: Created output directory: {}".format(output_directory))
|
|
184
|
+
|
|
185
|
+
return output_directory
|
|
186
|
+
|
|
187
|
+
def convert_files_for_submission(detailed_patient_data, config):
|
|
188
|
+
"""
|
|
189
|
+
Processes detailed patient data for submission based on their confirmed endpoints,
|
|
190
|
+
generating a separate 837P file for each endpoint.
|
|
191
|
+
|
|
192
|
+
Parameters:
|
|
193
|
+
- detailed_patient_data: A list containing detailed patient data with endpoint information.
|
|
194
|
+
- config: Configuration settings loaded from a JSON file.
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
- A list of paths to the converted files ready for submission.
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
# Initialize a dictionary to hold patient data segregated by confirmed endpoints
|
|
201
|
+
data_by_endpoint = {}
|
|
202
|
+
|
|
203
|
+
# Populate the dictionary with patient data
|
|
204
|
+
for data in detailed_patient_data:
|
|
205
|
+
endpoint = data['confirmed_endpoint']
|
|
206
|
+
if endpoint not in data_by_endpoint:
|
|
207
|
+
data_by_endpoint[endpoint] = []
|
|
208
|
+
data_by_endpoint[endpoint].append(data)
|
|
209
|
+
|
|
210
|
+
# List to store paths of converted files for each endpoint
|
|
211
|
+
converted_files_paths = []
|
|
212
|
+
|
|
213
|
+
# Determine the total number of unique endpoints for progress bar
|
|
214
|
+
# total_endpoints = len(data_by_endpoint)
|
|
215
|
+
|
|
216
|
+
# Iterate over each endpoint and process its corresponding patient data
|
|
217
|
+
for endpoint, patient_data_list in data_by_endpoint.items():
|
|
218
|
+
# tqdm(data_by_endpoint.items(), desc="Creating 837p(s)", total=total_endpoints, ascii=True)
|
|
219
|
+
# Each endpoint might have multiple patients' data to be processed into a single 837P file
|
|
220
|
+
if patient_data_list:
|
|
221
|
+
converted_path = process_claim(config['MediLink_Config'], endpoint, patient_data_list)
|
|
222
|
+
if converted_path:
|
|
223
|
+
converted_files_paths.append(converted_path)
|
|
224
|
+
|
|
225
|
+
return converted_files_paths
|
|
226
|
+
|
|
227
|
+
def process_claim(config, endpoint, patient_data_list):
|
|
228
|
+
"""
|
|
229
|
+
Processes patient data for a specified endpoint, converting it into the 837P format.
|
|
230
|
+
Generates a separate 837P file for each endpoint based on detailed patient data.
|
|
231
|
+
|
|
232
|
+
Parameters:
|
|
233
|
+
- config: Configuration settings loaded from a JSON file.
|
|
234
|
+
- endpoint_key: The key representing the endpoint for which the data is being processed.
|
|
235
|
+
- patient_data_list: A list of dictionaries, each containing detailed patient data.
|
|
236
|
+
|
|
237
|
+
Returns:
|
|
238
|
+
- Path to the converted file, or None if an error occurs.
|
|
239
|
+
"""
|
|
240
|
+
# Retrieve endpoint-specific configuration
|
|
241
|
+
endpoint_config = config['endpoints'].get(endpoint)
|
|
242
|
+
if not endpoint_config:
|
|
243
|
+
print("Endpoint configuration for {} not found.".format(endpoint))
|
|
244
|
+
return None
|
|
245
|
+
|
|
246
|
+
# Retrieve desired default output file path from config
|
|
247
|
+
output_directory = config.get('outputFilePath', '')
|
|
248
|
+
|
|
249
|
+
# BUG This is a WinSCP validation because of the mishandling of spaces in paths.
|
|
250
|
+
# This shouldn't need to exist.
|
|
251
|
+
output_directory = winscp_validate_output_directory(output_directory)
|
|
252
|
+
|
|
253
|
+
if not os.path.isdir(output_directory):
|
|
254
|
+
print("Output directory does not exist. Please check the configuration.")
|
|
255
|
+
return None
|
|
256
|
+
|
|
257
|
+
# Initialize document segments with headers
|
|
258
|
+
isa_header, gs_header = MediLink_837p_encoder_library.create_interchange_header(config, endpoint)
|
|
259
|
+
document_segments = [isa_header, gs_header]
|
|
260
|
+
|
|
261
|
+
# Initialize the transaction set control number
|
|
262
|
+
transaction_set_control_number = 1
|
|
263
|
+
|
|
264
|
+
# Process each patient's data in the list
|
|
265
|
+
for patient_data in patient_data_list:
|
|
266
|
+
# Assuming validate_claim_data is adapted to handle individual patient data dictionaries
|
|
267
|
+
if True: #validate_claim_data(patient_data, config): BUG DISABLED VALIDATION
|
|
268
|
+
# The format_single_claim function needs to be adapted to handle individual patient data dictionaries
|
|
269
|
+
formatted_claim = format_single_claim(patient_data, config, endpoint, transaction_set_control_number)
|
|
270
|
+
document_segments.append(formatted_claim)
|
|
271
|
+
transaction_set_control_number += 1
|
|
272
|
+
|
|
273
|
+
# Append interchange trailer
|
|
274
|
+
ge_trailer, iea_trailer = MediLink_837p_encoder_library.create_interchange_trailer(config, endpoint, transaction_set_control_number - 1)
|
|
275
|
+
document_segments.extend([ge_trailer, iea_trailer])
|
|
276
|
+
|
|
277
|
+
# Write the complete 837P document to an output file and return its path
|
|
278
|
+
return write_output_file(document_segments, output_directory, endpoint, patient_data_list[0]['file_path'], config)
|
|
279
|
+
|
|
280
|
+
# Validation Function checks the completeness and correctness of each claim's data
|
|
281
|
+
def validate_claim_data(parsed_data, config):
|
|
282
|
+
required_fields = ['CHART', 'billing_provider_npi', 'IPOLICY', 'CODEA', 'DATE', 'AMOUNT', 'TOS', 'DIAG']
|
|
283
|
+
errors = []
|
|
284
|
+
|
|
285
|
+
# Check for missing or empty fields
|
|
286
|
+
for field in required_fields:
|
|
287
|
+
if not parsed_data.get(field):
|
|
288
|
+
errors.append("Missing or empty field: {}".format(field))
|
|
289
|
+
|
|
290
|
+
# Validate NPI format
|
|
291
|
+
if not re.match(r'^\d{10}$', parsed_data.get('billing_provider_npi', '')):
|
|
292
|
+
errors.append("Invalid NPI format: {}".format(parsed_data.get('billing_provider_npi')))
|
|
293
|
+
|
|
294
|
+
# Validate date format
|
|
295
|
+
try:
|
|
296
|
+
datetime.strptime(parsed_data.get('DATE'), "%Y%m%d")
|
|
297
|
+
except ValueError:
|
|
298
|
+
errors.append("Invalid date format: {}".format(parsed_data.get('DATE')))
|
|
299
|
+
|
|
300
|
+
# Log validation errors
|
|
301
|
+
if errors:
|
|
302
|
+
for error in errors:
|
|
303
|
+
MediLink_837p_encoder_library.log(error, config, level="ERROR")
|
|
304
|
+
return False
|
|
305
|
+
|
|
306
|
+
return True
|
|
307
|
+
|
|
308
|
+
if __name__ == "__main__":
|
|
309
|
+
"""
|
|
310
|
+
Converts fixed-width files to 837P format for health claim submissions.
|
|
311
|
+
|
|
312
|
+
Usage:
|
|
313
|
+
------
|
|
314
|
+
1. Convert a single file:
|
|
315
|
+
python MediLink_837p_encoder.py -e [endpoint] -p [file_path]
|
|
316
|
+
|
|
317
|
+
2. Convert all files in a directory:
|
|
318
|
+
python MediLink_837p_encoder.py -e [endpoint] -p [directory_path] -d
|
|
319
|
+
|
|
320
|
+
Arguments:
|
|
321
|
+
----------
|
|
322
|
+
- "-e": Specify endpoint ("AVAILITY", "OPTUMEDI", "PNT_DATA").
|
|
323
|
+
- "-p": Path to file/directory for processing.
|
|
324
|
+
- "-d": Flag for directory processing.
|
|
325
|
+
|
|
326
|
+
Note: Ensure correct config file path.
|
|
327
|
+
"""
|
|
328
|
+
parser = argparse.ArgumentParser(
|
|
329
|
+
description="Converts fixed-width files to the 837P format for health claim submissions. Supports processing individual files or all files within a directory.",
|
|
330
|
+
formatter_class=argparse.ArgumentDefaultsHelpFormatter
|
|
331
|
+
)
|
|
332
|
+
parser.add_argument(
|
|
333
|
+
"-e", "--endpoint",
|
|
334
|
+
required=True,
|
|
335
|
+
choices=["AVAILITY", "OPTUMEDI", "PNT_DATA"],
|
|
336
|
+
help="Specify the endpoint for which the conversion is intended."
|
|
337
|
+
)
|
|
338
|
+
parser.add_argument(
|
|
339
|
+
"-p", "--path",
|
|
340
|
+
required=True,
|
|
341
|
+
help="Path to the input fixed-width file or directory to process. If a directory is provided, all .DAT files within will be processed."
|
|
342
|
+
)
|
|
343
|
+
parser.add_argument(
|
|
344
|
+
"-d", "--is-directory",
|
|
345
|
+
action='store_true',
|
|
346
|
+
help="Flag indicating the path provided is a directory. If set, all .DAT files within the directory will be processed."
|
|
347
|
+
)
|
|
348
|
+
args = parser.parse_args()
|
|
349
|
+
|
|
350
|
+
print("Starting the conversion process for {}. Processing {} at '{}'.".format(args.endpoint, 'directory' if args.is_directory else 'file', args.path))
|
|
351
|
+
|
|
352
|
+
# Load configuration
|
|
353
|
+
config, _ = MediLink_ConfigLoader.load_configuration()
|
|
354
|
+
|
|
355
|
+
def process_and_write_file(file_path, config, endpoint, starting_tscn=1):
|
|
356
|
+
"""
|
|
357
|
+
Process a single file, create complete 837P document with headers and trailers, and write to output file.
|
|
358
|
+
|
|
359
|
+
Parameters:
|
|
360
|
+
- file_path: Path to the .DAT file to be processed.
|
|
361
|
+
- config: Configuration settings.
|
|
362
|
+
- endpoint: Endpoint key.
|
|
363
|
+
- starting_tscn: Starting Transaction Set Control Number.
|
|
364
|
+
"""
|
|
365
|
+
print("Processing: {}".format(file_path))
|
|
366
|
+
formatted_data, transaction_set_control_number = process_file(file_path, config, endpoint, starting_tscn)
|
|
367
|
+
isa_header, gs_header, ge_trailer, iea_trailer = create_interchange_elements(config, endpoint, transaction_set_control_number - 1)
|
|
368
|
+
|
|
369
|
+
# Combine everything into a single document
|
|
370
|
+
complete_document = "{}\n{}\n{}\n{}\n{}".format(
|
|
371
|
+
isa_header,
|
|
372
|
+
gs_header,
|
|
373
|
+
formatted_data,
|
|
374
|
+
ge_trailer,
|
|
375
|
+
iea_trailer
|
|
376
|
+
)
|
|
377
|
+
|
|
378
|
+
# Write to output file
|
|
379
|
+
output_file_path = write_output_file([complete_document], config.get('outputFilePath', ''), endpoint, file_path, config)
|
|
380
|
+
print("File processed. Output saved to: {}".format(output_file_path))
|
|
381
|
+
|
|
382
|
+
if args.is_directory:
|
|
383
|
+
# Process each .DAT file within the directory
|
|
384
|
+
for file_name in os.listdir(args.path):
|
|
385
|
+
if file_name.endswith(".DAT"):
|
|
386
|
+
file_path = os.path.join(args.path, file_name)
|
|
387
|
+
process_and_write_file(file_path, config, args.endpoint)
|
|
388
|
+
else:
|
|
389
|
+
# Process a single file
|
|
390
|
+
process_and_write_file(args.path, config, args.endpoint)
|
|
391
|
+
|
|
392
|
+
print("Conversion complete.")
|