medicafe 0.240517.0__py3-none-any.whl → 0.240716.2__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.

Files changed (37) hide show
  1. MediBot/MediBot.bat +46 -6
  2. MediBot/MediBot.py +9 -36
  3. MediBot/MediBot_Charges.py +0 -28
  4. MediBot/MediBot_Crosswalk_Library.py +16 -8
  5. MediBot/MediBot_Post.py +0 -0
  6. MediBot/MediBot_Preprocessor.py +26 -63
  7. MediBot/MediBot_Preprocessor_lib.py +182 -43
  8. MediBot/MediBot_UI.py +2 -7
  9. MediBot/MediBot_dataformat_library.py +0 -9
  10. MediBot/MediBot_docx_decoder.py +283 -60
  11. MediLink/MediLink.py +80 -120
  12. MediLink/MediLink_837p_encoder.py +3 -28
  13. MediLink/MediLink_837p_encoder_library.py +19 -53
  14. MediLink/MediLink_API_Generator.py +246 -0
  15. MediLink/MediLink_API_v2.py +2 -0
  16. MediLink/MediLink_API_v3.py +325 -0
  17. MediLink/MediLink_APIs.py +2 -0
  18. MediLink/MediLink_ClaimStatus.py +144 -0
  19. MediLink/MediLink_ConfigLoader.py +13 -7
  20. MediLink/MediLink_DataMgmt.py +224 -68
  21. MediLink/MediLink_Decoder.py +165 -0
  22. MediLink/MediLink_Deductible.py +203 -0
  23. MediLink/MediLink_Down.py +122 -96
  24. MediLink/MediLink_Gmail.py +453 -74
  25. MediLink/MediLink_Mailer.py +0 -7
  26. MediLink/MediLink_Parser.py +193 -0
  27. MediLink/MediLink_Scan.py +0 -0
  28. MediLink/MediLink_Scheduler.py +2 -172
  29. MediLink/MediLink_StatusCheck.py +0 -4
  30. MediLink/MediLink_UI.py +54 -18
  31. MediLink/MediLink_Up.py +6 -15
  32. {medicafe-0.240517.0.dist-info → medicafe-0.240716.2.dist-info}/METADATA +4 -1
  33. medicafe-0.240716.2.dist-info/RECORD +47 -0
  34. {medicafe-0.240517.0.dist-info → medicafe-0.240716.2.dist-info}/WHEEL +1 -1
  35. medicafe-0.240517.0.dist-info/RECORD +0 -39
  36. {medicafe-0.240517.0.dist-info → medicafe-0.240716.2.dist-info}/LICENSE +0 -0
  37. {medicafe-0.240517.0.dist-info → medicafe-0.240716.2.dist-info}/top_level.txt +0 -0
@@ -7,31 +7,6 @@ from MediLink_DataMgmt import parse_fixed_width_data, read_fixed_width_data
7
7
  import MediLink_837p_encoder_library
8
8
  #from tqdm import tqdm
9
9
 
10
- """
11
- Single File Processing Flow:
12
-
13
- This flow is triggered when the -d (directory) flag is not set. It handles the conversion of a single file specified by the -p flag.
14
- It directly processes the single file specified, without the need to iterate over a directory.
15
- The conversion initializes and processes this single file, appending the necessary EDI segments, and directly writes the output once processing is complete.
16
-
17
- Batch Directory Processing Flow:
18
-
19
- Activated when the -d flag is set, indicating that the -p flag points to a directory rather than a single file.
20
- Iterates over all files in the specified directory, processing only those that end with the ".DAT" extension.
21
- Each file is processed in sequence, with each undergoing a full cycle of reading, processing, and output file generation as in the single file flow.
22
-
23
- Development Task List:
24
-
25
- - [ ] 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.
26
- - [ ] 2. User Interface Improvement: Advance the CLI for intuitive user interaction, offering clear options for file processing and real-time progress updates.
27
- - [ ] 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.
28
- - [ ] 4. Batch Processing and Output Handling: Enhance output file management to support efficient batch operations, including systematic naming and organization for output files.
29
- - [ ] 5. Comprehensive Documentation: Maintain up-to-date and detailed documentation within the codebase, ensuring all functions and complex logic are clearly explained.
30
- - [ ] 6. De-persisting Intermediate Files.
31
- - [ ] 7. Determination of Relationship to Patient for insurance holder. Can Compare Insured Name & closeness of DOB (usually spouse [2], child [3]).
32
- - [ ] 8. Consolidation of certain functions needs to happen here.
33
- """
34
-
35
10
  def format_single_claim(patient_data, config, endpoint, transaction_set_control_number):
36
11
  """
37
12
  Formats a single claim into 837P segments based on the provided patient data and endpoint.
@@ -196,9 +171,9 @@ def read_and_validate_claims(file_path, config):
196
171
  validation_errors = [] # List to store validation errors
197
172
 
198
173
  # Iterate over data in the file
199
- for personal_info, insurance_info, service_info in read_fixed_width_data(file_path):
174
+ for personal_info, insurance_info, service_info, service_info_2, service_info_3 in read_fixed_width_data(file_path):
200
175
  # Parse data into a usable format
201
- parsed_data = parse_fixed_width_data(personal_info, insurance_info, service_info, config.get('MediLink_Config', config))
176
+ parsed_data = parse_fixed_width_data(personal_info, insurance_info, service_info, service_info_2, service_info_3, config.get('MediLink_Config', config))
202
177
  # Validate the parsed data
203
178
  is_valid, errors = validate_claim_data(parsed_data, config)
204
179
  if is_valid:
@@ -263,7 +238,7 @@ def validate_claim_data(parsed_data, config, required_fields=[]):
263
238
  ]
264
239
  """
265
240
  errors = []
266
- MediLink_ConfigLoader.log("Starting claim vata validation...")
241
+ MediLink_ConfigLoader.log("Starting claim data validation...")
267
242
  if not required_fields:
268
243
  # If no required fields are specified, assume validation is true
269
244
  return True, []
@@ -1,30 +1,18 @@
1
1
  from datetime import datetime
2
2
  import sys
3
- from MediLink import MediLink_ConfigLoader
3
+ from MediLink import MediLink_ConfigLoader, MediLink_UI
4
4
 
5
5
  # Add parent directory of the project to the Python path
6
6
  import sys
7
7
  import os
8
+
8
9
  project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
9
10
  sys.path.append(project_dir)
10
11
 
11
12
  from MediBot import MediBot_Preprocessor_lib
12
13
  load_insurance_data_from_mains = MediBot_Preprocessor_lib.load_insurance_data_from_mains
13
14
  from MediBot import MediBot_Crosswalk_Library
14
- from MediLink_API_v2 import fetch_payer_name_from_api
15
-
16
- """
17
- - [ ] 1. Code Refactoring: Increase modularity and clarity, particularly in segment creation functions (e.g., `create_st_segment`, `create_nm1_billing_provider_segment`), for better maintenance and readability.
18
- - [ ] 2. Endpoint Support: Extend support within segment creation for additional endpoints with attention to their unique claim submission requirements.
19
- - [ ] 3. Payer Identification Mechanism: Refine the mechanism for dynamically identifying payers, leveraging payer mappings and integrating with external APIs like Availity for precise payer information retrieval.
20
- - [ ] 4. Adherence to Endpoint-Specific Standards: Implement and verify the compliance of claim data formatting and inclusion based on the specific demands of each target endpoint within the segment creation logic.
21
- - [ ] 5. De-persisting Intermediate Files.
22
- - [ ] 6. Get an API for Optum "Entered As".
23
- - [ ] 7. (MED) Add Authorization Number
24
- - [X] 8. (HIGH) Interchange number should be 000HHMMSS instead of fixed constant.
25
- - [ ] 9. Upgrade Interchange formation to consolidate the batch processor and also de-risk batch
26
- interchange number matching for multiple .DAT.
27
- """
15
+ from MediLink_API_v3 import fetch_payer_name_from_api
28
16
 
29
17
  # Converts date format from one format to another.
30
18
  def convert_date_format(date_str):
@@ -520,43 +508,21 @@ def insurance_type_selection(parsed_data):
520
508
  - str: The insurance type code selected by the user.
521
509
 
522
510
  """
523
- print("\nHorrible Temporary Menu: Select the insurance type for patient {}: ".format(parsed_data['LAST']))
524
-
525
- # Define insurance options with codes and descriptions
526
- insurance_options = {
527
- "11": "Other Non-Federal Programs",
528
- "12": "Preferred Provider Organization (PPO)",
529
- "13": "Point of Service (POS)",
530
- "14": "Exclusive Provider Organization (EPO)",
531
- "15": "Indemnity Insurance",
532
- "16": "Health Maintenance Organization (HMO) Medicare Risk",
533
- "17": "Dental Maintenance Organization",
534
- "AM": "Automobile Medical",
535
- "BL": "Blue Cross/Blue Shield",
536
- "CH": "Champus",
537
- "CI": "Commercial Insurance Co.",
538
- "DS": "Disability",
539
- "FI": "Federal Employees Program",
540
- "HM": "Health Maintenance Organization",
541
- "LM": "Liability Medical",
542
- "MA": "Medicare Part A",
543
- "MB": "Medicare Part B",
544
- "MC": "Medicaid",
545
- "OF": "Other Federal Program",
546
- "TV": "Title V",
547
- "VA": "Veterans Affairs Plan",
548
- "WC": "Workers Compensation Health Claim",
549
- "ZZ": "Mutually Defined"
550
- }
551
-
552
- # Function to display full list of insurance options
553
- def display_insurance_options(options):
554
- print("Insurance Type Options:")
555
- # Sorting the dictionary keys to ensure consistent order
556
- sorted_keys = sorted(options.keys())
557
- for code in sorted_keys:
558
- description = options[code]
559
- print("{} - {}".format(code, description))
511
+ MediLink_ConfigLoader.log("insurance_type_selection(parsed_data): {}".format(parsed_data), level="DEBUG")
512
+
513
+ # Check if insurance type is already assigned and is valid
514
+ insurance_type_code = parsed_data.get('insurance_type')
515
+ if insurance_type_code and len(insurance_type_code) == 2 and insurance_type_code.isalnum():
516
+ MediLink_ConfigLoader.log("Insurance type already assigned: {}".format(insurance_type_code), level="DEBUG")
517
+ return insurance_type_code
518
+ elif insurance_type_code:
519
+ MediLink_ConfigLoader.log("Invalid insurance type: {}".format(insurance_type_code), level="WARNING")
520
+
521
+ print("\nInsurance Type Validation Error: Select the insurance type for patient {}: ".format(parsed_data['LAST']))
522
+
523
+ # Retrieve insurance options with codes and descriptions
524
+ config, _ = MediLink_ConfigLoader.load_configuration()
525
+ insurance_options = config['MediLink_Config'].get('insurance_options')
560
526
 
561
527
  def prompt_display_insurance_options():
562
528
  # Prompt to display full list
@@ -564,7 +530,7 @@ def insurance_type_selection(parsed_data):
564
530
 
565
531
  # Display full list if user confirms
566
532
  if display_full_list in ['yes', 'y']:
567
- display_insurance_options(insurance_options)
533
+ MediLink_UI.display_insurance_options(insurance_options)
568
534
 
569
535
  # Horrible menu
570
536
  prompt_display_insurance_options()
@@ -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()
@@ -1,3 +1,5 @@
1
+ # Unused archive backup. This has been superceded by API_v3
2
+
1
3
  import time
2
4
  import requests
3
5