medicafe 0.250811.4__tar.gz → 0.250812.1__tar.gz

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.
Files changed (76) hide show
  1. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot.bat +10 -4
  2. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/update_medicafe.py +53 -9
  3. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_837p_encoder.py +18 -2
  4. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_837p_encoder_library.py +7 -4
  5. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Up.py +20 -2
  6. {medicafe-0.250811.4 → medicafe-0.250812.1}/PKG-INFO +1 -1
  7. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/PKG-INFO +1 -1
  8. {medicafe-0.250811.4 → medicafe-0.250812.1}/setup.py +1 -1
  9. {medicafe-0.250811.4 → medicafe-0.250812.1}/LICENSE +0 -0
  10. {medicafe-0.250811.4 → medicafe-0.250812.1}/MANIFEST.in +0 -0
  11. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot.py +0 -0
  12. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Charges.py +0 -0
  13. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Crosswalk_Library.py +0 -0
  14. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
  15. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Post.py +0 -0
  16. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Preprocessor.py +0 -0
  17. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_Preprocessor_lib.py +0 -0
  18. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_UI.py +0 -0
  19. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_dataformat_library.py +0 -0
  20. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_docx_decoder.py +0 -0
  21. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/MediBot_smart_import.py +0 -0
  22. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/__init__.py +0 -0
  23. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/get_medicafe_version.py +0 -0
  24. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediBot/update_json.py +0 -0
  25. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/MediLink_ConfigLoader.py +0 -0
  26. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/__init__.py +0 -0
  27. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/__main__.py +0 -0
  28. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/api_core.py +0 -0
  29. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/api_core_backup.py +0 -0
  30. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/api_factory.py +0 -0
  31. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/api_utils.py +0 -0
  32. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/core_utils.py +0 -0
  33. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/graphql_utils.py +0 -0
  34. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/logging_config.py +0 -0
  35. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/logging_demo.py +0 -0
  36. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/migration_helpers.py +0 -0
  37. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediCafe/smart_import.py +0 -0
  38. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_837p_cob_library.py +0 -0
  39. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_837p_utilities.py +0 -0
  40. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_API_Generator.py +0 -0
  41. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Azure.py +0 -0
  42. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_ClaimStatus.py +0 -0
  43. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_DataMgmt.py +0 -0
  44. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Decoder.py +0 -0
  45. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Deductible.py +0 -0
  46. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Deductible_Validator.py +0 -0
  47. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Display_Utils.py +0 -0
  48. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Down.py +0 -0
  49. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Gmail.py +0 -0
  50. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Mailer.py +0 -0
  51. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Parser.py +0 -0
  52. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_PatientProcessor.py +0 -0
  53. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Scan.py +0 -0
  54. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_Scheduler.py +0 -0
  55. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_UI.py +0 -0
  56. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_insurance_utils.py +0 -0
  57. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_main.py +0 -0
  58. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/MediLink_smart_import.py +0 -0
  59. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/Soumit_api.py +0 -0
  60. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/__init__.py +0 -0
  61. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/gmail_http_utils.py +0 -0
  62. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/gmail_oauth_utils.py +0 -0
  63. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/insurance_type_integration_test.py +0 -0
  64. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/openssl.cnf +0 -0
  65. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/test.py +0 -0
  66. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/test_cob_library.py +0 -0
  67. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/test_timing.py +0 -0
  68. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/test_validation.py +0 -0
  69. {medicafe-0.250811.4 → medicafe-0.250812.1}/MediLink/webapp.html +0 -0
  70. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/SOURCES.txt +0 -0
  71. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/dependency_links.txt +0 -0
  72. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/entry_points.txt +0 -0
  73. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/not-zip-safe +0 -0
  74. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/requires.txt +0 -0
  75. {medicafe-0.250811.4 → medicafe-0.250812.1}/medicafe.egg-info/top_level.txt +0 -0
  76. {medicafe-0.250811.4 → medicafe-0.250812.1}/setup.cfg +0 -0
@@ -497,7 +497,7 @@ echo rem Wait briefly to ensure the main window has exited and released file loc
497
497
  echo ping 127.0.0.1 -n 3 ^>nul>> "%_UPD_RUNNER%"
498
498
  echo echo Starting MediCafe updater...>> "%_UPD_RUNNER%"
499
499
  :: XP-compatible Python presence check
500
- echo python --version ^>nul 2^>^&1>> "%_UPD_RUNNER%"
500
+ echo python --version ^>nul 2^>nul>> "%_UPD_RUNNER%"
501
501
  echo if errorlevel 1 (>> "%_UPD_RUNNER%"
502
502
  echo echo [ERROR] Python not found in PATH. Aborting update.>> "%_UPD_RUNNER%"
503
503
  echo goto :eof>> "%_UPD_RUNNER%"
@@ -506,9 +506,15 @@ echo )>> "%_UPD_RUNNER%"
506
506
  echo python "%upgrade_medicafe%" >> "%_UPD_RUNNER%"
507
507
  echo set "RET=%%ERRORLEVEL%%" >> "%_UPD_RUNNER%"
508
508
  echo echo Update process exited with code %%RET%%>> "%_UPD_RUNNER%"
509
- echo rem Relaunch MediBot if available>> "%_UPD_RUNNER%"
510
- echo if exist "%%MEDIBOT_PATH%%" start "MediBot" "%%MEDIBOT_PATH%%" >> "%_UPD_RUNNER%"
511
- echo exit /b %%RET%%>> "%_UPD_RUNNER%"
509
+ echo if %%RET%% neq 0 (>> "%_UPD_RUNNER%"
510
+ echo echo Update failed. Press any key to close...>> "%_UPD_RUNNER%"
511
+ echo pause ^>nul>> "%_UPD_RUNNER%"
512
+ echo exit /b %%RET%%>> "%_UPD_RUNNER%"
513
+ echo ) else (>> "%_UPD_RUNNER%"
514
+ echo rem Relaunch MediBot on success>> "%_UPD_RUNNER%"
515
+ echo if exist "%%MEDIBOT_PATH%%" start "MediBot" "%%MEDIBOT_PATH%%" >> "%_UPD_RUNNER%"
516
+ echo exit>> "%_UPD_RUNNER%"
517
+ echo )>> "%_UPD_RUNNER%"
512
518
 
513
519
  if not exist "%_UPD_RUNNER%" (
514
520
  echo [ERROR] Failed to create updater script at %_UPD_RUNNER%
@@ -15,6 +15,17 @@ except ImportError:
15
15
  requests = None
16
16
  print("Warning: requests module not available. Some functionality may be limited.")
17
17
 
18
+ # Safe tqdm import with fallback
19
+ try:
20
+ from tqdm import tqdm as _real_tqdm
21
+ def tqdm(iterable, **kwargs):
22
+ return _real_tqdm(iterable, **kwargs)
23
+ TQDM_AVAILABLE = True
24
+ except Exception:
25
+ TQDM_AVAILABLE = False
26
+ def tqdm(iterable, **kwargs):
27
+ return iterable
28
+
18
29
  # Initialize console output
19
30
  print("="*60)
20
31
  print("MediCafe Update Started")
@@ -205,9 +216,9 @@ def clear_python_cache(workspace_path=None):
205
216
  ]
206
217
 
207
218
  cleared_count = 0
219
+ # First, remove __pycache__ directories (these are few, so prints are acceptable)
208
220
  for cache_dir in cache_dirs:
209
221
  if os.path.exists(cache_dir):
210
- # Remove __pycache__ directories
211
222
  pycache_path = os.path.join(cache_dir, '__pycache__')
212
223
  if os.path.exists(pycache_path):
213
224
  try:
@@ -216,17 +227,50 @@ def clear_python_cache(workspace_path=None):
216
227
  cleared_count += 1
217
228
  except Exception as e:
218
229
  print("Warning: Could not clear cache at {}: {}".format(pycache_path, e))
219
-
220
- # Remove .pyc files
230
+
231
+ # Next, collect all .pyc files to provide a clean progress indicator
232
+ pyc_files = []
233
+ for cache_dir in cache_dirs:
234
+ if os.path.exists(cache_dir):
221
235
  for root, dirs, files in os.walk(cache_dir):
222
236
  for file in files:
223
237
  if file.endswith('.pyc'):
224
- try:
225
- os.remove(os.path.join(root, file))
226
- print("Removed .pyc file: {}".format(os.path.join(root, file)))
227
- cleared_count += 1
228
- except Exception as e:
229
- print("Warning: Could not remove .pyc file {}: {}".format(file, e))
238
+ pyc_files.append(os.path.join(root, file))
239
+
240
+ # Remove .pyc files with a progress bar (tqdm if available, otherwise a single-line counter)
241
+ if pyc_files:
242
+ total_files = len(pyc_files)
243
+ removed_files = 0
244
+
245
+ if TQDM_AVAILABLE:
246
+ for file_path in tqdm(pyc_files, desc="Removing .pyc files", unit="file"):
247
+ try:
248
+ os.remove(file_path)
249
+ cleared_count += 1
250
+ removed_files += 1
251
+ except Exception as e:
252
+ print("Warning: Could not remove .pyc file {}: {}".format(os.path.basename(file_path), e))
253
+ else:
254
+ # Minimal, XP-safe single-line progress indicator
255
+ for file_path in pyc_files:
256
+ try:
257
+ os.remove(file_path)
258
+ cleared_count += 1
259
+ removed_files += 1
260
+ except Exception as e:
261
+ print("Warning: Could not remove .pyc file {}: {}".format(os.path.basename(file_path), e))
262
+ # Update progress on one line
263
+ try:
264
+ sys.stdout.write("\rRemoving .pyc files: {}/{}".format(removed_files, total_files))
265
+ sys.stdout.flush()
266
+ except Exception:
267
+ pass
268
+ # Finish the line after completion
269
+ try:
270
+ sys.stdout.write("\n")
271
+ sys.stdout.flush()
272
+ except Exception:
273
+ pass
230
274
 
231
275
  if cleared_count > 0:
232
276
  print_status("Successfully cleared {} cache items".format(cleared_count), "SUCCESS")
@@ -502,8 +502,21 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
502
502
  # Initialize a dictionary to hold patient data segregated by confirmed endpoints
503
503
  data_by_endpoint = {}
504
504
 
505
+ # Sanitize input: filter out None or non-dict entries to avoid NoneType.get errors
506
+ if detailed_patient_data is None:
507
+ detailed_patient_data = []
508
+ safe_records = []
509
+ for record in detailed_patient_data:
510
+ if isinstance(record, dict):
511
+ safe_records.append(record)
512
+ else:
513
+ try:
514
+ MediLink_ConfigLoader.log("Skipping invalid patient record of type {}".format(type(record)), config, level="WARNING")
515
+ except Exception:
516
+ pass
517
+
505
518
  # Group patient data by endpoint
506
- for data in detailed_patient_data:
519
+ for data in safe_records:
507
520
  endpoint = data.get('confirmed_endpoint')
508
521
  if endpoint:
509
522
  if endpoint not in data_by_endpoint:
@@ -517,7 +530,10 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
517
530
  for endpoint, patient_data_list in data_by_endpoint.items():
518
531
  # Retrieve submission type from config; default to "batch" if not specified
519
532
  medi = extract_medilink_config(config)
520
- submission_type = medi.get('endpoints', {}).get(endpoint, {}).get('submission_type', 'batch')
533
+ endpoint_cfg = medi.get('endpoints', {}).get(endpoint)
534
+ if not isinstance(endpoint_cfg, dict):
535
+ endpoint_cfg = {}
536
+ submission_type = endpoint_cfg.get('submission_type', 'batch')
521
537
 
522
538
  if submission_type == 'single':
523
539
  # Process each patient's data individually for single-patient submissions
@@ -252,7 +252,7 @@ def create_1000A_submitter_name_segment(patient_data, config, endpoint):
252
252
  Returns:
253
253
  list: A list containing the NM1 segment for the submitter name and the PER segment for contact information.
254
254
  """
255
- endpoint_config = config['endpoints'].get(endpoint.upper(), {})
255
+ endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
256
256
  submitter_id_qualifier = endpoint_config.get('submitter_id_qualifier', '46') # '46' for ETIN or 'XX' for NPI
257
257
  submitter_name = endpoint_config.get('nm_103_value', 'DEFAULT NAME') # Default name if not in config
258
258
 
@@ -835,7 +835,7 @@ def _original_insurance_type_selection_logic(parsed_data):
835
835
  # Constructs the NM1 segment for subscriber based on parsed data and configuration.
836
836
  def create_nm1_subscriber_segment(config, parsed_data, endpoint):
837
837
  if endpoint.lower() == 'optumedi':
838
- entity_identifier_code = config['endpoints']['OPTUMEDI'].get('subscriber_entity_code', 'IL')
838
+ entity_identifier_code = config.get('endpoints', {}).get('OPTUMEDI', {}).get('subscriber_entity_code', 'IL')
839
839
  else:
840
840
  entity_identifier_code = 'IL' # Default value if endpoint is not 'optumedi'
841
841
 
@@ -928,6 +928,9 @@ def create_clm_and_related_segments(parsed_data, config, crosswalk):
928
928
  """
929
929
 
930
930
  # FINAL LINE OF DEFENSE: Validate all claim data before creating segments
931
+ # Ensure crosswalk is a dict to prevent NoneType.get errors downstream
932
+ if not isinstance(crosswalk, dict):
933
+ crosswalk = {}
931
934
  validated_data = validate_claim_data_for_837p(parsed_data, config, crosswalk)
932
935
 
933
936
  segments = []
@@ -1019,7 +1022,7 @@ def get_endpoint_config(config, endpoint):
1019
1022
  Returns:
1020
1023
  dict: The configuration for the specified endpoint, or raises an error if not found.
1021
1024
  """
1022
- endpoint_config = config['endpoints'].get(endpoint.upper(), None)
1025
+ endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), None)
1023
1026
 
1024
1027
  if endpoint_config is None:
1025
1028
  error_message = "Endpoint configuration for '{}' not found.".format(endpoint)
@@ -1071,7 +1074,7 @@ def create_interchange_header(config, endpoint, isa13):
1071
1074
  Returns:
1072
1075
  - Tuple containing the ISA segment, GS segment, and group control number (gs06).
1073
1076
  """
1074
- endpoint_config = config['endpoints'].get(endpoint.upper(), {})
1077
+ endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
1075
1078
 
1076
1079
  # Set defaults for ISA segment values
1077
1080
  isa02 = isa04 = " " # Default value for ISA02 and ISA04
@@ -92,13 +92,27 @@ def submit_claims(detailed_patient_data_grouped_by_endpoint, config, crosswalk):
92
92
 
93
93
  # Iterate through each endpoint and submit claims
94
94
  for endpoint, patients_data in tqdm(detailed_patient_data_grouped_by_endpoint.items(), desc="Progress", unit="endpoint"):
95
+ # Debug context to trace NoneType.get issues early
96
+ try:
97
+ log("[submit_claims] Starting endpoint: {}".format(endpoint), level="INFO")
98
+ if patients_data is None:
99
+ log("[submit_claims] Warning: patients_data is None for endpoint {}".format(endpoint), level="WARNING")
100
+ else:
101
+ try:
102
+ log("[submit_claims] patients_data count: {}".format(len(patients_data)), level="DEBUG")
103
+ except Exception:
104
+ log("[submit_claims] patients_data length unavailable (type: {})".format(type(patients_data)), level="DEBUG")
105
+ except Exception:
106
+ pass
107
+
95
108
  if not patients_data:
96
109
  continue
97
110
 
98
111
  # Determine the submission method (e.g., "winscp" or "api")
99
112
  try:
100
113
  method = cfg.get('endpoints', {}).get(endpoint, {}).get('submission_method', 'winscp')
101
- except Exception:
114
+ except Exception as e:
115
+ log("[submit_claims] Error deriving submission method for endpoint {}: {}".format(endpoint, e), level="ERROR")
102
116
  # Absolute fallback if cfg was unexpectedly not a dict
103
117
  method = 'winscp'
104
118
 
@@ -116,7 +130,11 @@ def submit_claims(detailed_patient_data_grouped_by_endpoint, config, crosswalk):
116
130
  print("Error: Unable to create API client")
117
131
  continue
118
132
  # Process files per endpoint
119
- converted_files = MediLink_837p_encoder.convert_files_for_submission(patients_data, config, crosswalk, client)
133
+ try:
134
+ converted_files = MediLink_837p_encoder.convert_files_for_submission(patients_data, config, crosswalk, client)
135
+ except Exception as e:
136
+ log("[submit_claims] convert_files_for_submission failed for endpoint {}: {}".format(endpoint, e), level="ERROR")
137
+ raise
120
138
  if converted_files:
121
139
  if method == 'winscp':
122
140
  # Transmit files via WinSCP
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250811.4
3
+ Version: 0.250812.1
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2/MediCafe
6
6
  Author: Daniel Vidaud
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250811.4
3
+ Version: 0.250812.1
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2/MediCafe
6
6
  Author: Daniel Vidaud
@@ -54,7 +54,7 @@ if long_description_text is None:
54
54
 
55
55
  setup(
56
56
  name='medicafe',
57
- version="0.250811.4",
57
+ version="0.250812.1",
58
58
  description='MediCafe',
59
59
  long_description=long_description_text,
60
60
  long_description_content_type='text/markdown',
File without changes
File without changes
File without changes