medicafe 0.240809.0__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.

@@ -1,9 +1,6 @@
1
- # MediLink_DataMgmt.py
2
- import csv
3
- import os
1
+
2
+ import csv, os, re, subprocess, time
4
3
  from datetime import datetime, timedelta
5
- import re
6
- import subprocess
7
4
 
8
5
  # Need this for running Medibot and MediLink
9
6
  try:
@@ -167,64 +164,170 @@ def consolidate_csvs(source_directory, file_prefix="Consolidated", interactive=F
167
164
  def operate_winscp(operation_type, files, endpoint_config, local_storage_path, config):
168
165
  """
169
166
  General function to operate WinSCP for uploading or downloading files.
167
+ """
168
+ MediLink_ConfigLoader.log("Starting operate_winscp with operation_type: {}".format(operation_type))
169
+
170
+ config = ensure_config_loaded(config)
171
+ winscp_path = get_winscp_path(config)
172
+
173
+ if not os.path.isfile(winscp_path):
174
+ MediLink_ConfigLoader.log("WinSCP.com not found at {}".format(winscp_path), level="ERROR")
175
+ return []
170
176
 
171
- :param operation_type: 'upload' or 'download'
172
- :param files: List of files to upload or pattern for files to download.
173
- :param endpoint_config: Dictionary containing endpoint configuration.
174
- :param local_storage_path: Base local storage path for logs and files.
177
+ validate_endpoint_config(endpoint_config)
178
+ winscp_log_path = setup_logging(operation_type, local_storage_path)
175
179
 
176
- # Example of how to call this function for uploads
177
- upload_files = ['path/to/local/file1.txt', 'path/to/local/file2.txt']
178
- upload_config = {
179
- 'session_name': 'MySession',
180
- 'remote_directory_up': '/remote/upload/path'
181
- }
180
+ # Validate the local_storage_path and replace it if necessary
181
+ local_storage_path = validate_local_storage_path(local_storage_path, config)
182
182
 
183
- operate_winscp('upload', upload_files, upload_config, 'path/to/local/storage', config)
183
+ remote_directory = get_remote_directory(endpoint_config, operation_type)
184
+ command = build_command(winscp_path, winscp_log_path, endpoint_config, remote_directory, operation_type, files, local_storage_path)
184
185
 
185
- # Example of how to call this function for downloads
186
- download_config = {
187
- 'session_name': 'MySession',
188
- 'remote_directory_down': '/remote/download/path'
189
- }
186
+ if config.get("TestMode", True):
187
+ MediLink_ConfigLoader.log("Test mode is enabled. Simulating operation.")
188
+ return simulate_operation(operation_type, files, config)
189
+
190
+ result = execute_winscp_command(command, operation_type, files, local_storage_path)
191
+ MediLink_ConfigLoader.log("[Execute WinSCP Command] Result: {}".format(result), level="DEBUG")
192
+ return result
190
193
 
191
- operate_winscp('download', None, download_config, 'path/to/local/storage', config)
194
+ def validate_local_storage_path(local_storage_path, config):
192
195
  """
193
- # Setup paths
194
- try:
195
- # TODO (Easy / Config) Get this updated. ??
196
- winscp_path = config['winscp_path']
197
- except KeyError:
198
- winscp_path = os.path.join(os.getcwd(), "Installers", "WinSCP-Portable", "WinSCP.com")
199
- except Exception as e:
200
- # Handle any other exceptions here
201
- print("An error occurred while running WinSCP:", e)
202
- winscp_path = None
196
+ Validates the local storage path and replaces it with outputFilePath from config if it contains spaces.
197
+ """
198
+ if ' ' in local_storage_path:
199
+ MediLink_ConfigLoader.log("Local storage path contains spaces, using outputFilePath from config.", level="WARN")
200
+ output_file_path = config.get('outputFilePath', None)
201
+ if not output_file_path:
202
+ raise ValueError("outputFilePath not found in config.")
203
+ return os.path.normpath(output_file_path)
204
+ return os.path.normpath(local_storage_path)
205
+
206
+ def ensure_config_loaded(config):
207
+ MediLink_ConfigLoader.log("Ensuring configuration is loaded.")
208
+ if not config:
209
+ MediLink_ConfigLoader.log("Warning: No config passed to ensure_config_loaded. Re-loading config...")
210
+ config, _ = MediLink_ConfigLoader.load_configuration()
211
+
212
+ # Check if config was successfully loaded
213
+ if not config or 'MediLink_Config' not in config:
214
+ MediLink_ConfigLoader.log("Failed to load the MediLink configuration. Config is None or missing 'MediLink_Config'.")
215
+ raise RuntimeError("Failed to load the MediLink configuration. Config is None or missing 'MediLink_Config'.")
216
+
217
+ # Check that 'endpoints' key exists within 'MediLink_Config'
218
+ if 'endpoints' not in config['MediLink_Config']:
219
+ MediLink_ConfigLoader.log("The loaded configuration is missing the 'endpoints' section.")
220
+ raise ValueError("The loaded configuration is missing the 'endpoints' section.")
221
+
222
+ # Additional checks can be added here to ensure all expected keys and structures are present
223
+ if 'local_storage_path' not in config['MediLink_Config']:
224
+ MediLink_ConfigLoader.log("The loaded configuration is missing the 'local_storage_path' setting.")
225
+ raise ValueError("The loaded configuration is missing the 'local_storage_path' setting.")
226
+
227
+ MediLink_ConfigLoader.log("Configuration loaded successfully.")
228
+ return config['MediLink_Config'] # Return the relevant part of the config for simplicity
229
+
230
+ def get_winscp_path(config):
231
+ MediLink_ConfigLoader.log("Retrieving WinSCP path from provided config.")
232
+
233
+ def find_winscp_path(cfg):
234
+ if 'winscp_path' in cfg:
235
+ # cfg is already 'MediLink_Config'
236
+ MediLink_ConfigLoader.log("Config provided directly as 'MediLink_Config'.")
237
+ return cfg.get('winscp_path')
238
+ else:
239
+ # cfg is the full configuration, retrieve 'MediLink_Config'
240
+ MediLink_ConfigLoader.log("Config provided as full configuration; accessing 'MediLink_Config'.")
241
+ medi_link_config = cfg.get('MediLink_Config', {})
242
+ return medi_link_config.get('winscp_path')
243
+
244
+ # Attempt to find the WinSCP path using the provided config
245
+ winscp_path = find_winscp_path(config)
246
+
247
+ # If the path is not found, attempt to use default paths
248
+ if not winscp_path:
249
+ error_message = "WinSCP path not found in config. Attempting to use default paths."
250
+ # print(error_message)
251
+ MediLink_ConfigLoader.log(error_message)
203
252
 
204
- if not os.path.isfile(winscp_path):
205
- MediLink_ConfigLoader.log("WinSCP.com not found at {}".format(winscp_path))
206
- return []
253
+ # Try the default paths
254
+ default_paths = [
255
+ os.path.join(os.getcwd(), "Installers", "WinSCP-Portable", "WinSCP.com"),
256
+ os.path.join(os.getcwd(), "Necessary Programs", "WinSCP-Portable", "WinSCP.com")
257
+ ]
258
+
259
+ for path in default_paths:
260
+ if os.path.exists(path):
261
+ found_message = "WinSCP found at {}. Using this path.".format(path)
262
+ # print(found_message)
263
+ MediLink_ConfigLoader.log(found_message)
264
+ return path
265
+
266
+ # If no valid path is found, attempt to reload the configuration
267
+ reload_message = "WinSCP not found in config or default paths. Reloading the entire configuration."
268
+ # print(reload_message)
269
+ MediLink_ConfigLoader.log(reload_message)
270
+
271
+ try:
272
+ config, _ = MediLink_ConfigLoader.load_configuration()
273
+ winscp_path = find_winscp_path(config)
274
+
275
+ if winscp_path:
276
+ success_message = "WinSCP path found after reloading configuration. Using this path."
277
+ # print(success_message)
278
+ MediLink_ConfigLoader.log(success_message)
279
+ return winscp_path
280
+ else:
281
+ raise FileNotFoundError("WinSCP path not found even after reloading configuration.")
282
+
283
+ except Exception as e:
284
+ error_message = "Failed to reload configuration or find WinSCP path: {}. Exiting script.".format(e)
285
+ print(error_message)
286
+ MediLink_ConfigLoader.log(error_message)
287
+ raise FileNotFoundError(error_message)
288
+
289
+ return winscp_path
207
290
 
208
- # Setup logging
291
+ def validate_endpoint_config(endpoint_config):
292
+ MediLink_ConfigLoader.log("Validating endpoint configuration.")
293
+ if not isinstance(endpoint_config, dict):
294
+ MediLink_ConfigLoader.log("Endpoint configuration object is invalid. Expected a dictionary, got: {}".format(type(endpoint_config)))
295
+ raise ValueError("Endpoint configuration object is invalid. Expected a dictionary, got: {}".format(type(endpoint_config)))
296
+
297
+ def setup_logging(operation_type, local_storage_path):
298
+ MediLink_ConfigLoader.log("Setting up logging for operation type: {}".format(operation_type))
209
299
  log_filename = "winscp_upload.log" if operation_type == "upload" else "winscp_download.log"
210
- winscp_log_path = os.path.join(local_storage_path, log_filename)
300
+ return os.path.join(local_storage_path, log_filename)
301
+
302
+ def get_remote_directory(endpoint_config, operation_type):
303
+ MediLink_ConfigLoader.log("Getting remote directory for operation type: {}".format(operation_type))
304
+ if endpoint_config is None:
305
+ MediLink_ConfigLoader.log("Error: Endpoint configuration is None.")
306
+ raise ValueError("Endpoint configuration is None. Expected a dictionary with configuration details.")
307
+
308
+ if not isinstance(endpoint_config, dict):
309
+ MediLink_ConfigLoader.log("Error: Endpoint configuration is invalid. Expected a dictionary, got: {}".format(type(endpoint_config)))
310
+ raise TypeError("Endpoint configuration is invalid. Expected a dictionary, got: {}".format(type(endpoint_config)))
211
311
 
212
- # Session and directory setup
213
312
  try:
214
- session_name = endpoint_config.get('session_name', '')
215
313
  if operation_type == "upload":
216
- remote_directory = endpoint_config['remote_directory_up']
314
+ return endpoint_config['remote_directory_up']
315
+ elif operation_type == "download":
316
+ return endpoint_config['remote_directory_down']
217
317
  else:
218
- remote_directory = endpoint_config['remote_directory_down']
318
+ MediLink_ConfigLoader.log("Invalid operation type: {}. Expected 'upload' or 'download'.".format(operation_type))
319
+ raise ValueError("Invalid operation type: {}. Expected 'upload' or 'download'.".format(operation_type))
219
320
  except KeyError as e:
220
- # Log the missing key information
221
- missing_key = str(e)
222
- message = "Critical Error: Endpoint config is missing key: {}".format(missing_key)
223
- MediLink_ConfigLoader.log(message)
224
- # Raise an exception to halt execution
225
- raise RuntimeError("Configuration error: The endpoint configuration is missing definitions for the required remote directories. Please check the configuration and try again.")
226
-
227
- # Command building
321
+ MediLink_ConfigLoader.log("Critical Error: Endpoint config is missing key: {}".format(e))
322
+ raise RuntimeError("Configuration error: Missing required remote directory in endpoint configuration.")
323
+
324
+ def build_command(winscp_path, winscp_log_path, endpoint_config, remote_directory, operation_type, files, local_storage_path, newer_than=None, filemask=None):
325
+ # Log the operation type
326
+ MediLink_ConfigLoader.log("[Build Command] Building WinSCP command for operation type: {}".format(operation_type))
327
+
328
+ session_name = endpoint_config.get('session_name', '')
329
+
330
+ # Initial command structure
228
331
  command = [
229
332
  winscp_path,
230
333
  '/log=' + winscp_log_path,
@@ -235,66 +338,184 @@ def operate_winscp(operation_type, files, endpoint_config, local_storage_path, c
235
338
  'cd {}'.format(remote_directory)
236
339
  ]
237
340
 
238
- # Add commands to WinSCP script
239
- # BUG (Low SFTP) We really need to fix this path situation.
240
- # Unfortunately, this just needs to be a non-spaced path because WinSCP can't
241
- # handle the spaces. Also, Windows won't let me use shutil to move the files out of G:\ into C:\ and it it wants an administrator security
242
- # check or verification thing for me to even move the file by hand so that doesn't work either.
243
- # command.append("put {}".format("C:\\Z_optumedi_04161742.txt"))
244
- if operation_type == "upload":
245
- for file_path in files:
246
- normalized_path = os.path.normpath(file_path)
247
- command.append("put {}".format(normalized_path))
248
- else:
249
- command.append('get *') # Adjust pattern as needed
250
-
251
- command += ['close', 'exit']
252
-
253
- # Check if TestMode is enabled in the configuration
254
- if config.get("MediLink_Config", {}).get("TestMode", True):
255
- # TestMode is enabled, do not execute the command
256
- print("Test Mode is enabled! WinSCP Command not executed.")
257
- MediLink_ConfigLoader.log("Test Mode is enabled! WinSCP Command not executed.")
258
- MediLink_ConfigLoader.log("TEST MODE: Simulating WinSCP {} File List.".format(operation_type))
259
- uploaded_files = []
260
- if files is not None: # Check if files is not None
261
- for file_path in files:
262
- normalized_path = os.path.normpath(file_path)
263
- if os.path.exists(normalized_path): # Check if the file exists before appending
264
- uploaded_files.append(normalized_path)
341
+ try:
342
+ # Handle upload operation
343
+ if operation_type == "upload":
344
+ if not files:
345
+ MediLink_ConfigLoader.log("Error: No files provided for upload operation.", level="ERROR")
346
+ raise ValueError("No files provided for upload operation.")
347
+
348
+ put_commands = []
349
+ for f in files:
350
+ # Normalize the path
351
+ normalized_path = os.path.normpath(f)
352
+ original_path = normalized_path # Keep for logging
353
+
354
+ # Remove leading slash if present
355
+ if normalized_path.startswith('\\') or normalized_path.startswith('/'):
356
+ normalized_path = normalized_path.lstrip('\\/')
357
+ MediLink_ConfigLoader.log("Removed leading slash from path: {}".format(original_path), level="DEBUG")
358
+
359
+ # Remove trailing slash if present
360
+ if normalized_path.endswith('\\') or normalized_path.endswith('/'):
361
+ normalized_path = normalized_path.rstrip('\\/')
362
+ MediLink_ConfigLoader.log("Removed trailing slash from path: {}".format(original_path), level="DEBUG")
363
+
364
+ # Determine if quotes are necessary (e.g., if path contains spaces)
365
+ if ' ' in normalized_path:
366
+ put_command = 'put "{}"'.format(normalized_path)
367
+ MediLink_ConfigLoader.log("Constructed put command with quotes: {}".format(put_command), level="DEBUG")
368
+ else:
369
+ put_command = 'put {}'.format(normalized_path)
370
+ MediLink_ConfigLoader.log("Constructed put command without quotes: {}".format(put_command), level="DEBUG")
371
+
372
+ put_commands.append(put_command)
373
+ command += put_commands
374
+
375
+ # Handle download operation
376
+ elif operation_type == "download":
377
+ lcd_path = os.path.normpath(local_storage_path)
378
+ original_lcd_path = lcd_path # Keep for logging
379
+
380
+ # Remove leading slash if present
381
+ if lcd_path.startswith('\\') or lcd_path.startswith('/'):
382
+ lcd_path = lcd_path.lstrip('\\/')
383
+ MediLink_ConfigLoader.log("Removed leading slash from local storage path: {}".format(original_lcd_path), level="DEBUG")
384
+
385
+ # Remove trailing slash if present
386
+ if lcd_path.endswith('\\') or lcd_path.endswith('/'):
387
+ lcd_path = lcd_path.rstrip('\\/')
388
+ MediLink_ConfigLoader.log("Removed trailing slash from local storage path: {}".format(original_lcd_path), level="DEBUG")
389
+
390
+ # Determine if quotes are necessary (e.g., if path contains spaces)
391
+ if ' ' in lcd_path:
392
+ lcd_command = 'lcd "{}"'.format(lcd_path)
393
+ MediLink_ConfigLoader.log("Constructed lcd command with quotes: {}".format(lcd_command), level="DEBUG")
394
+ else:
395
+ lcd_command = 'lcd {}'.format(lcd_path)
396
+ MediLink_ConfigLoader.log("Constructed lcd command without quotes: {}".format(lcd_command), level="DEBUG")
397
+
398
+ command.append(lcd_command)
399
+
400
+ # Handle filemask input
401
+ if filemask:
402
+ # TODO: Implement logic to translate filemask into WinSCP syntax
403
+ # This should handle cases where filemask is a list, JSON, dictionary, or None.
404
+ # Example: Convert to a string like "*.{ext1}|*.{ext2}|*.{ext3}".
405
+ if isinstance(filemask, list):
406
+ filemask_str = '|'.join(['*.' + ext for ext in filemask])
407
+ elif isinstance(filemask, dict):
408
+ filemask_str = '|'.join(['*.' + ext for ext in filemask.keys()])
409
+ elif isinstance(filemask, str):
410
+ filemask_str = filemask # Assume it's already in the correct format
265
411
  else:
266
- MediLink_ConfigLoader.log("TEST MODE: Failed to {} file: {} does not exist.".format(operation_type, normalized_path))
412
+ filemask_str = '*' # Default to all files if filemask is None or unsupported type
413
+ else:
414
+ filemask_str = '*' # Default to all files if filemask is None
415
+
416
+ # Use synchronize command for efficient downloading
417
+ if newer_than:
418
+ command.append('synchronize local -filemask="{}" -newerthan={}'.format(filemask_str, newer_than))
419
+ else:
420
+ command.append('synchronize local -filemask="{}"'.format(filemask_str))
421
+
422
+ # Close and exit commands
423
+ command += ['close', 'exit']
424
+ MediLink_ConfigLoader.log("[Build Command] WinSCP command: {}".format(command))
425
+ return command
426
+
427
+ except Exception as e:
428
+ MediLink_ConfigLoader.log("Error in build_command: {}. Reverting to original implementation.".format(e), level="ERROR")
429
+
430
+ # Fallback to original implementation
431
+ # Handle upload operation
432
+ if operation_type == "upload":
433
+ if not files:
434
+ MediLink_ConfigLoader.log("Error: No files provided for upload operation.", level="ERROR")
435
+ raise ValueError("No files provided for upload operation.")
436
+ command.extend(["put {}".format(os.path.normpath(file_path)) for file_path in files])
437
+
438
+ # Handle download operation
267
439
  else:
268
- MediLink_ConfigLoader.log("TEST MODE: No files to upload.")
269
- return uploaded_files if files is not None else []
440
+ command.append('get *')
441
+
442
+ # Close and exit commands
443
+ command.extend(['close', 'exit'])
444
+ MediLink_ConfigLoader.log("[Build Command] Original WinSCP command: {}".format(command))
445
+ return command
446
+
447
+ def simulate_operation(operation_type, files, config):
448
+ MediLink_ConfigLoader.log("Test Mode is enabled! Simulating WinSCP {} operation.".format(operation_type))
449
+
450
+ if operation_type == 'upload' and files:
451
+ MediLink_ConfigLoader.log("Simulating 3 second delay for upload operation for files: {}".format(files))
452
+ time.sleep(3)
453
+ return [os.path.normpath(file) for file in files if os.path.exists(file)]
454
+ elif operation_type == 'download':
455
+ MediLink_ConfigLoader.log("Simulating 3 second delay for download operation. No files to download in test mode.")
456
+ time.sleep(3)
457
+ return []
270
458
  else:
271
- # TestMode is not enabled, execute the command
459
+ MediLink_ConfigLoader.log("Invalid operation type during simulation: {}".format(operation_type))
460
+ return []
461
+
462
+ def execute_winscp_command(command, operation_type, files, local_storage_path):
463
+ """
464
+ Execute the WinSCP command for the specified operation type.
465
+ """
466
+ MediLink_ConfigLoader.log("Executing WinSCP command for operation type: {}".format(operation_type))
467
+
468
+ try:
272
469
  process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False)
273
470
  stdout, stderr = process.communicate()
274
-
275
- if process.returncode == 0: # BUG Does this work as intended?
276
- MediLink_ConfigLoader.log("WinSCP {} attempted.".format(operation_type))
277
- # Construct a list of downloaded files if operation_type is 'download'
471
+ except Exception as e:
472
+ MediLink_ConfigLoader.log("Error occurred while executing WinSCP command: {}".format(e), level="ERROR")
473
+ return [] # Return an empty list instead of None
474
+
475
+ if process.returncode == 0:
476
+ MediLink_ConfigLoader.log("WinSCP {} operation completed successfully.".format(operation_type))
477
+
278
478
  if operation_type == 'download':
279
- downloaded_files = []
280
- for root, dirs, files in os.walk(local_storage_path):
281
- for file in files:
282
- downloaded_files.append(os.path.join(root, file))
479
+ downloaded_files = list_downloaded_files(local_storage_path) # BUG This isn't behaving correctly because the local_storage_path isn't where winscp is dumping the files
480
+ MediLink_ConfigLoader.log("Files currently located in local_storage_path: {}".format(downloaded_files), level="DEBUG")
481
+
482
+ if not downloaded_files:
483
+ MediLink_ConfigLoader.log("No files were downloaded or an error occurred during the listing process.", level="WARNING")
283
484
  return downloaded_files
284
-
285
- if operation_type == 'upload':
286
- # Return a list of uploaded files
287
- uploaded_files = []
288
- for file_path in files:
289
- normalized_path = os.path.normpath(file_path)
290
- if os.path.exists(normalized_path): # Check if the file exists before appending
291
- uploaded_files.append(normalized_path)
292
- else:
293
- MediLink_ConfigLoader.log("Failed to upload file: {} does not exist.".format(normalized_path))
485
+
486
+ elif operation_type == 'upload':
487
+ uploaded_files = [os.path.normpath(file) for file in files if os.path.exists(file)]
488
+ MediLink_ConfigLoader.log("Uploaded files: {}".format(uploaded_files), level="DEBUG")
294
489
  return uploaded_files
295
490
  else:
296
- MediLink_ConfigLoader.log("Failed to {} files. Details: {}".format(operation_type, stderr.decode('utf-8')))
297
- return [] # Return empty list to indicate failure. BUG check to make sure this doesn't break something else.
491
+ error_message = stderr.decode('utf-8').strip()
492
+ MediLink_ConfigLoader.log("Failed to {} files. Exit code: {}. Details: {}".format(
493
+ operation_type, process.returncode, error_message), level="ERROR")
494
+ return [] # Return an empty list instead of None
495
+
496
+ def list_downloaded_files(local_storage_path):
497
+
498
+ MediLink_ConfigLoader.log("Listing downloaded files in local storage path: {}".format(local_storage_path))
499
+
500
+ # Initialize an empty list to hold file paths
501
+ downloaded_files = []
502
+
503
+ try:
504
+ # Walk through the directory and collect all file paths
505
+ for root, _, files in os.walk(local_storage_path):
506
+ for file in files:
507
+ file_path = os.path.join(root, file)
508
+ downloaded_files.append(file_path)
509
+ MediLink_ConfigLoader.log("File found: {}".format(file_path), level="DEBUG")
510
+
511
+ if not downloaded_files:
512
+ MediLink_ConfigLoader.log("No files found in the directory: {}".format(local_storage_path), level="WARNING")
513
+
514
+ except Exception as e:
515
+ MediLink_ConfigLoader.log("Error occurred while listing files in {}: {}".format(local_storage_path, e), level="ERROR")
516
+
517
+ # Ensure that the function always returns a list
518
+ return downloaded_files
298
519
 
299
520
  def detect_new_files(directory_path, file_extension='.DAT'):
300
521
  """