medicafe 0.250723.0__tar.gz → 0.250723.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.

Potentially problematic release.


This version of medicafe might be problematic. Click here for more details.

Files changed (59) hide show
  1. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot.py +7 -0
  2. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_Crosswalk_Library.py +27 -9
  3. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_Preprocessor_lib.py +29 -4
  4. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_837p_encoder_library.py +142 -20
  5. {medicafe-0.250723.0 → medicafe-0.250723.2}/PKG-INFO +1 -1
  6. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/PKG-INFO +1 -1
  7. {medicafe-0.250723.0 → medicafe-0.250723.2}/setup.py +1 -1
  8. {medicafe-0.250723.0 → medicafe-0.250723.2}/LICENSE +0 -0
  9. {medicafe-0.250723.0 → medicafe-0.250723.2}/MANIFEST.in +0 -0
  10. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot.bat +0 -0
  11. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_Charges.py +0 -0
  12. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_Post.py +0 -0
  13. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_Preprocessor.py +0 -0
  14. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_UI.py +0 -0
  15. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_dataformat_library.py +0 -0
  16. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/MediBot_docx_decoder.py +0 -0
  17. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/PDF_to_CSV_Cleaner.py +0 -0
  18. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/__init__.py +0 -0
  19. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/update_json.py +0 -0
  20. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediBot/update_medicafe.py +0 -0
  21. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink.py +0 -0
  22. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_837p_cob_library.py +0 -0
  23. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_837p_encoder.py +0 -0
  24. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_837p_utilities.py +0 -0
  25. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_API_Generator.py +0 -0
  26. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_API_v2.py +0 -0
  27. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_API_v3.py +0 -0
  28. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_APIs.py +0 -0
  29. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Azure.py +0 -0
  30. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_ClaimStatus.py +0 -0
  31. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_ConfigLoader.py +0 -0
  32. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_DataMgmt.py +0 -0
  33. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Decoder.py +0 -0
  34. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Deductible.py +0 -0
  35. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Deductible_Validator.py +0 -0
  36. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Down.py +0 -0
  37. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Gmail.py +0 -0
  38. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_GraphQL.py +0 -0
  39. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Mailer.py +0 -0
  40. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Parser.py +0 -0
  41. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Scan.py +0 -0
  42. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Scheduler.py +0 -0
  43. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_UI.py +0 -0
  44. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_Up.py +0 -0
  45. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/MediLink_batch.bat +0 -0
  46. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/Soumit_api.py +0 -0
  47. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/__init__.py +0 -0
  48. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/openssl.cnf +0 -0
  49. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/test.py +0 -0
  50. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/test_cob_library.py +0 -0
  51. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/test_validation.py +0 -0
  52. {medicafe-0.250723.0 → medicafe-0.250723.2}/MediLink/webapp.html +0 -0
  53. {medicafe-0.250723.0 → medicafe-0.250723.2}/README.md +0 -0
  54. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/SOURCES.txt +0 -0
  55. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/dependency_links.txt +0 -0
  56. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/not-zip-safe +0 -0
  57. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/requires.txt +0 -0
  58. {medicafe-0.250723.0 → medicafe-0.250723.2}/medicafe.egg-info/top_level.txt +0 -0
  59. {medicafe-0.250723.0 → medicafe-0.250723.2}/setup.cfg +0 -0
@@ -66,13 +66,16 @@ def run_ahk_script(script_content):
66
66
 
67
67
  if process.returncode != 0:
68
68
  print("AHK script failed with exit status: {}".format(process.returncode)) # Log the exit status of the failed script
69
+ MediLink_ConfigLoader.log("AHK script failed with exit status: {}".format(process.returncode), level="ERROR")
69
70
  if stderr:
70
71
  print("AHK Error: {}".format(stderr.decode('utf-8', errors='ignore')))
72
+ MediLink_ConfigLoader.log("AHK Error: {}".format(stderr.decode('utf-8', errors='ignore')), level="ERROR")
71
73
  return # Success - no file cleanup needed
72
74
 
73
75
  except Exception as e:
74
76
  # If stdin method fails, fall back to traditional file method
75
77
  print("AHK stdin execution failed, falling back to file method: {}".format(e))
78
+ MediLink_ConfigLoader.log("AHK stdin execution failed, falling back to file method: {}".format(e), level="ERROR")
76
79
  # Continue to fallback implementation below
77
80
 
78
81
  # Traditional file-based method (fallback or when optimization disabled)
@@ -87,9 +90,12 @@ def run_ahk_script(script_content):
87
90
  subprocess.check_call([AHK_EXECUTABLE, temp_script_name]) # Execute the AHK script using the AutoHotkey executable
88
91
  except subprocess.CalledProcessError as e:
89
92
  print("AHK script failed with exit status: {}".format(e.returncode)) # Log the exit status of the failed script
93
+ MediLink_ConfigLoader.log("AHK script failed with exit status: {}".format(e.returncode), level="ERROR")
90
94
  print("Output from AHK script: {}".format(e.output)) # Log the output from the failed script
95
+ MediLink_ConfigLoader.log("Output from AHK script: {}".format(e.output), level="ERROR")
91
96
  except Exception as e:
92
97
  print("An unexpected error occurred while running the AHK script: {}".format(e)) # Log any unexpected errors
98
+ MediLink_ConfigLoader.log("An unexpected error occurred while running the AHK script: {}".format(e), level="ERROR")
93
99
  traceback.print_exc() # Print the full traceback for debugging purposes
94
100
  finally:
95
101
  # Delete the temporary script file
@@ -98,6 +104,7 @@ def run_ahk_script(script_content):
98
104
  os.unlink(temp_script_name) # Attempt to delete the temporary script file
99
105
  except OSError as e:
100
106
  print("Error deleting temporary script file: {}".format(e)) # Log any errors encountered while deleting the file
107
+ MediLink_ConfigLoader.log("Error deleting temporary script file: {}".format(e), level="ERROR")
101
108
  # Future Improvement: Implement a cleanup mechanism to handle orphaned temporary files
102
109
 
103
110
  # Global variable to store the last processed entry
@@ -279,19 +279,22 @@ def check_crosswalk_health(crosswalk):
279
279
  crosswalk (dict): The crosswalk dictionary to check.
280
280
 
281
281
  Returns:
282
- tuple: (is_healthy, missing_names_count, missing_medisoft_ids_count)
282
+ tuple: (is_healthy, missing_names_count, missing_medisoft_ids_count, missing_names_list, missing_medisoft_ids_list)
283
283
  """
284
284
  if 'payer_id' not in crosswalk or not crosswalk['payer_id']:
285
- return False, 0, 0
285
+ return False, 0, 0, [], []
286
286
 
287
287
  missing_names = 0
288
288
  missing_medisoft_ids = 0
289
+ missing_names_list = []
290
+ missing_medisoft_ids_list = []
289
291
 
290
292
  for payer_id, details in crosswalk['payer_id'].items():
291
293
  # Check if name is missing or "Unknown"
292
294
  name = details.get('name', '')
293
295
  if not name or name == 'Unknown':
294
296
  missing_names += 1
297
+ missing_names_list.append(payer_id)
295
298
 
296
299
  # Check if at least one medisoft ID exists in either field
297
300
  medisoft_id = details.get('medisoft_id', [])
@@ -306,10 +309,11 @@ def check_crosswalk_health(crosswalk):
306
309
  # If both are empty, count as missing; if either has at least one, it's healthy
307
310
  if not medisoft_id and not medisoft_medicare_id:
308
311
  missing_medisoft_ids += 1
312
+ missing_medisoft_ids_list.append(payer_id)
309
313
 
310
314
  # Consider healthy if no missing names and no missing medisoft IDs
311
315
  is_healthy = (missing_names == 0 and missing_medisoft_ids == 0)
312
- return is_healthy, missing_names, missing_medisoft_ids
316
+ return is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list
313
317
 
314
318
  def prompt_user_for_api_calls(crosswalk, config):
315
319
  """
@@ -324,7 +328,7 @@ def prompt_user_for_api_calls(crosswalk, config):
324
328
  bool: True if should proceed with API calls, False if should skip
325
329
  """
326
330
 
327
- is_healthy, missing_names, missing_medisoft_ids = check_crosswalk_health(crosswalk)
331
+ is_healthy, missing_names, missing_medisoft_ids, missing_names_list, missing_medisoft_ids_list = check_crosswalk_health(crosswalk)
328
332
  total_payers = len(crosswalk.get('payer_id', {}))
329
333
 
330
334
  if is_healthy:
@@ -362,13 +366,27 @@ def prompt_user_for_api_calls(crosswalk, config):
362
366
  else:
363
367
  print("\nCrosswalk needs attention:")
364
368
  print(" - {} payers found".format(total_payers))
369
+
370
+ # Show detailed information about missing names
365
371
  if missing_names > 0:
366
- print(" - {} payers missing names".format(missing_names))
372
+ print(" - {} payers missing names: {}".format(missing_names, ", ".join(missing_names_list)))
373
+
374
+ # Show detailed information about missing medisoft IDs
367
375
  if missing_medisoft_ids > 0:
368
- print(" - {} payers missing medisoft IDs".format(missing_medisoft_ids))
369
- print("Proceeding with API validation calls...")
370
- MediLink_ConfigLoader.log("Crosswalk unhealthy - proceeding with API calls", config, level="INFO")
371
- return True
376
+ print(" - {} payers missing medisoft IDs: {}".format(missing_medisoft_ids, ", ".join(missing_medisoft_ids_list)))
377
+ # API validation CANNOT resolve missing medisoft IDs
378
+ print(" TODO: Need user interface to manually input medisoft IDs for these payers")
379
+
380
+ # Only proceed with API calls if there are missing names (API can help with those)
381
+ if missing_names > 0:
382
+ print("Proceeding with API validation calls to resolve missing names...")
383
+ MediLink_ConfigLoader.log("Crosswalk has missing names - proceeding with API calls", config, level="INFO")
384
+ return True
385
+ else:
386
+ print("No missing names to resolve via API. Skipping API validation calls.")
387
+ print("TODO: Manual intervention needed for missing medisoft IDs")
388
+ MediLink_ConfigLoader.log("Crosswalk has missing medisoft IDs but no missing names - skipping API calls", config, level="INFO")
389
+ return False
372
390
 
373
391
  def crosswalk_update(client, config, crosswalk, skip_known_payers=True): # Upstream of this is only MediBot_Preprocessor.py and MediBot.py
374
392
  """
@@ -434,28 +434,50 @@ def update_procedure_codes(csv_data, crosswalk):
434
434
  for diagnosis_code in diagnosis_codes
435
435
  }
436
436
 
437
- # Initialize counter for updated rows
437
+ # Initialize counters for tracking
438
438
  updated_count = 0
439
+ missing_medisoft_codes = set()
440
+ missing_procedure_mappings = set()
439
441
 
440
442
  # Update the "Procedure Code" column in the CSV data
441
443
  for row_num, row in enumerate(csv_data, start=1):
442
444
  try:
443
445
  medisoft_code = row.get('Default Diagnosis #1', '').strip()
444
446
  diagnosis_code = medisoft_to_diagnosis.get(medisoft_code)
447
+
445
448
  if diagnosis_code:
446
449
  procedure_code = diagnosis_to_procedure.get(diagnosis_code)
447
450
  if procedure_code:
448
451
  row['Procedure Code'] = procedure_code
449
452
  updated_count += 1
450
453
  else:
451
- row['Procedure Code'] = "Unknown" # Or handle as appropriate
454
+ # Track missing procedure mapping
455
+ missing_procedure_mappings.add(diagnosis_code)
456
+ row['Procedure Code'] = "Unknown" # Will be handled by 837p encoder
457
+ MediLink_ConfigLoader.log("Missing procedure mapping for diagnosis code '{}' (Medisoft code: '{}') in row {}".format(
458
+ diagnosis_code, medisoft_code, row_num), level="WARNING")
452
459
  else:
453
- row['Procedure Code'] = "Unknown" # Or handle as appropriate
460
+ # Track missing Medisoft code mapping
461
+ if medisoft_code: # Only track if there's actually a code
462
+ missing_medisoft_codes.add(medisoft_code)
463
+ row['Procedure Code'] = "Unknown" # Will be handled by 837p encoder
464
+ MediLink_ConfigLoader.log("Missing Medisoft code mapping for '{}' in row {}".format(
465
+ medisoft_code, row_num), level="WARNING")
454
466
  except Exception as e:
455
467
  MediLink_ConfigLoader.log("In update_procedure_codes, Error processing row {}: {}".format(row_num, e), level="ERROR")
456
468
 
457
- # Log total count of updated rows
469
+ # Log summary statistics
458
470
  MediLink_ConfigLoader.log("Total {} 'Procedure Code' rows updated.".format(updated_count), level="INFO")
471
+
472
+ if missing_medisoft_codes:
473
+ MediLink_ConfigLoader.log("Missing Medisoft code mappings: {}".format(sorted(missing_medisoft_codes)), level="WARNING")
474
+ print("WARNING: {} Medisoft codes need to be added to diagnosis_to_medisoft mapping: {}".format(
475
+ len(missing_medisoft_codes), sorted(missing_medisoft_codes)))
476
+
477
+ if missing_procedure_mappings:
478
+ MediLink_ConfigLoader.log("Missing procedure mappings for diagnosis codes: {}".format(sorted(missing_procedure_mappings)), level="WARNING")
479
+ print("WARNING: {} diagnosis codes need to be added to procedure_to_diagnosis mapping: {}".format(
480
+ len(missing_procedure_mappings), sorted(missing_procedure_mappings)))
459
481
 
460
482
  return True
461
483
 
@@ -573,8 +595,11 @@ def update_diagnosis_codes(csv_data):
573
595
  # Convert diagnosis code to Medisoft shorthand format.
574
596
  medisoft_shorthand = diagnosis_to_medisoft.get(diagnosis_code, None)
575
597
  if medisoft_shorthand is None and diagnosis_code:
598
+ # Use fallback logic for missing mapping
576
599
  defaulted_code = diagnosis_code.lstrip('H').lstrip('T8').replace('.', '')[-5:]
577
600
  medisoft_shorthand = defaulted_code
601
+ MediLink_ConfigLoader.log("Missing diagnosis mapping for '{}', using fallback code '{}'".format(
602
+ diagnosis_code, medisoft_shorthand), level="WARNING")
578
603
  MediLink_ConfigLoader.log("Converted diagnosis code to Medisoft shorthand: {}".format(medisoft_shorthand), level="DEBUG")
579
604
 
580
605
  row['Default Diagnosis #1'] = medisoft_shorthand
@@ -733,41 +733,37 @@ def create_clm_and_related_segments(parsed_data, config, crosswalk):
733
733
  See MediLink_837p_cob_library.create_enhanced_clm_segment() for enhanced implementation.
734
734
  """
735
735
 
736
+ # FINAL LINE OF DEFENSE: Validate all claim data before creating segments
737
+ validated_data = validate_claim_data_for_837p(parsed_data, config, crosswalk)
738
+
736
739
  segments = []
737
740
 
738
741
  # Format the claim number
739
- chart_number = parsed_data.get('CHART', '')
740
- date_of_service = parsed_data.get('DATE', '')
742
+ chart_number = validated_data.get('CHART', '')
743
+ date_of_service = validated_data.get('DATE', '')
741
744
  formatted_claim_number = format_claim_number(chart_number, date_of_service)
742
745
 
743
746
  # CLM - Claim Information
744
747
  # TODO (COB ENHANCEMENT): Enhanced claim frequency handling
745
748
  # For COB claims, CLM05-3 should be "1" (original) unless replacement logic applies
746
749
  claim_frequency = "1" # Default to original
747
- # if parsed_data.get('claim_type') == 'secondary':
750
+ # if validated_data.get('claim_type') == 'secondary':
748
751
  # claim_frequency = "1" # Original for secondary claims
749
752
 
750
753
  segments.append("CLM*{}*{}***{}:B:{}*Y*A*Y*Y~".format(
751
754
  formatted_claim_number,
752
- parsed_data['AMOUNT'],
753
- parsed_data['TOS'],
755
+ validated_data['AMOUNT'],
756
+ validated_data['TOS'],
754
757
  claim_frequency))
755
758
 
756
759
  # HI - Health Care Diagnosis Code
757
760
  # Hardcoding "ABK" for ICD-10 codes as they are the only ones used now.
758
- medisoft_code = ''.join(filter(str.isalnum, parsed_data['DIAG']))
761
+ medisoft_code = ''.join(filter(str.isalnum, validated_data['DIAG']))
759
762
  diagnosis_code = next((key for key, value in crosswalk.get('diagnosis_to_medisoft', {}).items() if value == medisoft_code), None)
760
763
 
764
+ # This should never be None now due to validation, but keeping as safety check
761
765
  if diagnosis_code is None:
762
- error_message = "Diagnosis code is empty for chart number: {}. Please verify. Medisoft code is {}".format(chart_number, medisoft_code)
763
- MediLink_ConfigLoader.log(error_message, config, level="CRITICAL")
764
- print(error_message)
765
- diagnosis_code = input("Enter the complete diagnosis code: ")
766
- # Update the crosswalk dictionary with the new pairing of diagnosis_code and medisoft_code.
767
- crosswalk['diagnosis_to_medisoft'][diagnosis_code] = medisoft_code
768
- MediLink_ConfigLoader.log("Updated crosswalk with new diagnosis code: {}, for Medisoft code {}".format(diagnosis_code, medisoft_code), config, level="INFO")
769
- # TODO This needs to actually save the .json though which right now I'd like to route through the dedicated function for updating the crosswalk.
770
- # TODO This should have been a validation exercise upstream and not a last minute check like this.
766
+ raise ValueError("Diagnosis code mapping failed for patient {} with medisoft code {}".format(chart_number, medisoft_code))
771
767
 
772
768
  cleaned_diagnosis_code = ''.join(char for char in diagnosis_code if char.isalnum())
773
769
  segments.append("HI*ABK:{}~".format(cleaned_diagnosis_code))
@@ -795,13 +791,13 @@ def create_clm_and_related_segments(parsed_data, config, crosswalk):
795
791
 
796
792
  # SV1 - Professional Service
797
793
  segments.append("SV1*HC:{}:{}*{}*MJ*{}***1~".format(
798
- parsed_data['CODEA'],
799
- parsed_data['POS'],
800
- parsed_data['AMOUNT'],
801
- parsed_data['MINTUES']))
794
+ validated_data['CODEA'],
795
+ validated_data['POS'],
796
+ validated_data['AMOUNT'],
797
+ validated_data['MINTUES']))
802
798
 
803
799
  # DTP - Date
804
- segments.append("DTP*472*D8*{}~".format(convert_date_format(parsed_data['DATE'])))
800
+ segments.append("DTP*472*D8*{}~".format(convert_date_format(validated_data['DATE'])))
805
801
 
806
802
  # Is there REF - Line Item Control Number missing here? Private insurance doesn't need it, but Medicare does?
807
803
  # segments.append("REF*6R*1~") # REF01, Reference Identification Qualifier; REF02, Line Item Control Number.
@@ -946,5 +942,131 @@ def create_interchange_trailer(config, num_transactions, isa13, num_functional_g
946
942
 
947
943
  return ge_segment, iea_segment
948
944
 
945
+ def validate_claim_data_for_837p(parsed_data, config, crosswalk):
946
+ """
947
+ Final line of defense validation for 837P claim data.
948
+
949
+ This function validates that all required fields have valid values before creating
950
+ the 837P claim. If invalid values are found, it provides detailed user guidance
951
+ and allows manual input to correct the data.
952
+
953
+ Parameters:
954
+ - parsed_data: Dictionary containing claim data
955
+ - config: Configuration settings
956
+ - crosswalk: Crosswalk data for mappings
957
+
958
+ Returns:
959
+ - Dictionary with validated claim data, or raises ValueError if validation fails
960
+ """
961
+ validated_data = parsed_data.copy()
962
+ chart_number = parsed_data.get('CHART', 'UNKNOWN')
963
+
964
+ # Validate diagnosis code
965
+ medisoft_code = ''.join(filter(str.isalnum, parsed_data.get('DIAG', '')))
966
+ diagnosis_code = next((key for key, value in crosswalk.get('diagnosis_to_medisoft', {}).items() if value == medisoft_code), None)
967
+
968
+ if not diagnosis_code:
969
+ # Log the error condition with detailed context
970
+ error_message = "Diagnosis code is empty for chart number: {}. Please verify. Medisoft code is {}".format(chart_number, medisoft_code)
971
+ MediLink_ConfigLoader.log(error_message, config, level="CRITICAL")
972
+
973
+ print("\n{}".format("="*80))
974
+ print("CRITICAL: Missing diagnosis code mapping for patient {}".format(chart_number))
975
+ print("{}".format("="*80))
976
+ print("Medisoft code: '{}'".format(medisoft_code))
977
+ print("Patient: {}, {}".format(parsed_data.get('LAST', 'Unknown'), parsed_data.get('FIRST', 'Unknown')))
978
+ print("Service Date: {}".format(parsed_data.get('DATE', 'Unknown')))
979
+ print("\nThis diagnosis code needs to be added to the crosswalk.json file.")
980
+ print("\nCurrent diagnosis_to_medisoft mapping format:")
981
+
982
+ # Show example entries from the crosswalk
983
+ diagnosis_examples = list(crosswalk.get('diagnosis_to_medisoft', {}).items())[:3]
984
+ for full_code, medisoft_short in diagnosis_examples:
985
+ print(" '{}': '{}'".format(full_code, medisoft_short))
986
+
987
+ print("\nPlease enter the complete ICD-10 diagnosis code (e.g., H25.10):")
988
+ diagnosis_code = input("> ").strip()
989
+
990
+ if not diagnosis_code:
991
+ raise ValueError("Cannot proceed without diagnosis code for patient {}".format(chart_number))
992
+
993
+ # Update the crosswalk dictionary with the new pairing of diagnosis_code and medisoft_code
994
+ crosswalk['diagnosis_to_medisoft'][diagnosis_code] = medisoft_code
995
+ MediLink_ConfigLoader.log("Updated crosswalk with new diagnosis code: {}, for Medisoft code {}".format(diagnosis_code, medisoft_code), config, level="INFO")
996
+ print("\n✓ Added '{}' -> '{}' to crosswalk".format(diagnosis_code, medisoft_code))
997
+ print(" IMPORTANT: You must manually save this to crosswalk.json to persist the change!")
998
+ # TODO This needs to actually save the .json though which right now I'd like to route through the dedicated function for updating the crosswalk.
999
+ # TODO This should have been a validation exercise upstream and not a last minute check like this.
1000
+
1001
+ # Validate procedure code
1002
+ procedure_code = parsed_data.get('CODEA', '').strip()
1003
+ if not procedure_code or procedure_code.lower() in ['unknown', 'none', '']:
1004
+ # Log the error condition with detailed context
1005
+ error_message = "Procedure code is empty for chart number: {}. Please verify. Diagnosis code is {}".format(chart_number, diagnosis_code)
1006
+ MediLink_ConfigLoader.log(error_message, config, level="CRITICAL")
1007
+
1008
+ print("\n{}".format("="*80))
1009
+ print("CRITICAL: Missing procedure code for patient {}".format(chart_number))
1010
+ print("{}".format("="*80))
1011
+ print("Diagnosis: {}".format(diagnosis_code))
1012
+ print("Patient: {}, {}".format(parsed_data.get('LAST', 'Unknown'), parsed_data.get('FIRST', 'Unknown')))
1013
+ print("Service Date: {}".format(parsed_data.get('DATE', 'Unknown')))
1014
+ print("\nThis procedure code needs to be added to the crosswalk.json file.")
1015
+ print("\nCurrent procedure_to_diagnosis mapping format:")
1016
+
1017
+ # Show example entries from the crosswalk
1018
+ procedure_examples = list(crosswalk.get('procedure_to_diagnosis', {}).items())[:3]
1019
+ for proc_code, diagnosis_list in procedure_examples:
1020
+ print(" '{}': {}".format(proc_code, diagnosis_list))
1021
+
1022
+ print("\nPlease enter the CPT procedure code (e.g., 66984):")
1023
+ procedure_code = input("> ").strip()
1024
+
1025
+ if not procedure_code:
1026
+ raise ValueError("Cannot proceed without procedure code for patient {}".format(chart_number))
1027
+
1028
+ # Update the crosswalk dictionary with the new pairing of procedure_code and diagnosis_code
1029
+ if procedure_code not in crosswalk.get('procedure_to_diagnosis', {}):
1030
+ crosswalk.setdefault('procedure_to_diagnosis', {})[procedure_code] = []
1031
+ crosswalk['procedure_to_diagnosis'][procedure_code].append(diagnosis_code)
1032
+ MediLink_ConfigLoader.log("Updated crosswalk with new procedure code: {}, for diagnosis code {}".format(procedure_code, diagnosis_code), config, level="INFO")
1033
+ print("\n✓ Added '{}' -> ['{}'] to crosswalk".format(procedure_code, diagnosis_code))
1034
+ print(" IMPORTANT: You must manually save this to crosswalk.json to persist the change!")
1035
+ # TODO This needs to actually save the .json though which right now I'd like to route through the dedicated function for updating the crosswalk.
1036
+ # TODO This should have been a validation exercise upstream and not a last minute check like this.
1037
+
1038
+ # Update the validated data
1039
+ validated_data['DIAG'] = medisoft_code
1040
+ validated_data['CODEA'] = procedure_code
1041
+
1042
+ # Validate other critical fields
1043
+ critical_fields = {
1044
+ 'AMOUNT': 'claim amount',
1045
+ 'TOS': 'type of service',
1046
+ 'POS': 'place of service',
1047
+ 'MINTUES': 'service minutes'
1048
+ }
1049
+
1050
+ for field, description in critical_fields.items():
1051
+ value = validated_data.get(field, '').strip()
1052
+ if not value or value.lower() in ['unknown', 'none', '']:
1053
+ print("\n{}".format("="*80))
1054
+ print("CRITICAL: Missing {} for patient {}".format(description, chart_number))
1055
+ print("{}".format("="*80))
1056
+ print("Field: {}".format(field))
1057
+ print("Patient: {}, {}".format(parsed_data.get('LAST', 'Unknown'), parsed_data.get('FIRST', 'Unknown')))
1058
+ print("Service Date: {}".format(parsed_data.get('DATE', 'Unknown')))
1059
+ print("\nPlease enter the {}:".format(description))
1060
+ new_value = input("> ").strip()
1061
+
1062
+ if not new_value:
1063
+ raise ValueError("Cannot proceed without {} for patient {}".format(description, chart_number))
1064
+
1065
+ validated_data[field] = new_value
1066
+ print("\n✓ Updated {} to '{}'".format(field, new_value))
1067
+
1068
+ print("\n✓ All claim data validated for patient {}".format(chart_number))
1069
+ return validated_data
1070
+
949
1071
 
950
1072
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250723.0
3
+ Version: 0.250723.2
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: medicafe
3
- Version: 0.250723.0
3
+ Version: 0.250723.2
4
4
  Summary: MediCafe
5
5
  Home-page: https://github.com/katanada2
6
6
  Author: Daniel Vidaud
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
2
2
 
3
3
  setup(
4
4
  name='medicafe',
5
- version="0.250723.0",
5
+ version="0.250723.2",
6
6
  description='MediCafe',
7
7
  long_description="""
8
8
  # Project Overview: MediCafe
File without changes
File without changes
File without changes
File without changes