medicafe 0.250812.0__tar.gz → 0.250812.2__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.250812.0 → medicafe-0.250812.2}/MediBot/MediBot.bat +1 -1
  2. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_encoder.py +44 -3
  3. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_encoder_library.py +13 -5
  4. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Up.py +41 -2
  5. {medicafe-0.250812.0 → medicafe-0.250812.2}/PKG-INFO +1 -1
  6. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/PKG-INFO +1 -1
  7. {medicafe-0.250812.0 → medicafe-0.250812.2}/setup.py +1 -1
  8. {medicafe-0.250812.0 → medicafe-0.250812.2}/LICENSE +0 -0
  9. {medicafe-0.250812.0 → medicafe-0.250812.2}/MANIFEST.in +0 -0
  10. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot.py +0 -0
  11. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Charges.py +0 -0
  12. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Crosswalk_Library.py +0 -0
  13. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
  14. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Post.py +0 -0
  15. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Preprocessor.py +0 -0
  16. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_Preprocessor_lib.py +0 -0
  17. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_UI.py +0 -0
  18. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_dataformat_library.py +0 -0
  19. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_docx_decoder.py +0 -0
  20. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/MediBot_smart_import.py +0 -0
  21. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/__init__.py +0 -0
  22. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/get_medicafe_version.py +0 -0
  23. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/update_json.py +0 -0
  24. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediBot/update_medicafe.py +0 -0
  25. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/MediLink_ConfigLoader.py +0 -0
  26. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/__init__.py +0 -0
  27. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/__main__.py +0 -0
  28. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_core.py +0 -0
  29. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_core_backup.py +0 -0
  30. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_factory.py +0 -0
  31. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/api_utils.py +0 -0
  32. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/core_utils.py +0 -0
  33. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/graphql_utils.py +0 -0
  34. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/logging_config.py +0 -0
  35. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/logging_demo.py +0 -0
  36. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/migration_helpers.py +0 -0
  37. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediCafe/smart_import.py +0 -0
  38. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_cob_library.py +0 -0
  39. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_837p_utilities.py +0 -0
  40. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_API_Generator.py +0 -0
  41. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Azure.py +0 -0
  42. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_ClaimStatus.py +0 -0
  43. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_DataMgmt.py +0 -0
  44. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Decoder.py +0 -0
  45. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Deductible.py +0 -0
  46. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Deductible_Validator.py +0 -0
  47. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Display_Utils.py +0 -0
  48. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Down.py +0 -0
  49. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Gmail.py +0 -0
  50. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Mailer.py +0 -0
  51. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Parser.py +0 -0
  52. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_PatientProcessor.py +0 -0
  53. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Scan.py +0 -0
  54. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_Scheduler.py +0 -0
  55. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_UI.py +0 -0
  56. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_insurance_utils.py +0 -0
  57. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_main.py +0 -0
  58. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/MediLink_smart_import.py +0 -0
  59. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/Soumit_api.py +0 -0
  60. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/__init__.py +0 -0
  61. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/gmail_http_utils.py +0 -0
  62. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/gmail_oauth_utils.py +0 -0
  63. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/insurance_type_integration_test.py +0 -0
  64. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/openssl.cnf +0 -0
  65. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test.py +0 -0
  66. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_cob_library.py +0 -0
  67. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_timing.py +0 -0
  68. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/test_validation.py +0 -0
  69. {medicafe-0.250812.0 → medicafe-0.250812.2}/MediLink/webapp.html +0 -0
  70. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/SOURCES.txt +0 -0
  71. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/dependency_links.txt +0 -0
  72. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/entry_points.txt +0 -0
  73. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/not-zip-safe +0 -0
  74. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/requires.txt +0 -0
  75. {medicafe-0.250812.0 → medicafe-0.250812.2}/medicafe.egg-info/top_level.txt +0 -0
  76. {medicafe-0.250812.0 → medicafe-0.250812.2}/setup.cfg +0 -0
@@ -509,7 +509,7 @@ echo echo Update process exited with code %%RET%%>> "%_UPD_RUNNER%"
509
509
  echo if %%RET%% neq 0 (>> "%_UPD_RUNNER%"
510
510
  echo echo Update failed. Press any key to close...>> "%_UPD_RUNNER%"
511
511
  echo pause ^>nul>> "%_UPD_RUNNER%"
512
- echo exit /b %%RET%%>> "%_UPD_RUNNER%"
512
+ echo exit %%RET%%>> "%_UPD_RUNNER%"
513
513
  echo ) else (>> "%_UPD_RUNNER%"
514
514
  echo rem Relaunch MediBot on success>> "%_UPD_RUNNER%"
515
515
  echo if exist "%%MEDIBOT_PATH%%" start "MediBot" "%%MEDIBOT_PATH%%" >> "%_UPD_RUNNER%"
@@ -499,11 +499,32 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
499
499
  - Future implementation may include progress tracking using tools like `tqdm`.
500
500
  """
501
501
 
502
+ # Ensure crosswalk is a dictionary to avoid NoneType access downstream
503
+ if not isinstance(crosswalk, dict):
504
+ try:
505
+ MediLink_ConfigLoader.log("[convert_files_for_submission] crosswalk is not a dict; type={}".format(type(crosswalk)), config, level="WARNING")
506
+ except Exception:
507
+ pass
508
+ crosswalk = {}
509
+
502
510
  # Initialize a dictionary to hold patient data segregated by confirmed endpoints
503
511
  data_by_endpoint = {}
504
512
 
513
+ # Sanitize input: filter out None or non-dict entries to avoid NoneType.get errors
514
+ if detailed_patient_data is None:
515
+ detailed_patient_data = []
516
+ safe_records = []
517
+ for record in detailed_patient_data:
518
+ if isinstance(record, dict):
519
+ safe_records.append(record)
520
+ else:
521
+ try:
522
+ MediLink_ConfigLoader.log("Skipping invalid patient record of type {}".format(type(record)), config, level="WARNING")
523
+ except Exception:
524
+ pass
525
+
505
526
  # Group patient data by endpoint
506
- for data in detailed_patient_data:
527
+ for data in safe_records:
507
528
  endpoint = data.get('confirmed_endpoint')
508
529
  if endpoint:
509
530
  if endpoint not in data_by_endpoint:
@@ -515,9 +536,20 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
515
536
 
516
537
  # Iterate over each endpoint and process its corresponding patient data
517
538
  for endpoint, patient_data_list in data_by_endpoint.items():
539
+ try:
540
+ MediLink_ConfigLoader.log("[convert_files_for_submission] Endpoint {} has {} records".format(endpoint, len(patient_data_list)), config, level="INFO")
541
+ except Exception:
542
+ pass
518
543
  # Retrieve submission type from config; default to "batch" if not specified
519
544
  medi = extract_medilink_config(config)
520
- submission_type = medi.get('endpoints', {}).get(endpoint, {}).get('submission_type', 'batch')
545
+ endpoint_cfg = medi.get('endpoints', {}).get(endpoint)
546
+ if not isinstance(endpoint_cfg, dict):
547
+ endpoint_cfg = {}
548
+ submission_type = endpoint_cfg.get('submission_type', 'batch')
549
+ try:
550
+ MediLink_ConfigLoader.log("[convert_files_for_submission] submission_type for {}: {}".format(endpoint, submission_type), config, level="DEBUG")
551
+ except Exception:
552
+ pass
521
553
 
522
554
  if submission_type == 'single':
523
555
  # Process each patient's data individually for single-patient submissions
@@ -531,7 +563,16 @@ def convert_files_for_submission(detailed_patient_data, config, crosswalk, clien
531
563
  converted_files_paths.append(converted_path)
532
564
  else:
533
565
  # Process all patient data together for batch submissions
534
- converted_path = process_claim(config, endpoint, patient_data_list, crosswalk, client)
566
+ try:
567
+ converted_path = process_claim(config, endpoint, patient_data_list, crosswalk, client)
568
+ except Exception as e:
569
+ import traceback as _tb
570
+ tb_s = _tb.format_exc()
571
+ try:
572
+ MediLink_ConfigLoader.log("[convert_files_for_submission] process_claim failed for {}: {}\nTraceback: {}".format(endpoint, e, tb_s), config, level="ERROR")
573
+ except Exception:
574
+ pass
575
+ raise
535
576
  if converted_path:
536
577
  converted_files_paths.append(converted_path)
537
578
 
@@ -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
 
@@ -779,7 +779,12 @@ def _original_insurance_type_selection_logic(parsed_data):
779
779
 
780
780
  # Retrieve insurance options with codes and descriptions
781
781
  config, _ = MediLink_ConfigLoader.load_configuration()
782
- insurance_options = config['MediLink_Config'].get('insurance_options')
782
+ try:
783
+ from MediCafe.core_utils import extract_medilink_config
784
+ medi = extract_medilink_config(config)
785
+ insurance_options = medi.get('insurance_options', {})
786
+ except Exception:
787
+ insurance_options = {}
783
788
 
784
789
  # If COB library is available, augment options with Medicare codes (MB/MA/MC)
785
790
  if COB is not None:
@@ -835,7 +840,7 @@ def _original_insurance_type_selection_logic(parsed_data):
835
840
  # Constructs the NM1 segment for subscriber based on parsed data and configuration.
836
841
  def create_nm1_subscriber_segment(config, parsed_data, endpoint):
837
842
  if endpoint.lower() == 'optumedi':
838
- entity_identifier_code = config['endpoints']['OPTUMEDI'].get('subscriber_entity_code', 'IL')
843
+ entity_identifier_code = config.get('endpoints', {}).get('OPTUMEDI', {}).get('subscriber_entity_code', 'IL')
839
844
  else:
840
845
  entity_identifier_code = 'IL' # Default value if endpoint is not 'optumedi'
841
846
 
@@ -928,6 +933,9 @@ def create_clm_and_related_segments(parsed_data, config, crosswalk):
928
933
  """
929
934
 
930
935
  # FINAL LINE OF DEFENSE: Validate all claim data before creating segments
936
+ # Ensure crosswalk is a dict to prevent NoneType.get errors downstream
937
+ if not isinstance(crosswalk, dict):
938
+ crosswalk = {}
931
939
  validated_data = validate_claim_data_for_837p(parsed_data, config, crosswalk)
932
940
 
933
941
  segments = []
@@ -1019,7 +1027,7 @@ def get_endpoint_config(config, endpoint):
1019
1027
  Returns:
1020
1028
  dict: The configuration for the specified endpoint, or raises an error if not found.
1021
1029
  """
1022
- endpoint_config = config['endpoints'].get(endpoint.upper(), None)
1030
+ endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), None)
1023
1031
 
1024
1032
  if endpoint_config is None:
1025
1033
  error_message = "Endpoint configuration for '{}' not found.".format(endpoint)
@@ -1071,7 +1079,7 @@ def create_interchange_header(config, endpoint, isa13):
1071
1079
  Returns:
1072
1080
  - Tuple containing the ISA segment, GS segment, and group control number (gs06).
1073
1081
  """
1074
- endpoint_config = config['endpoints'].get(endpoint.upper(), {})
1082
+ endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
1075
1083
 
1076
1084
  # Set defaults for ISA segment values
1077
1085
  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,32 @@ 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
+ # Sanitize patient data structure before conversion
135
+ safe_patients = []
136
+ if isinstance(patients_data, list):
137
+ safe_patients = [item for item in patients_data if isinstance(item, dict)]
138
+ elif isinstance(patients_data, dict):
139
+ safe_patients = [patients_data]
140
+ else:
141
+ log("[submit_claims] Unexpected patients_data type for {}: {}".format(endpoint, type(patients_data)), level="ERROR")
142
+ safe_patients = []
143
+
144
+ converted_files = MediLink_837p_encoder.convert_files_for_submission(safe_patients, config, crosswalk, client)
145
+ except Exception as e:
146
+ tb = traceback.format_exc()
147
+ # Log via logger (may fail if logger expects config); also print to stderr to guarantee visibility
148
+ try:
149
+ log("[submit_claims] convert_files_for_submission failed for endpoint {}: {}\nTraceback: {}".format(endpoint, e, tb), level="ERROR")
150
+ except Exception:
151
+ pass
152
+ try:
153
+ import sys as _sys
154
+ _sys.stderr.write("[submit_claims] convert_files_for_submission failed for endpoint {}: {}\n".format(endpoint, e))
155
+ _sys.stderr.write(tb + "\n")
156
+ except Exception:
157
+ pass
158
+ raise
120
159
  if converted_files:
121
160
  if method == 'winscp':
122
161
  # Transmit files via WinSCP
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250812.0
3
+ Version: 0.250812.2
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.250812.0
3
+ Version: 0.250812.2
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.250812.0",
57
+ version="0.250812.2",
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