medicafe 0.250728.9__py3-none-any.whl → 0.250805.2__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 +314 -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_837p_cob_library.py +28 -28
- MediLink/MediLink_837p_encoder.py +33 -34
- MediLink/MediLink_837p_encoder_library.py +226 -150
- 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 +378 -63
- 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 +720 -653
- MediLink/MediLink_PatientProcessor.py +257 -0
- MediLink/MediLink_UI.py +85 -71
- MediLink/MediLink_Up.py +28 -4
- MediLink/MediLink_insurance_utils.py +227 -230
- MediLink/MediLink_main.py +248 -0
- MediLink/MediLink_smart_import.py +264 -0
- MediLink/__init__.py +93 -1
- MediLink/insurance_type_integration_test.py +13 -3
- MediLink/test.py +1 -1
- MediLink/test_timing.py +59 -0
- {medicafe-0.250728.9.dist-info → medicafe-0.250805.2.dist-info}/METADATA +1 -1
- medicafe-0.250805.2.dist-info/RECORD +81 -0
- medicafe-0.250805.2.dist-info/entry_points.txt +2 -0
- {medicafe-0.250728.9.dist-info → medicafe-0.250805.2.dist-info}/top_level.txt +1 -0
- medicafe-0.250728.9.dist-info/RECORD +0 -59
- {medicafe-0.250728.9.dist-info → medicafe-0.250805.2.dist-info}/LICENSE +0 -0
- {medicafe-0.250728.9.dist-info → medicafe-0.250805.2.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
|
-
convert_date_format = MediLink_837p_utilities.convert_date_format
|
|
53
|
-
format_datetime = MediLink_837p_utilities.format_datetime
|
|
54
|
-
get_user_confirmation = MediLink_837p_utilities.get_user_confirmation
|
|
55
|
-
prompt_user_for_payer_id = MediLink_837p_utilities.prompt_user_for_payer_id
|
|
56
|
-
format_claim_number = MediLink_837p_utilities.format_claim_number
|
|
57
|
-
generate_segment_counts = MediLink_837p_utilities.generate_segment_counts
|
|
58
|
-
handle_validation_errors = MediLink_837p_utilities.handle_validation_errors
|
|
59
|
-
get_output_directory = MediLink_837p_utilities.get_output_directory
|
|
60
|
-
winscp_validate_output_directory = MediLink_837p_utilities.winscp_validate_output_directory
|
|
55
|
+
MediLink_UI = None
|
|
61
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
|
|
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,14 +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
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
MediLink_ConfigLoader.log("
|
|
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")
|
|
615
701
|
return _original_insurance_type_selection_logic(parsed_data)
|
|
616
702
|
|
|
617
703
|
def _original_insurance_type_selection_logic(parsed_data):
|
|
@@ -644,8 +730,8 @@ def _original_insurance_type_selection_logic(parsed_data):
|
|
|
644
730
|
display_full_list = input("Do you want to see the full list of insurance options? (yes/no): ").strip().lower()
|
|
645
731
|
|
|
646
732
|
# Display full list if user confirms
|
|
647
|
-
if display_full_list in ['yes', 'y']:
|
|
648
|
-
|
|
733
|
+
if display_full_list in ['yes', 'y'] and MediLink_Display_Utils:
|
|
734
|
+
MediLink_Display_Utils.display_insurance_options(insurance_options)
|
|
649
735
|
|
|
650
736
|
# Horrible menu
|
|
651
737
|
prompt_display_insurance_options()
|
|
@@ -892,8 +978,8 @@ def create_interchange_elements(config, endpoint, transaction_set_control_number
|
|
|
892
978
|
isa13 = endpoint_config.get('isa_13_value', '000000001')
|
|
893
979
|
|
|
894
980
|
# Create interchange header and trailer using provided library functions.
|
|
895
|
-
isa_header, gs_header = create_interchange_header(config, endpoint, isa13)
|
|
896
|
-
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)
|
|
897
983
|
|
|
898
984
|
return isa_header, gs_header, ge_trailer, iea_trailer
|
|
899
985
|
|
|
@@ -909,7 +995,7 @@ def create_interchange_header(config, endpoint, isa13):
|
|
|
909
995
|
- isa13: The ISA13 field value representing the current system time.
|
|
910
996
|
|
|
911
997
|
Returns:
|
|
912
|
-
- Tuple containing the ISA
|
|
998
|
+
- Tuple containing the ISA segment, GS segment, and group control number (gs06).
|
|
913
999
|
"""
|
|
914
1000
|
endpoint_config = config['endpoints'].get(endpoint.upper(), {})
|
|
915
1001
|
|
|
@@ -935,19 +1021,22 @@ def create_interchange_header(config, endpoint, isa13):
|
|
|
935
1021
|
# GS Segment
|
|
936
1022
|
# GS04 YYYYMMDD
|
|
937
1023
|
# GS06 Group Control Number, Field Length 1/9, must match GE02
|
|
938
|
-
#
|
|
939
|
-
|
|
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
|
|
940
1029
|
|
|
941
1030
|
gs_segment = "GS*HC*{}*{}*{}*{}*{}*X*005010X222A1~".format(
|
|
942
1031
|
gs_sender_code, gs_receiver_code, format_datetime(), format_datetime(format_type='time'), gs06
|
|
943
1032
|
)
|
|
944
1033
|
|
|
945
|
-
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")
|
|
946
1035
|
|
|
947
|
-
return isa_segment, gs_segment
|
|
1036
|
+
return isa_segment, gs_segment, gs06
|
|
948
1037
|
|
|
949
1038
|
# Generates the GE and IEA segments for the interchange trailer based on the number of transactions and functional groups.
|
|
950
|
-
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):
|
|
951
1040
|
"""
|
|
952
1041
|
Generate GE and IEA segments for the interchange trailer.
|
|
953
1042
|
|
|
@@ -955,6 +1044,7 @@ def create_interchange_trailer(config, num_transactions, isa13, num_functional_g
|
|
|
955
1044
|
- config: Configuration dictionary with settings and identifiers.
|
|
956
1045
|
- num_transactions: The number of transactions within the functional group.
|
|
957
1046
|
- isa13: The ISA13 field value representing the current system time.
|
|
1047
|
+
- gs06: The group control number from GS segment (must match GE02).
|
|
958
1048
|
- num_functional_groups: The number of functional groups within the interchange. Default is 1.
|
|
959
1049
|
|
|
960
1050
|
Returns:
|
|
@@ -965,14 +1055,13 @@ def create_interchange_trailer(config, num_transactions, isa13, num_functional_g
|
|
|
965
1055
|
# Indicates the end of a functional group and provides the count of the number of transactions within it.
|
|
966
1056
|
|
|
967
1057
|
# GE02 Group Control Number, Field Length 1/9, must match GS06 (Header)
|
|
968
|
-
#
|
|
969
|
-
|
|
970
|
-
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)
|
|
971
1060
|
|
|
972
1061
|
# IEA Segment: Interchange Control Trailer (Note: IEA02 needs to equal ISA13)
|
|
973
1062
|
iea_segment = "IEA*{}*{}~".format(num_functional_groups, isa13)
|
|
974
1063
|
|
|
975
|
-
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")
|
|
976
1065
|
|
|
977
1066
|
return ge_segment, iea_segment
|
|
978
1067
|
|
|
@@ -1029,78 +1118,65 @@ def validate_claim_data_for_837p(parsed_data, config, crosswalk):
|
|
|
1029
1118
|
MediLink_ConfigLoader.log("Updated crosswalk with new diagnosis code: {}, for Medisoft code {}".format(diagnosis_code, medisoft_code), config, level="INFO")
|
|
1030
1119
|
print("\n[SUCCESS] Added '{}' -> '{}' to crosswalk".format(diagnosis_code, medisoft_code))
|
|
1031
1120
|
print(" IMPORTANT: You must manually save this to crosswalk.json to persist the change!")
|
|
1032
|
-
# TODO
|
|
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
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
new_value = input("> ").strip()
|
|
1095
|
-
|
|
1096
|
-
if not new_value:
|
|
1097
|
-
raise ValueError("Cannot proceed without {} for patient {}".format(description, chart_number))
|
|
1098
|
-
|
|
1099
|
-
validated_data[field] = new_value
|
|
1100
|
-
print("\n[SUCCESS] Updated {} to '{}'".format(field, new_value))
|
|
1101
|
-
|
|
1102
|
-
print("\n[SUCCESS] All claim data validated for patient {}".format(chart_number))
|
|
1103
|
-
return validated_data
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
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
|