medicafe 0.250728.8__py3-none-any.whl → 0.250805.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 +233 -19
- MediBot/MediBot.py +138 -46
- MediBot/MediBot_Crosswalk_Library.py +127 -623
- MediBot/MediBot_Crosswalk_Utils.py +618 -0
- MediBot/MediBot_Preprocessor.py +72 -17
- MediBot/MediBot_Preprocessor_lib.py +470 -76
- MediBot/MediBot_UI.py +32 -17
- MediBot/MediBot_dataformat_library.py +68 -20
- MediBot/MediBot_docx_decoder.py +120 -19
- MediBot/MediBot_smart_import.py +180 -0
- MediBot/__init__.py +89 -0
- MediBot/get_medicafe_version.py +25 -0
- MediBot/update_json.py +35 -6
- MediBot/update_medicafe.py +19 -1
- MediCafe/MediLink_ConfigLoader.py +160 -0
- MediCafe/__init__.py +171 -0
- MediCafe/__main__.py +222 -0
- MediCafe/api_core.py +1098 -0
- MediCafe/api_core_backup.py +427 -0
- MediCafe/api_factory.py +306 -0
- MediCafe/api_utils.py +356 -0
- MediCafe/core_utils.py +450 -0
- MediCafe/graphql_utils.py +445 -0
- MediCafe/logging_config.py +123 -0
- MediCafe/logging_demo.py +61 -0
- MediCafe/migration_helpers.py +463 -0
- MediCafe/smart_import.py +436 -0
- MediLink/MediLink.py +66 -26
- MediLink/MediLink_837p_cob_library.py +28 -28
- MediLink/MediLink_837p_encoder.py +33 -34
- MediLink/MediLink_837p_encoder_library.py +243 -151
- MediLink/MediLink_837p_utilities.py +129 -5
- MediLink/MediLink_API_Generator.py +83 -60
- MediLink/MediLink_API_v3.py +1 -1
- MediLink/MediLink_ClaimStatus.py +177 -31
- MediLink/MediLink_DataMgmt.py +405 -72
- MediLink/MediLink_Decoder.py +20 -1
- MediLink/MediLink_Deductible.py +155 -28
- MediLink/MediLink_Display_Utils.py +72 -0
- MediLink/MediLink_Down.py +127 -5
- MediLink/MediLink_Gmail.py +712 -653
- MediLink/MediLink_PatientProcessor.py +257 -0
- MediLink/MediLink_UI.py +85 -61
- MediLink/MediLink_Up.py +28 -4
- MediLink/MediLink_insurance_utils.py +227 -264
- MediLink/MediLink_main.py +248 -0
- MediLink/MediLink_smart_import.py +264 -0
- MediLink/__init__.py +93 -0
- MediLink/insurance_type_integration_test.py +66 -76
- MediLink/test.py +1 -1
- MediLink/test_timing.py +59 -0
- {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/METADATA +1 -1
- medicafe-0.250805.0.dist-info/RECORD +81 -0
- medicafe-0.250805.0.dist-info/entry_points.txt +2 -0
- {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/top_level.txt +1 -0
- medicafe-0.250728.8.dist-info/RECORD +0 -59
- {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/LICENSE +0 -0
- {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/WHEEL +0 -0
|
@@ -1,64 +1,101 @@
|
|
|
1
1
|
# MediLink_837p_encoder_library.py
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
import sys, os
|
|
4
|
-
|
|
4
|
+
# Set up paths and use core utilities
|
|
5
|
+
import os, sys
|
|
6
|
+
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
|
|
7
|
+
from MediCafe.core_utils import get_shared_config_loader
|
|
8
|
+
MediLink_ConfigLoader = get_shared_config_loader()
|
|
9
|
+
import re
|
|
5
10
|
|
|
6
11
|
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
7
12
|
if project_dir not in sys.path:
|
|
8
13
|
sys.path.append(project_dir)
|
|
9
14
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
15
|
+
# Import MediBot modules conditionally to avoid circular imports
|
|
16
|
+
MediBot_Preprocessor_lib = None
|
|
17
|
+
load_insurance_data_from_mains = None
|
|
18
|
+
|
|
19
|
+
try:
|
|
20
|
+
from MediBot import MediBot_Preprocessor_lib
|
|
21
|
+
if hasattr(MediBot_Preprocessor_lib, 'load_insurance_data_from_mains'):
|
|
22
|
+
load_insurance_data_from_mains = MediBot_Preprocessor_lib.load_insurance_data_from_mains
|
|
23
|
+
except ImportError:
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
try:
|
|
27
|
+
from MediBot import MediBot_Crosswalk_Library
|
|
28
|
+
except ImportError:
|
|
29
|
+
MediBot_Crosswalk_Library = None
|
|
13
30
|
|
|
14
31
|
# Safe import for API functions - works in multiple contexts
|
|
15
32
|
try:
|
|
16
|
-
from
|
|
33
|
+
from MediCafe import api_core as MediLink_API_v3 # [auto-migrated]
|
|
34
|
+
fetch_payer_name_from_api = getattr(MediLink_API_v3, 'fetch_payer_name_from_api', None)
|
|
35
|
+
except ImportError:
|
|
36
|
+
MediLink_API_v3 = None
|
|
37
|
+
fetch_payer_name_from_api = None
|
|
38
|
+
|
|
39
|
+
# Safe import for utility functions - works in multiple contexts
|
|
40
|
+
try:
|
|
41
|
+
import MediLink_837p_utilities
|
|
17
42
|
except (ImportError, SystemError):
|
|
18
43
|
try:
|
|
19
|
-
|
|
44
|
+
import MediLink_837p_utilities
|
|
20
45
|
except ImportError:
|
|
21
|
-
|
|
22
|
-
fetch_payer_name_from_api = MediLink_API_v3.fetch_payer_name_from_api
|
|
46
|
+
MediLink_837p_utilities = None
|
|
23
47
|
|
|
24
|
-
# Safe import for
|
|
48
|
+
# Safe import for UI functions - works in multiple contexts
|
|
25
49
|
try:
|
|
26
|
-
|
|
27
|
-
convert_date_format,
|
|
28
|
-
format_datetime,
|
|
29
|
-
get_user_confirmation,
|
|
30
|
-
prompt_user_for_payer_id,
|
|
31
|
-
format_claim_number,
|
|
32
|
-
generate_segment_counts,
|
|
33
|
-
handle_validation_errors,
|
|
34
|
-
get_output_directory,
|
|
35
|
-
winscp_validate_output_directory
|
|
36
|
-
)
|
|
50
|
+
import MediLink_UI
|
|
37
51
|
except (ImportError, SystemError):
|
|
38
52
|
try:
|
|
39
|
-
|
|
40
|
-
convert_date_format,
|
|
41
|
-
format_datetime,
|
|
42
|
-
get_user_confirmation,
|
|
43
|
-
prompt_user_for_payer_id,
|
|
44
|
-
format_claim_number,
|
|
45
|
-
generate_segment_counts,
|
|
46
|
-
handle_validation_errors,
|
|
47
|
-
get_output_directory,
|
|
48
|
-
winscp_validate_output_directory
|
|
49
|
-
)
|
|
53
|
+
import MediLink_UI
|
|
50
54
|
except ImportError:
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
55
|
+
MediLink_UI = None
|
|
56
|
+
|
|
57
|
+
# Import MediBot crosswalk utilities with fallback - using dynamic imports to avoid circular dependencies
|
|
58
|
+
try:
|
|
59
|
+
import MediBot_Crosswalk_Utils
|
|
60
|
+
update_crosswalk_with_corrected_payer_id = MediBot_Crosswalk_Utils.update_crosswalk_with_corrected_payer_id
|
|
61
|
+
update_crosswalk_with_new_payer_id = MediBot_Crosswalk_Utils.update_crosswalk_with_new_payer_id
|
|
62
|
+
except ImportError:
|
|
63
|
+
# Fallback to main library if utility import fails
|
|
64
|
+
try:
|
|
65
|
+
import MediBot_Crosswalk_Library
|
|
66
|
+
update_crosswalk_with_corrected_payer_id = MediBot_Crosswalk_Library.update_crosswalk_with_corrected_payer_id
|
|
67
|
+
update_crosswalk_with_new_payer_id = MediBot_Crosswalk_Library.update_crosswalk_with_new_payer_id
|
|
68
|
+
except (ImportError, AttributeError):
|
|
69
|
+
# If functions are not available, set to None for graceful handling
|
|
70
|
+
update_crosswalk_with_corrected_payer_id = None
|
|
71
|
+
update_crosswalk_with_new_payer_id = None
|
|
72
|
+
|
|
73
|
+
# Import enhanced insurance selection with fallback
|
|
74
|
+
try:
|
|
75
|
+
from MediLink_insurance_utils import safe_insurance_type_selection
|
|
76
|
+
except ImportError:
|
|
77
|
+
safe_insurance_type_selection = None
|
|
61
78
|
|
|
79
|
+
# Import display utilities
|
|
80
|
+
try:
|
|
81
|
+
import MediLink_Display_Utils
|
|
82
|
+
except ImportError:
|
|
83
|
+
MediLink_Display_Utils = None
|
|
84
|
+
|
|
85
|
+
# Re-export commonly used functions for backward compatibility
|
|
86
|
+
if MediLink_837p_utilities:
|
|
87
|
+
get_output_directory = MediLink_837p_utilities.get_output_directory
|
|
88
|
+
format_datetime = MediLink_837p_utilities.format_datetime
|
|
89
|
+
get_user_confirmation = MediLink_837p_utilities.get_user_confirmation
|
|
90
|
+
prompt_user_for_payer_id = MediLink_837p_utilities.prompt_user_for_payer_id
|
|
91
|
+
convert_date_format = MediLink_837p_utilities.convert_date_format
|
|
92
|
+
format_claim_number = MediLink_837p_utilities.format_claim_number
|
|
93
|
+
generate_segment_counts = MediLink_837p_utilities.generate_segment_counts
|
|
94
|
+
handle_validation_errors = MediLink_837p_utilities.handle_validation_errors
|
|
95
|
+
winscp_validate_output_directory = MediLink_837p_utilities.winscp_validate_output_directory
|
|
96
|
+
find_closest_insurance_matches = MediLink_837p_utilities.find_closest_insurance_matches
|
|
97
|
+
prompt_for_insurance_selection = MediLink_837p_utilities.prompt_for_insurance_selection
|
|
98
|
+
build_nm1_segment = MediLink_837p_utilities.build_nm1_segment
|
|
62
99
|
|
|
63
100
|
|
|
64
101
|
# Constructs the ST segment for transaction set.
|
|
@@ -174,7 +211,7 @@ def create_1000A_submitter_name_segment(patient_data, config, endpoint):
|
|
|
174
211
|
|
|
175
212
|
# Check if payer_id is Florida Blue (00590 or BCBSF) and assign submitter_id accordingly
|
|
176
213
|
if payer_id in ['00590', 'BCBSF']:
|
|
177
|
-
submitter_id = endpoint_config.get('
|
|
214
|
+
submitter_id = endpoint_config.get('nm_109_bcbs', 'DEFAULT BCBSF ID')
|
|
178
215
|
else:
|
|
179
216
|
submitter_id = endpoint_config.get('nm_109_value', 'DEFAULT ID') # Default ID if not specified in endpoint
|
|
180
217
|
|
|
@@ -329,7 +366,9 @@ def resolve_payer_name(payer_id, config, primary_endpoint, insurance_name, parse
|
|
|
329
366
|
exit(1)
|
|
330
367
|
|
|
331
368
|
# Step 9: Update the crosswalk with the corrected Payer ID
|
|
332
|
-
|
|
369
|
+
# Note: update_crosswalk_with_corrected_payer_id is imported at module level
|
|
370
|
+
|
|
371
|
+
if update_crosswalk_with_corrected_payer_id(client, payer_id, corrected_payer_id, config, crosswalk):
|
|
333
372
|
return resolved_name
|
|
334
373
|
else:
|
|
335
374
|
print("Failed to update crosswalk with the corrected Payer ID.")
|
|
@@ -358,8 +397,8 @@ def resolve_payer_name(payer_id, config, primary_endpoint, insurance_name, parse
|
|
|
358
397
|
|
|
359
398
|
# Placeholder for future implementation:
|
|
360
399
|
# corrected_name = input("Please enter the correct insurance name: ")
|
|
361
|
-
# MediLink_ConfigLoader.log(
|
|
362
|
-
# print(
|
|
400
|
+
# MediLink_ConfigLoader.log("User provided corrected insurance name: {}".format(corrected_name), config, level="INFO")
|
|
401
|
+
# print("Using manually entered insurance name: {}".format(corrected_name))
|
|
363
402
|
# TODO: Implement update_crosswalk_with_corrected_payer_name(corrected_name, payer_id, config, crosswalk)
|
|
364
403
|
# return corrected_name
|
|
365
404
|
|
|
@@ -416,9 +455,10 @@ def handle_missing_payer_id(insurance_name, config, crosswalk, client):
|
|
|
416
455
|
confirmation_prompt = "Is the standard insurance name '{}' correct? (yes/no): ".format(standard_insurance_name)
|
|
417
456
|
if get_user_confirmation(confirmation_prompt):
|
|
418
457
|
# Step 7: Update the crosswalk with the new payer ID and insurance name mapping
|
|
458
|
+
# Note: update_crosswalk_with_new_payer_id is imported at module level
|
|
419
459
|
try:
|
|
420
460
|
MediLink_ConfigLoader.log("Updating crosswalk with payer ID: {} for insurance name: {}".format(payer_id, insurance_name), config, level="DEBUG")
|
|
421
|
-
|
|
461
|
+
update_crosswalk_with_new_payer_id(client, insurance_name, payer_id, config, crosswalk)
|
|
422
462
|
return payer_id # Return the payer_id after successful update
|
|
423
463
|
except Exception as e:
|
|
424
464
|
# Enhanced error message to include exception type and context
|
|
@@ -436,9 +476,7 @@ def handle_missing_payer_id(insurance_name, config, crosswalk, client):
|
|
|
436
476
|
|
|
437
477
|
|
|
438
478
|
|
|
439
|
-
|
|
440
|
-
# Step 1: Build NM1 segment using payer name and ID
|
|
441
|
-
return "NM1*PR*2*{}*****PI*{}~".format(payer_name, payer_id)
|
|
479
|
+
|
|
442
480
|
|
|
443
481
|
def map_insurance_name_to_payer_id(insurance_name, config, client, crosswalk):
|
|
444
482
|
"""
|
|
@@ -466,10 +504,58 @@ def map_insurance_name_to_payer_id(insurance_name, config, client, crosswalk):
|
|
|
466
504
|
# Get medisoft ID corresponding to the insurance name
|
|
467
505
|
medisoft_id = insurance_to_id.get(insurance_name)
|
|
468
506
|
if medisoft_id is None:
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
507
|
+
# Find closest matches instead of immediately failing
|
|
508
|
+
closest_matches = find_closest_insurance_matches(insurance_name, insurance_to_id)
|
|
509
|
+
|
|
510
|
+
if closest_matches:
|
|
511
|
+
# Prompt user to select from closest matches
|
|
512
|
+
selected_insurance_name = prompt_for_insurance_selection(insurance_name, closest_matches, config)
|
|
513
|
+
|
|
514
|
+
if selected_insurance_name:
|
|
515
|
+
# Use the selected insurance name
|
|
516
|
+
medisoft_id = insurance_to_id.get(selected_insurance_name)
|
|
517
|
+
MediLink_ConfigLoader.log("Using selected insurance name '{}' for original '{}'".format(selected_insurance_name, insurance_name), config, level="INFO")
|
|
518
|
+
else:
|
|
519
|
+
# User chose manual intervention
|
|
520
|
+
error_message = "CLAIM SUBMISSION CANCELLED: Insurance name '{}' not found in MAINS and user chose manual intervention.".format(insurance_name)
|
|
521
|
+
MediLink_ConfigLoader.log(error_message, config, level="WARNING")
|
|
522
|
+
print("\n" + "="*80)
|
|
523
|
+
print("CLAIM SUBMISSION CANCELLED: MANUAL INTERVENTION REQUIRED")
|
|
524
|
+
print("="*80)
|
|
525
|
+
print("\nThe system cannot automatically process this claim because:")
|
|
526
|
+
print("- Insurance name '{}' was not found in the MAINS database".format(insurance_name))
|
|
527
|
+
print("- Without a valid insurance mapping, the claim cannot be submitted")
|
|
528
|
+
print("\nTo proceed with this claim, you need to:")
|
|
529
|
+
print("1. Verify the correct insurance company name")
|
|
530
|
+
print("2. Ensure the insurance company is in your Medisoft system")
|
|
531
|
+
print("3. Restart the claim submission process once the insurance is properly configured")
|
|
532
|
+
print("="*80)
|
|
533
|
+
raise ValueError(error_message)
|
|
534
|
+
else:
|
|
535
|
+
# No matches found in MAINS
|
|
536
|
+
error_message = "CLAIM SUBMISSION FAILED: Cannot find Medisoft ID for insurance name: '{}'. No similar matches found in MAINS database.".format(insurance_name)
|
|
537
|
+
MediLink_ConfigLoader.log(error_message, config, level="ERROR")
|
|
538
|
+
print("\n" + "="*80)
|
|
539
|
+
print("CLAIM SUBMISSION ERROR: INSURANCE MAPPING FAILED")
|
|
540
|
+
print("="*80)
|
|
541
|
+
print("\nThe system cannot process this claim because:")
|
|
542
|
+
print("- Insurance name '{}' was not found in the MAINS database".format(insurance_name))
|
|
543
|
+
print("- No similar insurance names were found for manual selection")
|
|
544
|
+
print("- Without a Medisoft ID, the system cannot:")
|
|
545
|
+
print(" - Convert the insurance name to a payer ID")
|
|
546
|
+
print(" - Generate the required 837p claim format")
|
|
547
|
+
print(" - Submit the claim to the insurance company")
|
|
548
|
+
print("\nThis typically happens when:")
|
|
549
|
+
print("- The insurance company name is misspelled or abbreviated")
|
|
550
|
+
print("- The insurance company is not in Medisoft")
|
|
551
|
+
print("- The MAINS database is missing or incomplete")
|
|
552
|
+
print("\nTO FIX THIS:")
|
|
553
|
+
print("1. Check the spelling of the insurance company name")
|
|
554
|
+
print("2. Verify the insurance company exists in Medisoft")
|
|
555
|
+
print("3. If the name is correct, llamar a Dani")
|
|
556
|
+
print("4. The insurance company may need to be added")
|
|
557
|
+
print("="*80)
|
|
558
|
+
raise ValueError(error_message)
|
|
473
559
|
|
|
474
560
|
# Convert medisoft_id to string to match the JSON data type
|
|
475
561
|
medisoft_id_str = str(medisoft_id)
|
|
@@ -604,11 +690,14 @@ def insurance_type_selection(parsed_data):
|
|
|
604
690
|
MediLink_ConfigLoader.log("insurance_type_selection(parsed_data): {}".format(parsed_data), level="DEBUG")
|
|
605
691
|
|
|
606
692
|
# Try enhanced selection with safe fallback
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
693
|
+
if safe_insurance_type_selection:
|
|
694
|
+
try:
|
|
695
|
+
return safe_insurance_type_selection(parsed_data, _original_insurance_type_selection_logic)
|
|
696
|
+
except Exception as e:
|
|
697
|
+
MediLink_ConfigLoader.log("Error in enhanced insurance selection: {}. Using original logic".format(str(e)), level="ERROR")
|
|
698
|
+
return _original_insurance_type_selection_logic(parsed_data)
|
|
699
|
+
else:
|
|
700
|
+
MediLink_ConfigLoader.log("Enhanced insurance selection not available. Using original logic", level="INFO")
|
|
612
701
|
return _original_insurance_type_selection_logic(parsed_data)
|
|
613
702
|
|
|
614
703
|
def _original_insurance_type_selection_logic(parsed_data):
|
|
@@ -641,8 +730,8 @@ def _original_insurance_type_selection_logic(parsed_data):
|
|
|
641
730
|
display_full_list = input("Do you want to see the full list of insurance options? (yes/no): ").strip().lower()
|
|
642
731
|
|
|
643
732
|
# Display full list if user confirms
|
|
644
|
-
if display_full_list in ['yes', 'y']:
|
|
645
|
-
|
|
733
|
+
if display_full_list in ['yes', 'y'] and MediLink_Display_Utils:
|
|
734
|
+
MediLink_Display_Utils.display_insurance_options(insurance_options)
|
|
646
735
|
|
|
647
736
|
# Horrible menu
|
|
648
737
|
prompt_display_insurance_options()
|
|
@@ -653,11 +742,24 @@ def _original_insurance_type_selection_logic(parsed_data):
|
|
|
653
742
|
# User input for insurance type
|
|
654
743
|
user_input = input("Enter the 2-character code for the insurance type (or press Enter to default to '12' for PPO): ").strip().upper()
|
|
655
744
|
|
|
656
|
-
#
|
|
657
|
-
if user_input
|
|
658
|
-
|
|
745
|
+
# Input validation and assignment
|
|
746
|
+
if user_input:
|
|
747
|
+
# Basic format validation
|
|
748
|
+
if len(user_input) > 3 or not user_input.isalnum():
|
|
749
|
+
print("Invalid format: Insurance codes should be 1-3 alphanumeric characters. Defaulting to PPO.")
|
|
750
|
+
elif user_input in insurance_options:
|
|
751
|
+
insurance_type_code = user_input
|
|
752
|
+
print("Selected: {} - {}".format(user_input, insurance_options[user_input]))
|
|
753
|
+
else:
|
|
754
|
+
# User wants to use a code not in config - confirm with them
|
|
755
|
+
confirm = input("Code '{}' not found in configuration. Use it anyway? (y/n): ".format(user_input)).strip().lower()
|
|
756
|
+
if confirm in ['y', 'yes']:
|
|
757
|
+
insurance_type_code = user_input
|
|
758
|
+
print("Using code: {}".format(user_input))
|
|
759
|
+
else:
|
|
760
|
+
print("Defaulting to PPO (Preferred Provider Organization)")
|
|
659
761
|
else:
|
|
660
|
-
print("
|
|
762
|
+
print("Using default: PPO (Preferred Provider Organization)")
|
|
661
763
|
|
|
662
764
|
return insurance_type_code
|
|
663
765
|
|
|
@@ -876,8 +978,8 @@ def create_interchange_elements(config, endpoint, transaction_set_control_number
|
|
|
876
978
|
isa13 = endpoint_config.get('isa_13_value', '000000001')
|
|
877
979
|
|
|
878
980
|
# Create interchange header and trailer using provided library functions.
|
|
879
|
-
isa_header, gs_header = create_interchange_header(config, endpoint, isa13)
|
|
880
|
-
ge_trailer, iea_trailer = create_interchange_trailer(config, transaction_set_control_number, isa13)
|
|
981
|
+
isa_header, gs_header, gs06 = create_interchange_header(config, endpoint, isa13)
|
|
982
|
+
ge_trailer, iea_trailer = create_interchange_trailer(config, transaction_set_control_number, isa13, gs06)
|
|
881
983
|
|
|
882
984
|
return isa_header, gs_header, ge_trailer, iea_trailer
|
|
883
985
|
|
|
@@ -893,7 +995,7 @@ def create_interchange_header(config, endpoint, isa13):
|
|
|
893
995
|
- isa13: The ISA13 field value representing the current system time.
|
|
894
996
|
|
|
895
997
|
Returns:
|
|
896
|
-
- Tuple containing the ISA
|
|
998
|
+
- Tuple containing the ISA segment, GS segment, and group control number (gs06).
|
|
897
999
|
"""
|
|
898
1000
|
endpoint_config = config['endpoints'].get(endpoint.upper(), {})
|
|
899
1001
|
|
|
@@ -919,19 +1021,22 @@ def create_interchange_header(config, endpoint, isa13):
|
|
|
919
1021
|
# GS Segment
|
|
920
1022
|
# GS04 YYYYMMDD
|
|
921
1023
|
# GS06 Group Control Number, Field Length 1/9, must match GE02
|
|
922
|
-
#
|
|
923
|
-
|
|
1024
|
+
# FIXED: Generate group control number using similar logic to ISA13 for consistency
|
|
1025
|
+
current_time = datetime.now().strftime('%H%M%S')
|
|
1026
|
+
gs06 = current_time[-6:] # Use last 6 digits of time for uniqueness within reasonable bounds
|
|
1027
|
+
if not gs06 or len(gs06) < 1:
|
|
1028
|
+
gs06 = '1' # Fallback to simple increment
|
|
924
1029
|
|
|
925
1030
|
gs_segment = "GS*HC*{}*{}*{}*{}*{}*X*005010X222A1~".format(
|
|
926
1031
|
gs_sender_code, gs_receiver_code, format_datetime(), format_datetime(format_type='time'), gs06
|
|
927
1032
|
)
|
|
928
1033
|
|
|
929
|
-
MediLink_ConfigLoader.log("Created interchange header for endpoint: {}".format(endpoint), config, level="INFO")
|
|
1034
|
+
MediLink_ConfigLoader.log("Created interchange header for endpoint: {} with group control number: {}".format(endpoint, gs06), config, level="INFO")
|
|
930
1035
|
|
|
931
|
-
return isa_segment, gs_segment
|
|
1036
|
+
return isa_segment, gs_segment, gs06
|
|
932
1037
|
|
|
933
1038
|
# Generates the GE and IEA segments for the interchange trailer based on the number of transactions and functional groups.
|
|
934
|
-
def create_interchange_trailer(config, num_transactions, isa13, num_functional_groups=1):
|
|
1039
|
+
def create_interchange_trailer(config, num_transactions, isa13, gs06, num_functional_groups=1):
|
|
935
1040
|
"""
|
|
936
1041
|
Generate GE and IEA segments for the interchange trailer.
|
|
937
1042
|
|
|
@@ -939,6 +1044,7 @@ def create_interchange_trailer(config, num_transactions, isa13, num_functional_g
|
|
|
939
1044
|
- config: Configuration dictionary with settings and identifiers.
|
|
940
1045
|
- num_transactions: The number of transactions within the functional group.
|
|
941
1046
|
- isa13: The ISA13 field value representing the current system time.
|
|
1047
|
+
- gs06: The group control number from GS segment (must match GE02).
|
|
942
1048
|
- num_functional_groups: The number of functional groups within the interchange. Default is 1.
|
|
943
1049
|
|
|
944
1050
|
Returns:
|
|
@@ -949,14 +1055,13 @@ def create_interchange_trailer(config, num_transactions, isa13, num_functional_g
|
|
|
949
1055
|
# Indicates the end of a functional group and provides the count of the number of transactions within it.
|
|
950
1056
|
|
|
951
1057
|
# GE02 Group Control Number, Field Length 1/9, must match GS06 (Header)
|
|
952
|
-
#
|
|
953
|
-
|
|
954
|
-
ge_segment = "GE*{}*{}~".format(num_transactions, ge02)
|
|
1058
|
+
# FIXED: Use the gs06 parameter passed from the header generation instead of hardcoded placeholder
|
|
1059
|
+
ge_segment = "GE*{}*{}~".format(num_transactions, gs06)
|
|
955
1060
|
|
|
956
1061
|
# IEA Segment: Interchange Control Trailer (Note: IEA02 needs to equal ISA13)
|
|
957
1062
|
iea_segment = "IEA*{}*{}~".format(num_functional_groups, isa13)
|
|
958
1063
|
|
|
959
|
-
MediLink_ConfigLoader.log("Created interchange trailer", config, level="INFO")
|
|
1064
|
+
MediLink_ConfigLoader.log("Created interchange trailer with matching group control number: {}".format(gs06), config, level="INFO")
|
|
960
1065
|
|
|
961
1066
|
return ge_segment, iea_segment
|
|
962
1067
|
|
|
@@ -1013,78 +1118,65 @@ def validate_claim_data_for_837p(parsed_data, config, crosswalk):
|
|
|
1013
1118
|
MediLink_ConfigLoader.log("Updated crosswalk with new diagnosis code: {}, for Medisoft code {}".format(diagnosis_code, medisoft_code), config, level="INFO")
|
|
1014
1119
|
print("\n[SUCCESS] Added '{}' -> '{}' to crosswalk".format(diagnosis_code, medisoft_code))
|
|
1015
1120
|
print(" IMPORTANT: You must manually save this to crosswalk.json to persist the change!")
|
|
1016
|
-
# TODO
|
|
1017
|
-
#
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
#
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
#
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
#
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
#
|
|
1054
|
-
#
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
new_value = input("> ").strip()
|
|
1079
|
-
|
|
1080
|
-
if not new_value:
|
|
1081
|
-
raise ValueError("Cannot proceed without {} for patient {}".format(description, chart_number))
|
|
1082
|
-
|
|
1083
|
-
validated_data[field] = new_value
|
|
1084
|
-
print("\n[SUCCESS] Updated {} to '{}'".format(field, new_value))
|
|
1085
|
-
|
|
1086
|
-
print("\n[SUCCESS] All claim data validated for patient {}".format(chart_number))
|
|
1087
|
-
return validated_data
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1121
|
+
# TODO (HIGH PRIORITY - Crosswalk Data Persistence and Validation):
|
|
1122
|
+
# PROBLEM: Diagnosis codes are added to crosswalk in memory but not persisted to file.
|
|
1123
|
+
# Changes are lost when application restarts, requiring manual intervention.
|
|
1124
|
+
# Additionally, validation should happen upstream rather than at encoding time.
|
|
1125
|
+
#
|
|
1126
|
+
# CURRENT WORKFLOW ISSUES:
|
|
1127
|
+
# 1. In-memory crosswalk changes are not saved to crosswalk.json automatically
|
|
1128
|
+
# 2. User must manually save changes (error-prone, often forgotten)
|
|
1129
|
+
# 3. Validation happens during encoding (too late in the process)
|
|
1130
|
+
# 4. No backup or rollback mechanism for crosswalk changes
|
|
1131
|
+
# 5. No validation of new mappings before they're added
|
|
1132
|
+
#
|
|
1133
|
+
# IMPLEMENTATION PLAN:
|
|
1134
|
+
#
|
|
1135
|
+
# Phase 1: Automatic Persistence
|
|
1136
|
+
# 1. Identify or create dedicated crosswalk update function:
|
|
1137
|
+
# - Look for existing save_crosswalk_to_file() or similar
|
|
1138
|
+
# - If doesn't exist, create update_crosswalk_mapping(section, key, value, config)
|
|
1139
|
+
# 2. Replace manual save requirement with automatic persistence:
|
|
1140
|
+
# - Call crosswalk persistence function immediately after in-memory update
|
|
1141
|
+
# - Add file locking to prevent concurrent modification conflicts
|
|
1142
|
+
# - Create backup before modification for rollback capability
|
|
1143
|
+
#
|
|
1144
|
+
# Phase 2: Upstream Validation (RECOMMENDED)
|
|
1145
|
+
# 1. Move validation to data preprocessing stage:
|
|
1146
|
+
# - Add validate_diagnosis_codes_in_csv() function in preprocessing
|
|
1147
|
+
# - Check all diagnosis codes against crosswalk before encoding starts
|
|
1148
|
+
# - Batch collect all missing codes and prompt user once
|
|
1149
|
+
# 2. Create interactive crosswalk update session:
|
|
1150
|
+
# - Present all missing codes to user in a single session
|
|
1151
|
+
# - Allow bulk updates with confirmation
|
|
1152
|
+
# - Save all changes at once rather than piecemeal
|
|
1153
|
+
#
|
|
1154
|
+
# Phase 3: Enhanced Validation (FUTURE)
|
|
1155
|
+
# 1. Add ICD-10 code validation against official code sets
|
|
1156
|
+
# 2. Suggest similar codes when exact matches aren't found
|
|
1157
|
+
# 3. Add crosswalk versioning and change tracking
|
|
1158
|
+
# 4. Implement crosswalk sharing across multiple users/systems
|
|
1159
|
+
#
|
|
1160
|
+
# IMMEDIATE IMPLEMENTATION:
|
|
1161
|
+
# 1. Find existing crosswalk persistence function or create one
|
|
1162
|
+
# 2. Add call here: save_crosswalk_to_file(crosswalk, config)
|
|
1163
|
+
# 3. Remove manual save message
|
|
1164
|
+
# 4. Add error handling for file save failures
|
|
1165
|
+
# 5. Add logging for successful crosswalk updates
|
|
1166
|
+
#
|
|
1167
|
+
# FUNCTIONS TO LOOK FOR:
|
|
1168
|
+
# - save_crosswalk_to_file()
|
|
1169
|
+
# - update_crosswalk_json()
|
|
1170
|
+
# - persist_crosswalk_changes()
|
|
1171
|
+
# - write_crosswalk_config()
|
|
1172
|
+
#
|
|
1173
|
+
# FILES TO CHECK:
|
|
1174
|
+
# - MediLink_main.py (likely location for config management)
|
|
1175
|
+
# - MediCafe/MediLink_ConfigLoader.py (configuration management)
|
|
1176
|
+
# - Current file (encoder_library.py) for existing patterns
|
|
1177
|
+
#
|
|
1178
|
+
# TESTING REQUIREMENTS:
|
|
1179
|
+
# - Verify crosswalk changes persist across application restarts
|
|
1180
|
+
# - Test concurrent access scenarios
|
|
1181
|
+
# - Verify backup/restore functionality
|
|
1182
|
+
# - Test with invalid JSON syntax in crosswalk file
|