medicafe 0.250725.17__py3-none-any.whl → 0.250728.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 CHANGED
@@ -106,6 +106,42 @@ if exist "C:\Python34\Lib\site-packages\MediBot\update_medicafe.py" (
106
106
  )
107
107
  )
108
108
 
109
+ :: Check for updates function
110
+ :check_for_updates
111
+ :: Run the check using the existing update script
112
+ for /f "delims=" %%a in ('python "%upgrade_medicafe%" --check-only 2^>nul') do (
113
+ set "update_check_result=%%a"
114
+ )
115
+
116
+ :: Check if update is available (with better error handling)
117
+ if "!update_check_result!"=="UP_TO_DATE" (
118
+ echo .
119
+ ping -n 2 127.0.0.1 >nul
120
+ ) else if "!update_check_result!"=="ERROR" (
121
+ echo Auto-Update Check Error.
122
+ ping -n 2 127.0.0.1 >nul
123
+ ) else if "!update_check_result:~0,16!"=="UPDATE_AVAILABLE:" (
124
+ set "new_version=!update_check_result:~16!"
125
+ echo.
126
+ echo =============================================================
127
+ echo UPDATE AVAILABLE
128
+ echo =============================================================
129
+ echo.
130
+ echo A new version of MediCafe is available!
131
+ echo Current version: %medicafe_version%
132
+ echo New version: !new_version!
133
+ echo.
134
+ echo To update, select option 1 from the menu below.
135
+ echo.
136
+ echo =============================================================
137
+ echo.
138
+ ping -n 3 127.0.0.1 >nul
139
+ ) else (
140
+ :: Handle any other unexpected results (empty, malformed, etc.)
141
+ echo Checking for updates... Unable to check for updates.
142
+ ping -n 2 127.0.0.1 >nul
143
+ )
144
+
109
145
  :: Main menu
110
146
  :main_menu
111
147
  cls
@@ -114,6 +150,16 @@ echo --------------------------------------------------------------
114
150
  echo .//* Welcome to MediCafe *\\.
115
151
  echo --------------------------------------------------------------
116
152
  echo.
153
+
154
+ :: Check for updates if internet is available
155
+ if "!internet_available!"=="1" (
156
+ call :check_for_updates
157
+ if errorlevel 1 (
158
+ echo Checking for updates... Error occurred.
159
+ ping -n 2 127.0.0.1 >nul
160
+ )
161
+ )
162
+
117
163
  echo Please select an option:
118
164
  echo.
119
165
  if "!internet_available!"=="1" (
@@ -184,7 +230,7 @@ goto main_menu
184
230
  :medibot_flow
185
231
  call :process_csvs
186
232
  cls
187
- echo Please wait...
233
+ echo Starting MediBot...
188
234
  py "%python_script2%" "%config_file%"
189
235
  if errorlevel 1 echo Failed to run MediBot.
190
236
  pause
MediBot/MediBot.py CHANGED
@@ -318,7 +318,7 @@ class ExecutionState:
318
318
  if __name__ == "__main__":
319
319
  e_state = None
320
320
  try:
321
- print("Please wait...")
321
+ print("Initializing. Loading configuration and preparing environment...")
322
322
  # Default paths for configuration and crosswalk files
323
323
  default_config_path = os.path.join(os.path.dirname(__file__), '..', 'json', 'config.json')
324
324
  default_crosswalk_path = os.path.join(os.path.dirname(__file__), '..', 'json', 'crosswalk.json')
@@ -352,7 +352,12 @@ if __name__ == "__main__":
352
352
 
353
353
  print("Load Complete...")
354
354
  MediLink_ConfigLoader.log("Load Complete event triggered. Clearing console. Displaying Menu...", level="INFO")
355
- _ = os.system('cls')
355
+ # Windows XP console buffer fix: Use cls with echo to reset buffer state
356
+ _ = os.system('cls && echo.')
357
+
358
+ # Additional buffer stabilization
359
+ time.sleep(0.1)
360
+ sys.stdout.flush()
356
361
 
357
362
  proceed, selected_patient_ids, selected_indices, fixed_values = user_interaction(csv_data, interaction_mode, error_message, reverse_mapping)
358
363
 
@@ -716,13 +716,15 @@ def update_crosswalk_with_new_payer_id(client, insurance_name, payer_id, config,
716
716
  print(message)
717
717
  MediLink_ConfigLoader.log(message, config, level="ERROR")
718
718
 
719
- def save_crosswalk(client, config, crosswalk):
719
+ def save_crosswalk(client, config, crosswalk, skip_api_operations=False):
720
720
  """
721
721
  Saves the crosswalk to a JSON file. Ensures that all necessary keys are present and logs the outcome.
722
722
 
723
723
  Args:
724
+ client (APIClient): API client for fetching payer names (ignored if skip_api_operations=True).
724
725
  config (dict): Configuration settings for logging.
725
726
  crosswalk (dict): The crosswalk dictionary to save.
727
+ skip_api_operations (bool): If True, skips API calls and user prompts for faster saves.
726
728
 
727
729
  Returns:
728
730
  bool: True if the crosswalk was saved successfully, False otherwise.
@@ -739,13 +741,21 @@ def save_crosswalk(client, config, crosswalk):
739
741
  for payer_id, details in crosswalk.get('payer_id', {}).items():
740
742
  current_endpoint = details.get('endpoint', None)
741
743
  if current_endpoint and current_endpoint not in config['MediLink_Config']['endpoints']:
742
- print("WARNING: The current endpoint '{}' for payer ID '{}' is not valid.".format(current_endpoint, payer_id))
743
- MediLink_ConfigLoader.log("Current endpoint '{}' for payer ID '{}' is not valid. Prompting for selection.".format(current_endpoint, payer_id), config, level="WARNING")
744
- selected_endpoint = select_endpoint(config, current_endpoint) # Prompt user to select a valid endpoint
745
- crosswalk['payer_id'][payer_id]['endpoint'] = selected_endpoint # Update the endpoint in the crosswalk
746
- MediLink_ConfigLoader.log("Updated payer ID {} with new endpoint '{}'.".format(payer_id, selected_endpoint), config, level="INFO")
744
+ if skip_api_operations:
745
+ # Log warning but don't prompt user during API-bypass mode
746
+ MediLink_ConfigLoader.log("WARNING: Invalid endpoint '{}' for payer ID '{}' - skipping correction due to API bypass mode".format(current_endpoint, payer_id), config, level="WARNING")
747
+ else:
748
+ print("WARNING: The current endpoint '{}' for payer ID '{}' is not valid.".format(current_endpoint, payer_id))
749
+ MediLink_ConfigLoader.log("Current endpoint '{}' for payer ID '{}' is not valid. Prompting for selection.".format(current_endpoint, payer_id), config, level="WARNING")
750
+ selected_endpoint = select_endpoint(config, current_endpoint) # Prompt user to select a valid endpoint
751
+ crosswalk['payer_id'][payer_id]['endpoint'] = selected_endpoint # Update the endpoint in the crosswalk
752
+ MediLink_ConfigLoader.log("Updated payer ID {} with new endpoint '{}'.".format(payer_id, selected_endpoint), config, level="INFO")
747
753
 
748
754
  try:
755
+ # Log API bypass mode if enabled
756
+ if skip_api_operations:
757
+ MediLink_ConfigLoader.log("save_crosswalk running in API bypass mode - skipping API calls and user prompts", config, level="INFO")
758
+
749
759
  # Initialize the 'payer_id' key if it doesn't exist
750
760
  if 'payer_id' not in crosswalk:
751
761
  print("save_crosswalk is initializing 'payer_id' key...")
@@ -755,13 +765,23 @@ def save_crosswalk(client, config, crosswalk):
755
765
  # Ensure all payer IDs have a name and initialize medisoft_id and medisoft_medicare_id as empty lists if they do not exist
756
766
  for payer_id in crosswalk['payer_id']:
757
767
  if 'name' not in crosswalk['payer_id'][payer_id]:
758
- fetch_and_store_payer_name(client, payer_id, crosswalk, config)
759
- MediLink_ConfigLoader.log("Fetched and stored payer name for payer ID: {}.".format(payer_id), config, level="DEBUG")
768
+ if skip_api_operations:
769
+ # Set placeholder name and log for MediBot to handle later
770
+ crosswalk['payer_id'][payer_id]['name'] = 'Unknown'
771
+ MediLink_ConfigLoader.log("Set placeholder name for payer ID {} - will be resolved by MediBot health check".format(payer_id), config, level="INFO")
772
+ else:
773
+ fetch_and_store_payer_name(client, payer_id, crosswalk, config)
774
+ MediLink_ConfigLoader.log("Fetched and stored payer name for payer ID: {}.".format(payer_id), config, level="DEBUG")
760
775
 
761
776
  # Check for the endpoint key
762
777
  if 'endpoint' not in crosswalk['payer_id'][payer_id]:
763
- crosswalk['payer_id'][payer_id]['endpoint'] = select_endpoint(config) # Use the helper function to set the endpoint
764
- MediLink_ConfigLoader.log("Initialized 'endpoint' for payer ID {}.".format(payer_id), config, level="DEBUG")
778
+ if skip_api_operations:
779
+ # Set default endpoint and log
780
+ crosswalk['payer_id'][payer_id]['endpoint'] = 'AVAILITY'
781
+ MediLink_ConfigLoader.log("Set default endpoint for payer ID {} - can be adjusted via MediBot if needed".format(payer_id), config, level="INFO")
782
+ else:
783
+ crosswalk['payer_id'][payer_id]['endpoint'] = select_endpoint(config) # Use the helper function to set the endpoint
784
+ MediLink_ConfigLoader.log("Initialized 'endpoint' for payer ID {}.".format(payer_id), config, level="DEBUG")
765
785
 
766
786
  # Initialize medisoft_id and medisoft_medicare_id as empty lists if they do not exist
767
787
  crosswalk['payer_id'][payer_id].setdefault('medisoft_id', [])
@@ -171,20 +171,108 @@ def filter_rows(csv_data):
171
171
  excluded_insurance = {'AETNA', 'AETNA MEDICARE', 'HUMANA MED HMO'}
172
172
  csv_data[:] = [row for row in csv_data if row.get('Patient ID') and row.get('Primary Insurance') not in excluded_insurance]
173
173
 
174
+ def clean_surgery_date_string(date_str):
175
+ """
176
+ Cleans and normalizes surgery date strings to handle damaged data.
177
+
178
+ Parameters:
179
+ - date_str (str): The raw date string from the CSV
180
+
181
+ Returns:
182
+ - str: Cleaned date string in MM/DD/YYYY format, or empty string if unparseable
183
+ """
184
+ if not date_str:
185
+ return ''
186
+
187
+ # Convert to string and strip whitespace
188
+ date_str = str(date_str).strip()
189
+ if not date_str:
190
+ return ''
191
+
192
+ # Remove common problematic characters and normalize
193
+ date_str = date_str.replace('\n', ' ').replace('\r', ' ').replace('\t', ' ')
194
+ date_str = ' '.join(date_str.split()) # Normalize whitespace
195
+
196
+ # Handle common date format variations
197
+ date_formats = [
198
+ '%m/%d/%Y', # 12/25/2023
199
+ '%m-%d-%Y', # 12-25-2023
200
+ '%m/%d/%y', # 12/25/23
201
+ '%m-%d-%y', # 12-25-23
202
+ '%Y/%m/%d', # 2023/12/25
203
+ '%Y-%m-%d', # 2023-12-25
204
+ '%m/%d/%Y %H:%M:%S', # 12/25/2023 14:30:00
205
+ '%m-%d-%Y %H:%M:%S', # 12-25-2023 14:30:00
206
+ ]
207
+
208
+ # Try to parse with different formats
209
+ for fmt in date_formats:
210
+ try:
211
+ parsed_date = datetime.strptime(date_str, fmt)
212
+ # Return in standard MM/DD/YYYY format
213
+ return parsed_date.strftime('%m/%d/%Y')
214
+ except ValueError:
215
+ continue
216
+
217
+ # If no format matches, try to extract date components
218
+ try:
219
+ # Remove any time components and extra text
220
+ date_only = date_str.split()[0] # Take first part if there's extra text
221
+
222
+ # Try to extract numeric components
223
+ import re
224
+ numbers = re.findall(r'\d+', date_only)
225
+
226
+ if len(numbers) >= 3:
227
+ # Assume MM/DD/YYYY or MM-DD-YYYY format
228
+ month, day, year = int(numbers[0]), int(numbers[1]), int(numbers[2])
229
+
230
+ # Validate ranges
231
+ if 1 <= month <= 12 and 1 <= day <= 31 and 1900 <= year <= 2100:
232
+ # Handle 2-digit years
233
+ if year < 100:
234
+ year += 2000 if year < 50 else 1900
235
+
236
+ parsed_date = datetime(year, month, day)
237
+ return parsed_date.strftime('%m/%d/%Y')
238
+ except (ValueError, IndexError):
239
+ pass
240
+
241
+ # If all parsing attempts fail, return empty string
242
+ return ''
243
+
174
244
  def convert_surgery_date(csv_data):
245
+ """
246
+ Converts surgery date strings to datetime objects with comprehensive data cleaning.
247
+
248
+ Parameters:
249
+ - csv_data (list): List of dictionaries containing CSV row data
250
+ """
175
251
  for row in csv_data:
176
252
  surgery_date_str = row.get('Surgery Date', '')
253
+
177
254
  if not surgery_date_str:
178
255
  MediLink_ConfigLoader.log("Warning: Surgery Date not found for row: {}".format(row), level="WARNING")
179
- # BUG This needs a cleaning step for the Surgery Date string in case we're receiving damaged data.
180
256
  row['Surgery Date'] = datetime.min # Assign a minimum datetime value if empty
181
257
  print("Surgery Date not found for row: {}".format(row))
182
258
  else:
183
- try:
184
- row['Surgery Date'] = datetime.strptime(surgery_date_str, '%m/%d/%Y')
185
- except ValueError as e:
186
- MediLink_ConfigLoader.log("Error parsing Surgery Date '{}': {} for row: {}".format(surgery_date_str, e, row), level="ERROR")
187
- row['Surgery Date'] = datetime.min # Assign a minimum datetime value if parsing fails
259
+ # Clean the date string first
260
+ cleaned_date_str = clean_surgery_date_string(surgery_date_str)
261
+
262
+ if not cleaned_date_str:
263
+ MediLink_ConfigLoader.log("Error: Could not clean Surgery Date '{}' for row: {}".format(surgery_date_str, row), level="ERROR")
264
+ row['Surgery Date'] = datetime.min # Assign a minimum datetime value if cleaning fails
265
+ print("Could not clean Surgery Date '{}' for row: {}".format(surgery_date_str, row))
266
+ else:
267
+ try:
268
+ # Parse the cleaned date string
269
+ row['Surgery Date'] = datetime.strptime(cleaned_date_str, '%m/%d/%Y')
270
+ MediLink_ConfigLoader.log("Successfully cleaned and parsed Surgery Date '{}' -> '{}' for row: {}".format(
271
+ surgery_date_str, cleaned_date_str, row), level="DEBUG")
272
+ except ValueError as e:
273
+ MediLink_ConfigLoader.log("Error parsing cleaned Surgery Date '{}': {} for row: {}".format(
274
+ cleaned_date_str, e, row), level="ERROR")
275
+ row['Surgery Date'] = datetime.min # Assign a minimum datetime value if parsing fails
188
276
 
189
277
  def sort_and_deduplicate(csv_data):
190
278
  # Create a dictionary to hold unique patients based on Patient ID