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
MediBot/MediBot_UI.py
CHANGED
|
@@ -2,21 +2,38 @@
|
|
|
2
2
|
import ctypes, time, re, os, sys
|
|
3
3
|
from ctypes import wintypes
|
|
4
4
|
from sys import exit
|
|
5
|
-
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
6
|
-
if project_dir not in sys.path:
|
|
7
|
-
sys.path.append(project_dir)
|
|
8
5
|
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
# Set up paths using core utilities
|
|
7
|
+
|
|
8
|
+
from MediCafe.core_utils import get_config_loader_with_fallback
|
|
9
|
+
MediLink_ConfigLoader = get_config_loader_with_fallback()
|
|
10
|
+
|
|
11
|
+
# Ensure MediLink_ConfigLoader is available
|
|
12
|
+
if MediLink_ConfigLoader is None:
|
|
13
|
+
print("Warning: MediLink_ConfigLoader not available. Some functionality may be limited.")
|
|
14
|
+
# Create a minimal fallback logger
|
|
15
|
+
class FallbackLogger:
|
|
16
|
+
def log(self, message, level="INFO"):
|
|
17
|
+
print("[{}] {}".format(level, message))
|
|
18
|
+
MediLink_ConfigLoader = FallbackLogger()
|
|
19
|
+
|
|
20
|
+
# Import current_patient_context with fallback
|
|
21
|
+
try:
|
|
22
|
+
from MediBot import current_patient_context
|
|
11
23
|
except ImportError:
|
|
12
|
-
|
|
24
|
+
current_patient_context = None
|
|
13
25
|
|
|
14
|
-
#
|
|
15
|
-
|
|
26
|
+
# Set up lazy configuration loading using core utilities
|
|
27
|
+
from MediCafe.core_utils import create_config_cache
|
|
28
|
+
_get_config, (_config_cache, _crosswalk_cache) = create_config_cache()
|
|
16
29
|
|
|
17
30
|
# Function to check if a specific key is pressed
|
|
18
|
-
|
|
19
|
-
|
|
31
|
+
def _get_vk_codes():
|
|
32
|
+
"""Get VK codes from config."""
|
|
33
|
+
config, _ = _get_config()
|
|
34
|
+
VK_END = int(config.get('VK_END', "23"), 16) # Default to 23 if not in config
|
|
35
|
+
VK_PAUSE = int(config.get('VK_PAUSE', "24"), 16) # Default to 24 if not in config
|
|
36
|
+
return VK_END, VK_PAUSE
|
|
20
37
|
|
|
21
38
|
|
|
22
39
|
|
|
@@ -49,8 +66,8 @@ class AppControl:
|
|
|
49
66
|
self.medisoft_shortcut = path
|
|
50
67
|
|
|
51
68
|
def load_paths_from_config(self, medicare=False):
|
|
52
|
-
#
|
|
53
|
-
|
|
69
|
+
# Load configuration when needed
|
|
70
|
+
config, _ = _get_config()
|
|
54
71
|
|
|
55
72
|
# PERFORMANCE FIX: Cache configuration lookups to reduce Medicare vs Private overhead
|
|
56
73
|
cache_key = 'medicare' if medicare else 'private'
|
|
@@ -86,6 +103,7 @@ def is_key_pressed(key_code):
|
|
|
86
103
|
|
|
87
104
|
def manage_script_pause(csv_data, error_message, reverse_mapping):
|
|
88
105
|
user_action = 0 # initialize as 'continue'
|
|
106
|
+
VK_END, VK_PAUSE = _get_vk_codes()
|
|
89
107
|
|
|
90
108
|
if not app_control.get_pause_status() and is_key_pressed(VK_PAUSE):
|
|
91
109
|
app_control.set_pause_status(True)
|
|
@@ -200,11 +218,6 @@ def display_menu_header(title):
|
|
|
200
218
|
print("-" * 60)
|
|
201
219
|
|
|
202
220
|
def handle_user_interaction(interaction_mode, error_message):
|
|
203
|
-
# Import here to avoid circular imports
|
|
204
|
-
try:
|
|
205
|
-
from MediBot import current_patient_context
|
|
206
|
-
except ImportError:
|
|
207
|
-
current_patient_context = None
|
|
208
221
|
|
|
209
222
|
while True:
|
|
210
223
|
# If interaction_mode is neither 'triage' nor 'error', then it's normal mode.
|
|
@@ -279,6 +292,8 @@ def user_interaction(csv_data, interaction_mode, error_message, reverse_mapping)
|
|
|
279
292
|
except KeyboardInterrupt:
|
|
280
293
|
print("\nOperation cancelled by user. Exiting script.")
|
|
281
294
|
|
|
295
|
+
# Load configuration when needed
|
|
296
|
+
config, _ = _get_config()
|
|
282
297
|
fixed_values = config.get('fixed_values', {}) # Get fixed values from config json
|
|
283
298
|
if response in ['yes', 'y']:
|
|
284
299
|
medicare_added_fixed_values = config.get('medicare_added_fixed_values', {})
|
|
@@ -1,27 +1,73 @@
|
|
|
1
|
-
#MediBot_dataformat_library.py
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
# MediBot_dataformat_library.py
|
|
2
|
+
"""
|
|
3
|
+
Data formatting library for MediBot
|
|
4
|
+
Contains functions for formatting various data types and handling CSV operations.
|
|
5
|
+
"""
|
|
5
6
|
|
|
6
|
-
# Add parent directory of the project to the Python path
|
|
7
7
|
import os
|
|
8
8
|
import sys
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
from MediLink import MediLink_ConfigLoader
|
|
14
|
-
config, crosswalk = MediLink_ConfigLoader.load_configuration()
|
|
15
|
-
except ImportError:
|
|
16
|
-
from MediLink_ConfigLoader import load_configuration
|
|
17
|
-
config, crosswalk = load_configuration()
|
|
18
|
-
|
|
19
|
-
from MediBot_Preprocessor_lib import open_csv_for_editing, initialize
|
|
20
|
-
from MediBot_UI import manage_script_pause, app_control
|
|
21
|
-
|
|
9
|
+
import csv
|
|
10
|
+
import json
|
|
11
|
+
import re
|
|
12
|
+
from datetime import datetime
|
|
22
13
|
|
|
23
|
-
#
|
|
24
|
-
|
|
14
|
+
# Add parent directory and current directory to the Python path
|
|
15
|
+
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
|
16
|
+
current_dir = os.path.abspath(os.path.dirname(__file__))
|
|
17
|
+
if project_dir not in sys.path:
|
|
18
|
+
sys.path.append(project_dir)
|
|
19
|
+
if current_dir not in sys.path:
|
|
20
|
+
sys.path.append(current_dir)
|
|
21
|
+
|
|
22
|
+
# Use core utilities for standardized imports
|
|
23
|
+
from MediCafe.core_utils import (
|
|
24
|
+
import_medibot_module,
|
|
25
|
+
get_config_loader_with_fallback,
|
|
26
|
+
create_config_cache
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
# Initialize configuration loader with fallback
|
|
30
|
+
MediLink_ConfigLoader = get_config_loader_with_fallback()
|
|
31
|
+
|
|
32
|
+
# Import MediBot modules using centralized import functions
|
|
33
|
+
MediBot_Preprocessor_lib = import_medibot_module('MediBot_Preprocessor_lib')
|
|
34
|
+
if MediBot_Preprocessor_lib:
|
|
35
|
+
open_csv_for_editing = getattr(MediBot_Preprocessor_lib, 'open_csv_for_editing', None)
|
|
36
|
+
initialize = getattr(MediBot_Preprocessor_lib, 'initialize', None)
|
|
37
|
+
else:
|
|
38
|
+
open_csv_for_editing = None
|
|
39
|
+
initialize = None
|
|
40
|
+
|
|
41
|
+
MediBot_UI = import_medibot_module('MediBot_UI')
|
|
42
|
+
if MediBot_UI:
|
|
43
|
+
manage_script_pause = getattr(MediBot_UI, 'manage_script_pause', None)
|
|
44
|
+
app_control = getattr(MediBot_UI, 'app_control', None)
|
|
45
|
+
else:
|
|
46
|
+
manage_script_pause = None
|
|
47
|
+
app_control = None
|
|
48
|
+
|
|
49
|
+
# Configuration will be loaded when needed
|
|
50
|
+
_get_config, (_config_cache, _crosswalk_cache) = create_config_cache()
|
|
51
|
+
|
|
52
|
+
# Ensure MediLink_ConfigLoader is available
|
|
53
|
+
if MediLink_ConfigLoader is None:
|
|
54
|
+
print("Warning: MediLink_ConfigLoader not available. Some functionality may be limited.")
|
|
55
|
+
# Create a minimal fallback logger
|
|
56
|
+
class FallbackLogger:
|
|
57
|
+
def log(self, message, level="INFO"):
|
|
58
|
+
print("[{}] {}".format(level, message))
|
|
59
|
+
MediLink_ConfigLoader = FallbackLogger()
|
|
60
|
+
|
|
61
|
+
# Initialize constants when needed
|
|
62
|
+
_initialized = False
|
|
63
|
+
|
|
64
|
+
def _ensure_initialized():
|
|
65
|
+
"""Ensure initialization has been done."""
|
|
66
|
+
global _initialized
|
|
67
|
+
if not _initialized:
|
|
68
|
+
config, _ = _get_config()
|
|
69
|
+
initialize(config)
|
|
70
|
+
_initialized = True
|
|
25
71
|
|
|
26
72
|
# Format Data
|
|
27
73
|
def format_name(value):
|
|
@@ -98,6 +144,7 @@ def enforce_significant_length(output):
|
|
|
98
144
|
return temp_output.replace(' ', '{Space}')
|
|
99
145
|
|
|
100
146
|
def format_street(value, csv_data, reverse_mapping, parsed_address_components):
|
|
147
|
+
_ensure_initialized()
|
|
101
148
|
# Temporarily disable script pause status
|
|
102
149
|
app_control.set_pause_status(False)
|
|
103
150
|
|
|
@@ -109,6 +156,7 @@ def format_street(value, csv_data, reverse_mapping, parsed_address_components):
|
|
|
109
156
|
try:
|
|
110
157
|
MediLink_ConfigLoader.log("Attempting to resolve address via regex...")
|
|
111
158
|
# Retrieve common city names from configuration and prepare a regex pattern
|
|
159
|
+
config, _ = _get_config()
|
|
112
160
|
common_cities = config.get('cities', [])
|
|
113
161
|
city_pattern = '|'.join(re.escape(city) for city in common_cities)
|
|
114
162
|
city_regex_pattern = r'(?P<City>{})'.format(city_pattern)
|
MediBot/MediBot_docx_decoder.py
CHANGED
|
@@ -1,19 +1,34 @@
|
|
|
1
1
|
#MediBot_docx_decoder.py
|
|
2
2
|
from datetime import datetime
|
|
3
3
|
from collections import OrderedDict
|
|
4
|
-
import os, re,
|
|
5
|
-
from docx import Document
|
|
6
|
-
from lxml import etree
|
|
4
|
+
import os, re, zipfile, pprint, time, sys
|
|
7
5
|
|
|
8
|
-
# Add
|
|
9
|
-
|
|
10
|
-
|
|
6
|
+
# Add workspace directory to Python path for MediCafe imports
|
|
7
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
8
|
+
workspace_dir = os.path.dirname(current_dir)
|
|
9
|
+
if workspace_dir not in sys.path:
|
|
10
|
+
sys.path.insert(0, workspace_dir)
|
|
11
11
|
|
|
12
12
|
try:
|
|
13
|
-
import
|
|
13
|
+
from docx import Document
|
|
14
14
|
except ImportError:
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
Document = None
|
|
16
|
+
try:
|
|
17
|
+
from lxml import etree
|
|
18
|
+
except ImportError:
|
|
19
|
+
etree = None
|
|
20
|
+
|
|
21
|
+
from MediCafe.core_utils import get_shared_config_loader
|
|
22
|
+
MediLink_ConfigLoader = get_shared_config_loader()
|
|
23
|
+
|
|
24
|
+
# Ensure MediLink_ConfigLoader is available
|
|
25
|
+
if MediLink_ConfigLoader is None:
|
|
26
|
+
print("Warning: MediLink_ConfigLoader not available. Some functionality may be limited.")
|
|
27
|
+
# Create a minimal fallback logger
|
|
28
|
+
class FallbackLogger:
|
|
29
|
+
def log(self, message, level="INFO"):
|
|
30
|
+
print("[{}] {}".format(level, message))
|
|
31
|
+
MediLink_ConfigLoader = FallbackLogger()
|
|
17
32
|
# Pre-compile regex patterns for better performance (XP/3.4.4 compatible)
|
|
18
33
|
_DIAGNOSIS_CODE_PATTERN = re.compile(r'H\d{2}\.\d+')
|
|
19
34
|
_DAY_WEEK_PATTERN = re.compile(r"(MONDAY|TUESDAY|WEDNESDAY|THURSDAY|FRIDAY|SATURDAY|SUNDAY)")
|
|
@@ -66,8 +81,19 @@ _DAY_MAP = {
|
|
|
66
81
|
|
|
67
82
|
|
|
68
83
|
def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
84
|
+
if Document is None:
|
|
85
|
+
MediLink_ConfigLoader.log("docx module not available, cannot parse .docx files", level="WARNING")
|
|
86
|
+
return {}
|
|
87
|
+
|
|
88
|
+
# TIMING: Start individual DOCX file processing
|
|
89
|
+
file_start_time = time.time()
|
|
90
|
+
|
|
69
91
|
try:
|
|
92
|
+
# TIMING: Start document opening
|
|
93
|
+
doc_open_start = time.time()
|
|
70
94
|
doc = Document(filepath) # Open the .docx file
|
|
95
|
+
doc_open_end = time.time()
|
|
96
|
+
doc_open_duration = doc_open_end - doc_open_start
|
|
71
97
|
except Exception as e:
|
|
72
98
|
MediLink_ConfigLoader.log("Error opening document: {}".format(e), level="ERROR") # Log error
|
|
73
99
|
return {}
|
|
@@ -75,9 +101,16 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
|
75
101
|
patient_data = OrderedDict() # Initialize OrderedDict to store data
|
|
76
102
|
MediLink_ConfigLoader.log("Extracting Date of Service from {}".format(filepath), level="DEBUG")
|
|
77
103
|
|
|
104
|
+
# TIMING: Start date extraction
|
|
105
|
+
date_extraction_start = time.time()
|
|
78
106
|
date_of_service = extract_date_of_service(filepath) # Extract date of service
|
|
107
|
+
date_extraction_end = time.time()
|
|
108
|
+
date_extraction_duration = date_extraction_end - date_extraction_start
|
|
79
109
|
MediLink_ConfigLoader.log("Date of Service recorded as: {}".format(date_of_service), level="DEBUG")
|
|
80
110
|
|
|
111
|
+
# TIMING: Start date conversion and validation
|
|
112
|
+
date_validation_start = time.time()
|
|
113
|
+
|
|
81
114
|
# Convert date_of_service to match the format of surgery_dates
|
|
82
115
|
date_of_service = datetime.strptime(date_of_service, '%m-%d-%Y') # Convert to datetime object
|
|
83
116
|
# Check if the date_of_service is in the passed surgery_dates
|
|
@@ -89,9 +122,20 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
|
89
122
|
# Convert back to MM-DD-YYYY format.
|
|
90
123
|
# TODO in the future, maybe just do the treatment to surgery_dates, no need to convert back and forth..
|
|
91
124
|
date_of_service = date_of_service.strftime('%m-%d-%Y')
|
|
125
|
+
|
|
126
|
+
date_validation_end = time.time()
|
|
127
|
+
date_validation_duration = date_validation_end - date_validation_start
|
|
128
|
+
|
|
129
|
+
# TIMING: Start table processing
|
|
130
|
+
table_processing_start = time.time()
|
|
131
|
+
tables_processed = 0
|
|
132
|
+
rows_processed = 0
|
|
133
|
+
patients_found = 0
|
|
92
134
|
|
|
93
135
|
for table in doc.tables: # Iterate over tables in the document
|
|
136
|
+
tables_processed += 1
|
|
94
137
|
for row in table.rows:
|
|
138
|
+
rows_processed += 1
|
|
95
139
|
cells = [cell.text.strip() for cell in row.cells]
|
|
96
140
|
if len(cells) > 4 and cells[3].startswith('#'):
|
|
97
141
|
try:
|
|
@@ -102,6 +146,7 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
|
102
146
|
|
|
103
147
|
if patient_id not in patient_data:
|
|
104
148
|
patient_data[patient_id] = {}
|
|
149
|
+
patients_found += 1
|
|
105
150
|
|
|
106
151
|
if date_of_service in patient_data[patient_id]:
|
|
107
152
|
MediLink_ConfigLoader.log("Duplicate entry for patient ID {} on date {}. Skipping.".format(patient_id, date_of_service), level="WARNING")
|
|
@@ -110,10 +155,34 @@ def parse_docx(filepath, surgery_dates): # Accept surgery_dates as a parameter
|
|
|
110
155
|
except Exception as e:
|
|
111
156
|
MediLink_ConfigLoader.log("Error processing row: {}. Error: {}".format(cells, e), level="ERROR")
|
|
112
157
|
|
|
158
|
+
table_processing_end = time.time()
|
|
159
|
+
table_processing_duration = table_processing_end - table_processing_start
|
|
160
|
+
|
|
161
|
+
# TIMING: Start validation
|
|
162
|
+
validation_start = time.time()
|
|
163
|
+
|
|
113
164
|
# Validation steps
|
|
114
165
|
validate_unknown_entries(patient_data)
|
|
115
166
|
validate_diagnostic_code(patient_data)
|
|
116
167
|
|
|
168
|
+
validation_end = time.time()
|
|
169
|
+
validation_duration = validation_end - validation_start
|
|
170
|
+
|
|
171
|
+
# TIMING: End total file processing
|
|
172
|
+
file_end_time = time.time()
|
|
173
|
+
total_duration = file_end_time - file_start_time
|
|
174
|
+
|
|
175
|
+
# Log timing details for slow files (more than 0.5 seconds)
|
|
176
|
+
if total_duration > 0.5:
|
|
177
|
+
print(" - DOCX file timing breakdown:")
|
|
178
|
+
print(" * Document opening: {:.3f}s".format(doc_open_duration))
|
|
179
|
+
print(" * Date extraction: {:.3f}s".format(date_extraction_duration))
|
|
180
|
+
print(" * Date validation: {:.3f}s".format(date_validation_duration))
|
|
181
|
+
print(" * Table processing: {:.3f}s ({} tables, {} rows, {} patients)".format(
|
|
182
|
+
table_processing_duration, tables_processed, rows_processed, patients_found))
|
|
183
|
+
print(" * Validation: {:.3f}s".format(validation_duration))
|
|
184
|
+
print(" * Total: {:.3f}s".format(total_duration))
|
|
185
|
+
|
|
117
186
|
return patient_data
|
|
118
187
|
|
|
119
188
|
|
|
@@ -148,6 +217,9 @@ def log_and_warn(patient_id, date, diagnostic_code, eye):
|
|
|
148
217
|
|
|
149
218
|
|
|
150
219
|
def extract_date_of_service(docx_path, use_in_memory=True):
|
|
220
|
+
# TIMING: Start date extraction process
|
|
221
|
+
extraction_start_time = time.time()
|
|
222
|
+
|
|
151
223
|
extract_to = "extracted_docx_debug"
|
|
152
224
|
in_memory_result = None
|
|
153
225
|
directory_based_result = None
|
|
@@ -160,6 +232,9 @@ def extract_date_of_service(docx_path, use_in_memory=True):
|
|
|
160
232
|
|
|
161
233
|
# Directory-Based Extraction
|
|
162
234
|
if not use_in_memory: # Only perform directory-based extraction if in-memory is not selected
|
|
235
|
+
# TIMING: Start directory-based extraction
|
|
236
|
+
dir_extraction_start = time.time()
|
|
237
|
+
|
|
163
238
|
try:
|
|
164
239
|
if not os.path.exists(extract_to):
|
|
165
240
|
os.makedirs(extract_to)
|
|
@@ -181,14 +256,23 @@ def extract_date_of_service(docx_path, use_in_memory=True):
|
|
|
181
256
|
MediLink_ConfigLoader.log("BadZipFile Error opening DOCX file {}: {}".format(docx_path, e), level="ERROR")
|
|
182
257
|
except Exception as e:
|
|
183
258
|
MediLink_ConfigLoader.log("Error opening DOCX file {}: {}".format(docx_path, e), level="ERROR")
|
|
259
|
+
|
|
260
|
+
# TIMING: End directory-based extraction
|
|
261
|
+
dir_extraction_end = time.time()
|
|
262
|
+
dir_extraction_duration = dir_extraction_end - dir_extraction_start
|
|
184
263
|
|
|
185
264
|
# In-Memory Extraction // Single-Pass Processing is typically more efficient in terms of both time and memory compared to list creation for header isolation.
|
|
186
265
|
if use_in_memory: # Only perform in-memory extraction if selected
|
|
266
|
+
# TIMING: Start in-memory extraction
|
|
267
|
+
mem_extraction_start = time.time()
|
|
268
|
+
|
|
187
269
|
try:
|
|
188
270
|
with zipfile.ZipFile(docx_path, 'r') as docx:
|
|
189
271
|
MediLink_ConfigLoader.log("Opened DOCX file for In-Memory extraction: {}".format(docx_path), level="DEBUG")
|
|
272
|
+
xml_files_processed = 0
|
|
190
273
|
for file_info in docx.infolist():
|
|
191
274
|
if file_info.filename.endswith('.xml'):
|
|
275
|
+
xml_files_processed += 1
|
|
192
276
|
MediLink_ConfigLoader.log("Processing XML file in-memory: {}".format(file_info.filename), level="DEBUG")
|
|
193
277
|
with docx.open(file_info) as file:
|
|
194
278
|
try:
|
|
@@ -208,6 +292,10 @@ def extract_date_of_service(docx_path, use_in_memory=True):
|
|
|
208
292
|
MediLink_ConfigLoader.log("BadZipFile Error opening DOCX file for In-Memory extraction {}: {}".format(docx_path, e), level="ERROR")
|
|
209
293
|
except Exception as e:
|
|
210
294
|
MediLink_ConfigLoader.log("Error during In-Memory extraction of DOCX file {}: {}".format(docx_path, e), level="ERROR")
|
|
295
|
+
|
|
296
|
+
# TIMING: End in-memory extraction
|
|
297
|
+
mem_extraction_end = time.time()
|
|
298
|
+
mem_extraction_duration = mem_extraction_end - mem_extraction_start
|
|
211
299
|
|
|
212
300
|
# Clean up the extracted directory if it exists
|
|
213
301
|
try:
|
|
@@ -217,6 +305,19 @@ def extract_date_of_service(docx_path, use_in_memory=True):
|
|
|
217
305
|
except Exception as e:
|
|
218
306
|
MediLink_ConfigLoader.log("Error cleaning up extraction directory {}: {}".format(extract_to, e), level="ERROR")
|
|
219
307
|
|
|
308
|
+
# TIMING: End total extraction process
|
|
309
|
+
extraction_end_time = time.time()
|
|
310
|
+
total_extraction_duration = extraction_end_time - extraction_start_time
|
|
311
|
+
|
|
312
|
+
# Log timing details for slow extractions (more than 0.2 seconds)
|
|
313
|
+
if total_extraction_duration > 0.2:
|
|
314
|
+
print(" - Date extraction timing:")
|
|
315
|
+
if not use_in_memory and 'dir_extraction_duration' in locals():
|
|
316
|
+
print(" * Directory-based: {:.3f}s".format(dir_extraction_duration))
|
|
317
|
+
if use_in_memory and 'mem_extraction_duration' in locals():
|
|
318
|
+
print(" * In-memory: {:.3f}s ({} XML files)".format(mem_extraction_duration, xml_files_processed if 'xml_files_processed' in locals() else 0))
|
|
319
|
+
print(" * Total: {:.3f}s".format(total_extraction_duration))
|
|
320
|
+
|
|
220
321
|
# Decide which result to return (prefer in-memory if available)
|
|
221
322
|
if in_memory_result:
|
|
222
323
|
return in_memory_result
|
|
@@ -389,7 +490,7 @@ def remove_directory(path):
|
|
|
389
490
|
|
|
390
491
|
|
|
391
492
|
def normalize_text(text):
|
|
392
|
-
# Optimized single-pass processing to avoid O(
|
|
493
|
+
# Optimized single-pass processing to avoid O(n2) complexity
|
|
393
494
|
# Process all abbreviations in one pass instead of multiple regex calls
|
|
394
495
|
for abbr, pattern in _MONTH_ABBR_PATTERNS.items():
|
|
395
496
|
text = pattern.sub(_MONTH_MAP[abbr], text)
|
|
@@ -485,18 +586,18 @@ def rotate_docx_files(directory, surgery_dates=None):
|
|
|
485
586
|
Returns:
|
|
486
587
|
- dict: Combined patient data from all processed files
|
|
487
588
|
"""
|
|
488
|
-
# PERFORMANCE OPTIMIZATION: Use os.
|
|
589
|
+
# PERFORMANCE OPTIMIZATION: Use os.listdir() for more efficient file system operations
|
|
489
590
|
# This reduces the number of file system calls and improves performance with large directories
|
|
490
591
|
valid_files = []
|
|
491
592
|
try:
|
|
492
|
-
# Use os.
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
593
|
+
# Use os.listdir() for better performance (XP/3.4.4 compatible)
|
|
594
|
+
for filename in os.listdir(directory):
|
|
595
|
+
# Filter files that contain "DR" and "SS" in the filename
|
|
596
|
+
if (filename.endswith('.docx') and
|
|
597
|
+
"DR" in filename and
|
|
598
|
+
"SS" in filename):
|
|
599
|
+
filepath = os.path.join(directory, filename)
|
|
600
|
+
valid_files.append(filepath)
|
|
500
601
|
except OSError as e:
|
|
501
602
|
print("Error accessing directory '{}': {}".format(directory, e))
|
|
502
603
|
return {}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
#MediBot_smart_import.py
|
|
2
|
+
"""
|
|
3
|
+
Upgraded MediBot main module using MediCafe Smart Import System
|
|
4
|
+
|
|
5
|
+
This is a demonstration of how MediBot.py should be migrated to use the
|
|
6
|
+
new centralized smart import system, eliminating sys.path manipulation
|
|
7
|
+
and circular import risks.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import os, subprocess, tempfile, traceback, re, time, sys
|
|
11
|
+
|
|
12
|
+
# Add workspace directory to Python path for MediCafe imports
|
|
13
|
+
current_dir = os.path.dirname(os.path.abspath(__file__))
|
|
14
|
+
workspace_dir = os.path.dirname(current_dir)
|
|
15
|
+
if workspace_dir not in sys.path:
|
|
16
|
+
sys.path.insert(0, workspace_dir)
|
|
17
|
+
|
|
18
|
+
try:
|
|
19
|
+
import msvcrt # Windows-specific module
|
|
20
|
+
except ImportError:
|
|
21
|
+
msvcrt = None # Not available on non-Windows systems
|
|
22
|
+
from collections import OrderedDict
|
|
23
|
+
|
|
24
|
+
# NEW SMART IMPORT APPROACH - Replace all MediCafe and MediBot imports with this
|
|
25
|
+
from MediCafe import setup_for_medibot, get_components
|
|
26
|
+
|
|
27
|
+
# Get everything needed for MediBot main functionality
|
|
28
|
+
print("[*] Loading MediBot components via smart import system...")
|
|
29
|
+
try:
|
|
30
|
+
components = setup_for_medibot('medibot_preprocessor')
|
|
31
|
+
print("[+] Loaded {} components successfully".format(len(components)))
|
|
32
|
+
|
|
33
|
+
# Extract components we need
|
|
34
|
+
core_utils = components.get('core_utils')
|
|
35
|
+
logging_config = components.get('logging_config')
|
|
36
|
+
api_core = components.get('api_core')
|
|
37
|
+
|
|
38
|
+
# MediBot specific components
|
|
39
|
+
medibot_dataformat_library = components.get('medibot_dataformat_library')
|
|
40
|
+
medibot_preprocessor = components.get('medibot_preprocessor')
|
|
41
|
+
medibot_preprocessor_lib = components.get('medibot_preprocessor_lib')
|
|
42
|
+
medibot_ui = components.get('medibot_ui')
|
|
43
|
+
medibot_crosswalk_library = components.get('medibot_crosswalk_library')
|
|
44
|
+
|
|
45
|
+
print("[+] Core components extracted")
|
|
46
|
+
|
|
47
|
+
except Exception as e:
|
|
48
|
+
print("[!] Some components unavailable (expected in dev): {}".format(e))
|
|
49
|
+
# Fallback imports for development
|
|
50
|
+
try:
|
|
51
|
+
core_utils = get_components('core_utils', silent_fail=True)
|
|
52
|
+
logging_config = get_components('logging_config', silent_fail=True)
|
|
53
|
+
api_core = get_components('api_core', silent_fail=True)
|
|
54
|
+
# Initialize other components as None for fallback
|
|
55
|
+
medibot_dataformat_library = None
|
|
56
|
+
medibot_preprocessor = None
|
|
57
|
+
medibot_preprocessor_lib = None
|
|
58
|
+
medibot_ui = None
|
|
59
|
+
medibot_crosswalk_library = None
|
|
60
|
+
print("[+] Fallback to individual component loading")
|
|
61
|
+
except:
|
|
62
|
+
print("[-] Smart import system not available - using legacy approach")
|
|
63
|
+
core_utils = None
|
|
64
|
+
logging_config = None
|
|
65
|
+
api_core = None
|
|
66
|
+
medibot_dataformat_library = None
|
|
67
|
+
medibot_preprocessor = None
|
|
68
|
+
medibot_preprocessor_lib = None
|
|
69
|
+
medibot_ui = None
|
|
70
|
+
medibot_crosswalk_library = None
|
|
71
|
+
|
|
72
|
+
# Configuration loader setup
|
|
73
|
+
MediLink_ConfigLoader = None
|
|
74
|
+
if core_utils:
|
|
75
|
+
try:
|
|
76
|
+
get_config_loader_with_fallback = getattr(core_utils, 'get_config_loader_with_fallback', None)
|
|
77
|
+
if get_config_loader_with_fallback:
|
|
78
|
+
MediLink_ConfigLoader = get_config_loader_with_fallback()
|
|
79
|
+
print("[+] Configuration loader initialized via smart import")
|
|
80
|
+
except Exception as e:
|
|
81
|
+
print("[!] Configuration loader setup issue: {}".format(e))
|
|
82
|
+
|
|
83
|
+
# API client setup
|
|
84
|
+
api_client = None
|
|
85
|
+
factory = None
|
|
86
|
+
|
|
87
|
+
if api_core and core_utils:
|
|
88
|
+
try:
|
|
89
|
+
get_api_client_factory = getattr(core_utils, 'get_api_client_factory', None)
|
|
90
|
+
if get_api_client_factory:
|
|
91
|
+
factory = get_api_client_factory()
|
|
92
|
+
if factory:
|
|
93
|
+
api_client = factory.get_shared_client()
|
|
94
|
+
print("[+] API client initialized via smart import")
|
|
95
|
+
except Exception as e:
|
|
96
|
+
print("[!] API client setup issue: {}".format(e))
|
|
97
|
+
|
|
98
|
+
# Function extraction from components (if available)
|
|
99
|
+
app_control = None
|
|
100
|
+
manage_script_pause = None
|
|
101
|
+
user_interaction = None
|
|
102
|
+
crosswalk_update = None
|
|
103
|
+
|
|
104
|
+
if medibot_ui:
|
|
105
|
+
try:
|
|
106
|
+
app_control = getattr(medibot_ui, 'app_control', None)
|
|
107
|
+
manage_script_pause = getattr(medibot_ui, 'manage_script_pause', None)
|
|
108
|
+
user_interaction = getattr(medibot_ui, 'user_interaction', None)
|
|
109
|
+
print("[+] UI functions extracted")
|
|
110
|
+
except Exception as e:
|
|
111
|
+
print("[!] UI function extraction issue: {}".format(e))
|
|
112
|
+
|
|
113
|
+
if medibot_crosswalk_library:
|
|
114
|
+
try:
|
|
115
|
+
crosswalk_update = getattr(medibot_crosswalk_library, 'crosswalk_update', None)
|
|
116
|
+
print("[+] Crosswalk functions extracted")
|
|
117
|
+
except Exception as e:
|
|
118
|
+
print("[!] Crosswalk function extraction issue: {}".format(e))
|
|
119
|
+
|
|
120
|
+
# Legacy functions for backward compatibility
|
|
121
|
+
def import_medibot_module_with_debug(module_name):
|
|
122
|
+
"""Legacy function wrapper for backwards compatibility."""
|
|
123
|
+
try:
|
|
124
|
+
component_name = "medibot_{}".format(module_name.lower().replace('medibot_', ''))
|
|
125
|
+
return get_components(component_name, silent_fail=True)
|
|
126
|
+
except:
|
|
127
|
+
print("[!] Could not load {} via smart import".format(module_name))
|
|
128
|
+
return None
|
|
129
|
+
|
|
130
|
+
def get_config_loader_with_fallback():
|
|
131
|
+
"""Legacy function wrapper for backwards compatibility."""
|
|
132
|
+
return MediLink_ConfigLoader
|
|
133
|
+
|
|
134
|
+
def get_api_client_factory():
|
|
135
|
+
"""Legacy function wrapper for backwards compatibility."""
|
|
136
|
+
return factory
|
|
137
|
+
|
|
138
|
+
# Rest of the MediBot functionality would continue here...
|
|
139
|
+
# This demonstrates the pattern for migrating the entire file
|
|
140
|
+
|
|
141
|
+
def main():
|
|
142
|
+
"""Main MediBot function using smart imports."""
|
|
143
|
+
print("\n[*] MediBot Starting with Smart Import System")
|
|
144
|
+
print("=" * 50)
|
|
145
|
+
|
|
146
|
+
if MediLink_ConfigLoader:
|
|
147
|
+
print("[+] Configuration system ready")
|
|
148
|
+
else:
|
|
149
|
+
print("[!] Configuration system not available")
|
|
150
|
+
|
|
151
|
+
if api_client:
|
|
152
|
+
print("[+] API client ready")
|
|
153
|
+
else:
|
|
154
|
+
print("[!] API client not available")
|
|
155
|
+
|
|
156
|
+
if medibot_preprocessor:
|
|
157
|
+
print("[+] Preprocessor ready")
|
|
158
|
+
else:
|
|
159
|
+
print("[!] Preprocessor not available")
|
|
160
|
+
|
|
161
|
+
if medibot_ui and app_control:
|
|
162
|
+
print("[+] UI system ready")
|
|
163
|
+
else:
|
|
164
|
+
print("[!] UI system not available")
|
|
165
|
+
|
|
166
|
+
print("\n[i] Benefits of Smart Import System:")
|
|
167
|
+
print(" - No sys.path manipulation needed")
|
|
168
|
+
print(" - No circular import risks")
|
|
169
|
+
print(" - Centralized component management")
|
|
170
|
+
print(" - Graceful fallback for missing components")
|
|
171
|
+
print(" - Easy to test and validate")
|
|
172
|
+
|
|
173
|
+
return True
|
|
174
|
+
|
|
175
|
+
if __name__ == "__main__":
|
|
176
|
+
success = main()
|
|
177
|
+
if success:
|
|
178
|
+
print("\n[+] MediBot smart import demonstration completed successfully!")
|
|
179
|
+
else:
|
|
180
|
+
print("\n[-] MediBot smart import demonstration had issues")
|