medicafe 0.240613.0__py3-none-any.whl → 0.240809.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of medicafe might be problematic. Click here for more details.
- MediBot/MediBot.bat +37 -5
- MediBot/MediBot.py +13 -2
- MediBot/MediBot_Crosswalk_Library.py +15 -8
- MediBot/MediBot_Preprocessor_lib.py +14 -2
- MediBot/MediBot_docx_decoder.py +13 -5
- MediLink/MediLink.py +42 -77
- MediLink/MediLink_837p_encoder.py +64 -47
- MediLink/MediLink_837p_encoder_library.py +24 -35
- MediLink/MediLink_API_Generator.py +246 -0
- MediLink/MediLink_API_v2.py +2 -0
- MediLink/MediLink_API_v3.py +429 -0
- MediLink/MediLink_ClaimStatus.py +144 -0
- MediLink/MediLink_ConfigLoader.py +13 -7
- MediLink/MediLink_DataMgmt.py +4 -4
- MediLink/MediLink_Decoder.py +122 -20
- MediLink/MediLink_Deductible.py +210 -0
- MediLink/MediLink_Down.py +97 -66
- MediLink/MediLink_Parser.py +106 -24
- MediLink/MediLink_UI.py +12 -26
- MediLink/MediLink_Up.py +181 -111
- {medicafe-0.240613.0.dist-info → medicafe-0.240809.0.dist-info}/METADATA +2 -1
- medicafe-0.240809.0.dist-info/RECORD +47 -0
- medicafe-0.240613.0.dist-info/RECORD +0 -43
- {medicafe-0.240613.0.dist-info → medicafe-0.240809.0.dist-info}/LICENSE +0 -0
- {medicafe-0.240613.0.dist-info → medicafe-0.240809.0.dist-info}/WHEEL +0 -0
- {medicafe-0.240613.0.dist-info → medicafe-0.240809.0.dist-info}/top_level.txt +0 -0
|
@@ -12,7 +12,7 @@ sys.path.append(project_dir)
|
|
|
12
12
|
from MediBot import MediBot_Preprocessor_lib
|
|
13
13
|
load_insurance_data_from_mains = MediBot_Preprocessor_lib.load_insurance_data_from_mains
|
|
14
14
|
from MediBot import MediBot_Crosswalk_Library
|
|
15
|
-
from
|
|
15
|
+
from MediLink_API_v3 import fetch_payer_name_from_api
|
|
16
16
|
|
|
17
17
|
# Converts date format from one format to another.
|
|
18
18
|
def convert_date_format(date_str):
|
|
@@ -288,7 +288,7 @@ def resolve_payer_name(payer_id, config, primary_endpoint, insurance_name, parse
|
|
|
288
288
|
MediLink_ConfigLoader.log("API Resolved to standard insurance name: {} for corrected payer ID: {}".format(resolved_name, corrected_payer_id), config, level="INFO")
|
|
289
289
|
|
|
290
290
|
confirmation = input("Is the standard insurance name '{}' correct? (yes/no): ".format(resolved_name)).strip().lower()
|
|
291
|
-
|
|
291
|
+
# BUG There is duplication of code here.
|
|
292
292
|
if confirmation in ['yes', 'y']:
|
|
293
293
|
if MediBot_Crosswalk_Library.update_crosswalk_with_corrected_payer_id(payer_id, corrected_payer_id):
|
|
294
294
|
return resolved_name
|
|
@@ -309,11 +309,16 @@ def resolve_payer_name(payer_id, config, primary_endpoint, insurance_name, parse
|
|
|
309
309
|
|
|
310
310
|
def prompt_user_for_payer_id(insurance_name):
|
|
311
311
|
"""
|
|
312
|
-
Prompts the user to input the payer ID manually.
|
|
312
|
+
Prompts the user to input the payer ID manually and ensures that a valid ID is provided.
|
|
313
313
|
"""
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
314
|
+
while True:
|
|
315
|
+
print("Manual intervention required: No payer ID found for insurance name '{}'.".format(insurance_name))
|
|
316
|
+
payer_id = input("Please enter the payer ID manually: ").strip()
|
|
317
|
+
|
|
318
|
+
if payer_id:
|
|
319
|
+
return payer_id
|
|
320
|
+
else:
|
|
321
|
+
print("Error: Payer ID cannot be empty. Please try again.")
|
|
317
322
|
|
|
318
323
|
def build_nm1_segment(payer_name, payer_id):
|
|
319
324
|
# Step 1: Build NM1 segment using payer name and ID
|
|
@@ -407,7 +412,7 @@ def handle_missing_payer_id(insurance_name, config):
|
|
|
407
412
|
|
|
408
413
|
# Ask for user confirmation
|
|
409
414
|
confirmation = input("Is the standard insurance name '{}' correct? (yes/no): ".format(standard_insurance_name)).strip().lower() or 'yes'
|
|
410
|
-
|
|
415
|
+
# BUG There is duplication of code here.
|
|
411
416
|
if confirmation in ['yes', 'y']:
|
|
412
417
|
# Update the crosswalk with the new payer ID and insurance name mapping
|
|
413
418
|
MediBot_Crosswalk_Library.update_crosswalk_with_new_payer_id(insurance_name, payer_id, config)
|
|
@@ -520,32 +525,9 @@ def insurance_type_selection(parsed_data):
|
|
|
520
525
|
|
|
521
526
|
print("\nInsurance Type Validation Error: Select the insurance type for patient {}: ".format(parsed_data['LAST']))
|
|
522
527
|
|
|
523
|
-
#
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
"12": "Preferred Provider Organization (PPO)",
|
|
527
|
-
"13": "Point of Service (POS)",
|
|
528
|
-
"14": "Exclusive Provider Organization (EPO)",
|
|
529
|
-
"15": "Indemnity Insurance",
|
|
530
|
-
"16": "Health Maintenance Organization (HMO) Medicare Risk",
|
|
531
|
-
"17": "Dental Maintenance Organization",
|
|
532
|
-
"AM": "Automobile Medical",
|
|
533
|
-
"BL": "Blue Cross/Blue Shield",
|
|
534
|
-
"CH": "Champus",
|
|
535
|
-
"CI": "Commercial Insurance Co.",
|
|
536
|
-
"DS": "Disability",
|
|
537
|
-
"FI": "Federal Employees Program",
|
|
538
|
-
"HM": "Health Maintenance Organization",
|
|
539
|
-
"LM": "Liability Medical",
|
|
540
|
-
"MA": "Medicare Part A",
|
|
541
|
-
"MB": "Medicare Part B",
|
|
542
|
-
"MC": "Medicaid",
|
|
543
|
-
"OF": "Other Federal Program",
|
|
544
|
-
"TV": "Title V",
|
|
545
|
-
"VA": "Veterans Affairs Plan",
|
|
546
|
-
"WC": "Workers Compensation Health Claim",
|
|
547
|
-
"ZZ": "Mutually Defined"
|
|
548
|
-
}
|
|
528
|
+
# Retrieve insurance options with codes and descriptions
|
|
529
|
+
config, _ = MediLink_ConfigLoader.load_configuration()
|
|
530
|
+
insurance_options = config['MediLink_Config'].get('insurance_options')
|
|
549
531
|
|
|
550
532
|
def prompt_display_insurance_options():
|
|
551
533
|
# Prompt to display full list
|
|
@@ -870,10 +852,17 @@ def winscp_validate_output_directory(output_directory):
|
|
|
870
852
|
|
|
871
853
|
def get_output_directory(config):
|
|
872
854
|
# Retrieve desired default output file path from config
|
|
873
|
-
output_directory = config.get('outputFilePath', '')
|
|
855
|
+
output_directory = config.get('outputFilePath', '').strip()
|
|
874
856
|
# BUG (Low SFTP) Add WinSCP validation because of the mishandling of spaces in paths. (This shouldn't need to exist.)
|
|
857
|
+
if not output_directory:
|
|
858
|
+
print("Output file path is not specified in the configuration.")
|
|
859
|
+
output_directory = input("Please enter a valid output directory path: ").strip()
|
|
860
|
+
|
|
861
|
+
# Validate the directory path (checks for spaces and existence)
|
|
875
862
|
output_directory = winscp_validate_output_directory(output_directory)
|
|
863
|
+
|
|
876
864
|
if not os.path.isdir(output_directory):
|
|
877
|
-
print("Output directory does not exist. Please check the configuration.")
|
|
865
|
+
print("Output directory does not exist or is not accessible. Please check the configuration.")
|
|
878
866
|
return None
|
|
867
|
+
|
|
879
868
|
return output_directory
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# This script requires Python 3.11 and is to be used for intalling new API clients.
|
|
2
|
+
import os
|
|
3
|
+
import time
|
|
4
|
+
import subprocess
|
|
5
|
+
import shutil
|
|
6
|
+
import tempfile
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from watchdog.observers import Observer
|
|
9
|
+
from watchdog.events import FileSystemEventHandler
|
|
10
|
+
from MediLink_API_v3 import ConfigLoader
|
|
11
|
+
import shlex
|
|
12
|
+
import re
|
|
13
|
+
|
|
14
|
+
class SwaggerHandler(FileSystemEventHandler):
|
|
15
|
+
def __init__(self, json_folder):
|
|
16
|
+
self.json_folder = json_folder
|
|
17
|
+
|
|
18
|
+
def on_created(self, event):
|
|
19
|
+
if event.is_directory:
|
|
20
|
+
return
|
|
21
|
+
if event.src_path.endswith('.yaml') or event.src_path.endswith('.json'):
|
|
22
|
+
print(f"New file detected: {event.src_path}")
|
|
23
|
+
time.sleep(2) # Add a short delay to ensure the file is ready for reading
|
|
24
|
+
self.process_swagger_file(event.src_path)
|
|
25
|
+
|
|
26
|
+
def process_swagger_file(self, file_path):
|
|
27
|
+
print(f"Processing file: {file_path}")
|
|
28
|
+
# Sanitize filename to replace spaces with underscores
|
|
29
|
+
sanitized_file_path = os.path.join(os.path.dirname(file_path), sanitize_filename(os.path.basename(file_path)))
|
|
30
|
+
if sanitized_file_path != file_path:
|
|
31
|
+
os.rename(file_path, sanitized_file_path)
|
|
32
|
+
print(f"Renamed file to: {sanitized_file_path}")
|
|
33
|
+
file_path = sanitized_file_path
|
|
34
|
+
|
|
35
|
+
with tempfile.TemporaryDirectory() as temp_dir:
|
|
36
|
+
temp_dir_path = Path(temp_dir)
|
|
37
|
+
temp_file_path = temp_dir_path / Path(file_path).name
|
|
38
|
+
config_path = temp_dir_path / 'openapi_config.json'
|
|
39
|
+
output_dir = temp_dir_path / "generated_client"
|
|
40
|
+
|
|
41
|
+
shutil.copy(file_path, temp_file_path)
|
|
42
|
+
config_source_path = Path(__file__).parent.parent / 'json' / 'openapi_config.json'
|
|
43
|
+
shutil.copy(config_source_path, config_path)
|
|
44
|
+
print(f"Copied files to: {temp_file_path} and {config_path}")
|
|
45
|
+
|
|
46
|
+
swagger_definitions = ConfigLoader.load_swagger_file(str(temp_file_path))
|
|
47
|
+
if swagger_definitions:
|
|
48
|
+
print(f"Swagger definitions loaded successfully from: {temp_file_path}")
|
|
49
|
+
if generate_api_client(temp_file_path, output_dir, config_path):
|
|
50
|
+
backport_code(output_dir)
|
|
51
|
+
move_generated_client(temp_dir, file_path)
|
|
52
|
+
provide_instructions(file_path)
|
|
53
|
+
else:
|
|
54
|
+
print(f"Failed to load Swagger definitions from: {temp_file_path}")
|
|
55
|
+
|
|
56
|
+
def sanitize_filename(filename):
|
|
57
|
+
return filename.replace(' ', '_')
|
|
58
|
+
|
|
59
|
+
def find_executable(name):
|
|
60
|
+
"""Find the full path to an executable."""
|
|
61
|
+
for path in os.environ["PATH"].split(os.pathsep):
|
|
62
|
+
full_path = Path(path) / name
|
|
63
|
+
if full_path.is_file():
|
|
64
|
+
return str(full_path)
|
|
65
|
+
return None
|
|
66
|
+
|
|
67
|
+
def generate_api_client(swagger_path, output_path, config_path):
|
|
68
|
+
"""
|
|
69
|
+
Generate the API client using openapi-generator-cli.
|
|
70
|
+
"""
|
|
71
|
+
openapi_generator_path = find_executable('openapi-generator-cli.cmd')
|
|
72
|
+
if not openapi_generator_path:
|
|
73
|
+
print("openapi-generator-cli not found in PATH.")
|
|
74
|
+
return False
|
|
75
|
+
|
|
76
|
+
command = [
|
|
77
|
+
openapi_generator_path,
|
|
78
|
+
'generate',
|
|
79
|
+
'-i', str(swagger_path),
|
|
80
|
+
'-g', 'python',
|
|
81
|
+
'-o', str(output_path),
|
|
82
|
+
'-c', str(config_path)
|
|
83
|
+
]
|
|
84
|
+
|
|
85
|
+
print(f"Attempting command: {' '.join(command)}")
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
result = subprocess.run(command, check=True, capture_output=True, text=True)
|
|
89
|
+
print("API client generated successfully.")
|
|
90
|
+
print(result.stdout)
|
|
91
|
+
print(result.stderr)
|
|
92
|
+
return True
|
|
93
|
+
except subprocess.CalledProcessError as e:
|
|
94
|
+
print("First attempt failed.")
|
|
95
|
+
print("Error generating API client:")
|
|
96
|
+
print(e.stdout)
|
|
97
|
+
print(e.stderr)
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
print("Attempting with shell=True.")
|
|
101
|
+
result = subprocess.run(' '.join(command), check=True, shell=True, capture_output=True, text=True)
|
|
102
|
+
print("API client generated successfully with shell=True.")
|
|
103
|
+
print(result.stdout)
|
|
104
|
+
print(result.stderr)
|
|
105
|
+
return True
|
|
106
|
+
except subprocess.CalledProcessError as e:
|
|
107
|
+
print("Second attempt with shell=True failed.")
|
|
108
|
+
print("Error generating API client:")
|
|
109
|
+
print(e.stdout)
|
|
110
|
+
print(e.stderr)
|
|
111
|
+
|
|
112
|
+
try:
|
|
113
|
+
quoted_command = [
|
|
114
|
+
openapi_generator_path,
|
|
115
|
+
'generate',
|
|
116
|
+
'-i', shlex.quote(str(swagger_path)),
|
|
117
|
+
'-g', 'python',
|
|
118
|
+
'-o', shlex.quote(str(output_path)),
|
|
119
|
+
'-c', shlex.quote(str(config_path))
|
|
120
|
+
]
|
|
121
|
+
print(f"Attempting quoted command: {' '.join(quoted_command)}")
|
|
122
|
+
result = subprocess.run(' '.join(quoted_command), check=True, shell=True, capture_output=True, text=True)
|
|
123
|
+
print("API client generated successfully with quoted command.")
|
|
124
|
+
print(result.stdout)
|
|
125
|
+
print(result.stderr)
|
|
126
|
+
return True
|
|
127
|
+
except subprocess.CalledProcessError as e:
|
|
128
|
+
print("Third attempt with quoted command failed.")
|
|
129
|
+
print("Error generating API client:")
|
|
130
|
+
print(e.stdout)
|
|
131
|
+
print(e.stderr)
|
|
132
|
+
|
|
133
|
+
try:
|
|
134
|
+
print("Attempting with batch script.")
|
|
135
|
+
batch_script_content = f"""
|
|
136
|
+
@echo off
|
|
137
|
+
{openapi_generator_path} generate -i "{swagger_path}" -g python -o "{output_path}" -c "{config_path}"
|
|
138
|
+
"""
|
|
139
|
+
batch_script_path = Path(tempfile.gettempdir()) / "generate_client.bat"
|
|
140
|
+
with open(batch_script_path, 'w') as batch_script:
|
|
141
|
+
batch_script.write(batch_script_content)
|
|
142
|
+
|
|
143
|
+
result = subprocess.run(str(batch_script_path), check=True, shell=True, capture_output=True, text=True)
|
|
144
|
+
print("API client generated successfully with batch script.")
|
|
145
|
+
print(result.stdout)
|
|
146
|
+
print(result.stderr)
|
|
147
|
+
return True
|
|
148
|
+
except subprocess.CalledProcessError as e:
|
|
149
|
+
print("Fourth attempt with batch script failed.")
|
|
150
|
+
print("Error generating API client:")
|
|
151
|
+
print(e.stdout)
|
|
152
|
+
print(e.stderr)
|
|
153
|
+
|
|
154
|
+
print("All attempts to generate API client failed.")
|
|
155
|
+
return False
|
|
156
|
+
|
|
157
|
+
def backport_code(output_dir):
|
|
158
|
+
for root, _, files in os.walk(output_dir):
|
|
159
|
+
for file in files:
|
|
160
|
+
if file.endswith('.py'):
|
|
161
|
+
file_path = os.path.join(root, file)
|
|
162
|
+
with open(file_path, 'r') as f:
|
|
163
|
+
code = f.read()
|
|
164
|
+
|
|
165
|
+
code = re.sub(r'f"(.*?)"', r'"\1".format', code) # Replace f-strings
|
|
166
|
+
code = re.sub(r'async def', 'def', code) # Remove async syntax
|
|
167
|
+
code = re.sub(r'await ', '', code) # Remove await syntax
|
|
168
|
+
code = re.sub(r':\s*\w+ =', '=', code) # Remove type hints in variable assignments
|
|
169
|
+
code = re.sub(r'def (\w+)\(.*?\) -> .*?:', r'def \1(', code) # Remove type hints in function definitions
|
|
170
|
+
code = re.sub(r'(from pydantic import.*)', '# \1', code) # Comment out pydantic imports
|
|
171
|
+
code = re.sub(r'(import pydantic)', '# \1', code) # Comment out pydantic imports
|
|
172
|
+
|
|
173
|
+
with open(file_path, 'w') as f:
|
|
174
|
+
f.write(code)
|
|
175
|
+
print(f"Backported {file_path}")
|
|
176
|
+
|
|
177
|
+
def move_generated_client(temp_dir, original_file_path):
|
|
178
|
+
api_name = Path(original_file_path).stem
|
|
179
|
+
destination_dir = Path('generated_clients') / api_name
|
|
180
|
+
if destination_dir.exists():
|
|
181
|
+
shutil.rmtree(destination_dir)
|
|
182
|
+
try:
|
|
183
|
+
shutil.move(str(Path(temp_dir) / 'generated_client'), str(destination_dir))
|
|
184
|
+
print(f"Moved generated client to {destination_dir}")
|
|
185
|
+
except FileNotFoundError as e:
|
|
186
|
+
print(f"Error moving generated client: {e}")
|
|
187
|
+
except Exception as e:
|
|
188
|
+
print(f"Unexpected error moving generated client: {e}")
|
|
189
|
+
|
|
190
|
+
def provide_instructions(swagger_path):
|
|
191
|
+
api_name = Path(swagger_path).stem
|
|
192
|
+
instructions = f"""
|
|
193
|
+
API Client for {api_name} has been generated successfully.
|
|
194
|
+
|
|
195
|
+
Integration Steps:
|
|
196
|
+
1. Locate the generated client code in the 'generated_clients/{api_name}' directory.
|
|
197
|
+
2. Integrate the generated client code into your project by following these steps:
|
|
198
|
+
a. Copy the generated client directory to your project's client directory.
|
|
199
|
+
b. Import the client classes in your project as needed.
|
|
200
|
+
c. Ensure that the generated client adheres to the BaseAPIClient interface if integrating with the existing system.
|
|
201
|
+
3. Update your configuration to include the new API endpoint details.
|
|
202
|
+
4. Test the integration thoroughly to ensure compatibility and functionality.
|
|
203
|
+
|
|
204
|
+
Example Integration:
|
|
205
|
+
from clients.generated.{api_name}.api_client import ApiClient as {api_name}Client
|
|
206
|
+
|
|
207
|
+
class New{api_name}APIClient(BaseAPIClient):
|
|
208
|
+
def __init__(self, config):
|
|
209
|
+
super().__init__(config)
|
|
210
|
+
self.generated_client = {api_name}Client()
|
|
211
|
+
|
|
212
|
+
def get_access_token(self, endpoint_name):
|
|
213
|
+
# Implement token retrieval logic
|
|
214
|
+
pass
|
|
215
|
+
|
|
216
|
+
def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None):
|
|
217
|
+
# Use the generated client to make the API call
|
|
218
|
+
if call_type == 'GET':
|
|
219
|
+
response = self.generated_client.call_api(url_extension, 'GET', params=params, header_params=headers)
|
|
220
|
+
elif call_type == 'POST':
|
|
221
|
+
response = self.generated_client.call_api(url_extension, 'POST', body=data, header_params=headers)
|
|
222
|
+
elif call_type == 'DELETE':
|
|
223
|
+
response = self.generated_client.call_api(url_extension, 'DELETE', header_params=headers)
|
|
224
|
+
else:
|
|
225
|
+
raise ValueError("Unsupported call type")
|
|
226
|
+
return response
|
|
227
|
+
|
|
228
|
+
"""
|
|
229
|
+
print(instructions)
|
|
230
|
+
|
|
231
|
+
def main():
|
|
232
|
+
json_folder = os.path.normpath(os.path.join(os.path.dirname(__file__), '..', 'json'))
|
|
233
|
+
event_handler = SwaggerHandler(json_folder)
|
|
234
|
+
observer = Observer()
|
|
235
|
+
observer.schedule(event_handler, path=json_folder, recursive=False)
|
|
236
|
+
observer.start()
|
|
237
|
+
print(f"Monitoring folder: {json_folder}")
|
|
238
|
+
try:
|
|
239
|
+
while True:
|
|
240
|
+
time.sleep(1)
|
|
241
|
+
except KeyboardInterrupt:
|
|
242
|
+
observer.stop()
|
|
243
|
+
observer.join()
|
|
244
|
+
|
|
245
|
+
if __name__ == "__main__":
|
|
246
|
+
main()
|