medicafe 0.250818.0__py3-none-any.whl → 0.250819.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.
- MediBot/MediBot.py +35 -1
- MediBot/MediBot_Crosswalk_Utils.py +1 -4
- MediBot/MediBot_Preprocessor.py +88 -14
- MediBot/MediBot_Preprocessor_lib.py +97 -0
- MediBot/__init__.py +1 -1
- MediCafe/__init__.py +1 -1
- MediLink/MediLink_837p_encoder.py +1 -1
- MediLink/MediLink_837p_encoder_library.py +580 -318
- MediLink/MediLink_837p_utilities.py +1 -4
- MediLink/MediLink_API_Generator.py +1 -1
- MediLink/MediLink_DataMgmt.py +2 -2
- MediLink/MediLink_Down.py +1 -1
- MediLink/MediLink_Up.py +45 -4
- MediLink/MediLink_main.py +2 -1
- MediLink/__init__.py +1 -1
- MediLink/gmail_oauth_utils.py +1 -4
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/METADATA +1 -1
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/RECORD +22 -22
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/LICENSE +0 -0
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/WHEEL +0 -0
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/entry_points.txt +0 -0
- {medicafe-0.250818.0.dist-info → medicafe-0.250819.0.dist-info}/top_level.txt +0 -0
MediBot/MediBot.py
CHANGED
@@ -662,6 +662,34 @@ if __name__ == "__main__":
|
|
662
662
|
# Initialize constants from config
|
663
663
|
MediBot_Preprocessor_lib.initialize(e_state.config)
|
664
664
|
|
665
|
+
# PERFORMANCE OPTIMIZATION: Load both Medicare and Private patient databases during startup
|
666
|
+
# Files are small (10K-20K rows each) so memory usage is minimal (~4MB total)
|
667
|
+
# This eliminates the 1-2 second delay from user workflow entirely
|
668
|
+
print("Loading patient databases...")
|
669
|
+
MediLink_ConfigLoader.log("Loading patient databases...", level="INFO")
|
670
|
+
|
671
|
+
try:
|
672
|
+
medicare_path = e_state.config.get('MEDICARE_MAPAT_MED_PATH', "")
|
673
|
+
private_path = e_state.config.get('MAPAT_MED_PATH', "")
|
674
|
+
|
675
|
+
# Load both databases into separate caches
|
676
|
+
medicare_cache = MediBot_Preprocessor.load_existing_patient_ids(medicare_path) if medicare_path else {}
|
677
|
+
private_cache = MediBot_Preprocessor.load_existing_patient_ids(private_path) if private_path else {}
|
678
|
+
|
679
|
+
# Store both caches for later use
|
680
|
+
MediBot_Preprocessor.set_patient_caches(medicare_cache, private_cache)
|
681
|
+
|
682
|
+
if PERFORMANCE_LOGGING:
|
683
|
+
print("Patient databases loaded: {} Medicare, {} Private patients".format(
|
684
|
+
len(medicare_cache), len(private_cache)))
|
685
|
+
MediLink_ConfigLoader.log("Patient databases loaded: {} Medicare, {} Private patients".format(
|
686
|
+
len(medicare_cache), len(private_cache)), level="INFO")
|
687
|
+
|
688
|
+
except Exception as e:
|
689
|
+
MediLink_ConfigLoader.log("Warning: Could not load patient databases: {}".format(e), level="WARNING")
|
690
|
+
if PERFORMANCE_LOGGING:
|
691
|
+
print("Warning: Could not load patient databases - will load on demand")
|
692
|
+
|
665
693
|
if PERFORMANCE_LOGGING:
|
666
694
|
print("Loading CSV Data...")
|
667
695
|
MediLink_ConfigLoader.log("Loading CSV Data...", level="INFO")
|
@@ -745,7 +773,13 @@ if __name__ == "__main__":
|
|
745
773
|
if (not _ac()) or (not _ac().get_mapat_med_path()) or (not os.path.exists(_ac().get_mapat_med_path())):
|
746
774
|
record_startup_warning("Warning: MAPAT.MED PATH is missing or invalid. Please check the path configuration.")
|
747
775
|
|
748
|
-
#
|
776
|
+
# PERFORMANCE OPTIMIZATION: Select the appropriate pre-loaded patient cache
|
777
|
+
# Both caches were loaded during startup, now we just select the right one
|
778
|
+
MediBot_Preprocessor.select_active_cache(is_medicare)
|
779
|
+
if PERFORMANCE_LOGGING:
|
780
|
+
print("Using {} patient cache for existing patient check".format("Medicare" if is_medicare else "Private"))
|
781
|
+
|
782
|
+
# Perform the existing patients check (now uses cached data)
|
749
783
|
existing_patients, patients_to_process = MediBot_Preprocessor.check_existing_patients(selected_patient_ids, _ac().get_mapat_med_path() if _ac() else '')
|
750
784
|
|
751
785
|
if existing_patients:
|
@@ -9,10 +9,7 @@ to improve code organization and maintainability.
|
|
9
9
|
Compatible with Python 3.4.4 and Windows XP environments.
|
10
10
|
"""
|
11
11
|
|
12
|
-
import json
|
13
|
-
import os
|
14
|
-
import sys
|
15
|
-
import threading
|
12
|
+
import json, os, sys
|
16
13
|
|
17
14
|
# Set the project directory to the parent directory of the current file
|
18
15
|
project_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
MediBot/MediBot_Preprocessor.py
CHANGED
@@ -144,30 +144,104 @@ def preprocess_csv_data(csv_data, crosswalk):
|
|
144
144
|
MediLink_ConfigLoader.log(message, level="ERROR")
|
145
145
|
print(message)
|
146
146
|
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
147
|
+
# Global caches for existing patient IDs to avoid repeated file I/O
|
148
|
+
_medicare_patients_cache = None
|
149
|
+
_private_patients_cache = None
|
150
|
+
_current_cache = None # Points to the active cache based on user selection
|
151
151
|
|
152
|
+
def load_existing_patient_ids(MAPAT_MED_PATH):
|
153
|
+
"""
|
154
|
+
Load all existing patient IDs from MAPAT.MED file into memory cache.
|
155
|
+
|
156
|
+
Args:
|
157
|
+
MAPAT_MED_PATH: Path to the MAPAT.MED file
|
158
|
+
|
159
|
+
Returns:
|
160
|
+
dict: {patient_id: patient_name} mapping of all existing patients
|
161
|
+
"""
|
162
|
+
patient_cache = {}
|
163
|
+
|
164
|
+
if not MAPAT_MED_PATH:
|
165
|
+
MediLink_ConfigLoader.log("MAPAT.MED path not provided - returning empty cache", level="WARNING")
|
166
|
+
return patient_cache
|
167
|
+
|
152
168
|
try:
|
169
|
+
MediLink_ConfigLoader.log("Loading patient cache from: {}".format(MAPAT_MED_PATH), level="INFO")
|
153
170
|
with open(MAPAT_MED_PATH, 'r') as file:
|
154
171
|
next(file) # Skip header row
|
155
172
|
for line in file:
|
156
|
-
if line.startswith("0"):
|
173
|
+
if line.startswith("0"): # 1 is a flag for a deleted record so it would need to be re-entered.
|
157
174
|
patient_id = line[194:202].strip() # Extract Patient ID (Columns 195-202)
|
158
175
|
patient_name = line[9:39].strip() # Extract Patient Name (Columns 10-39)
|
159
176
|
|
160
|
-
if patient_id
|
161
|
-
|
162
|
-
|
163
|
-
|
177
|
+
if patient_id: # Only cache non-empty patient IDs
|
178
|
+
patient_cache[patient_id] = patient_name
|
179
|
+
|
180
|
+
MediLink_ConfigLoader.log("Loaded {} patients into cache from {}".format(len(patient_cache), MAPAT_MED_PATH), level="INFO")
|
181
|
+
|
164
182
|
except FileNotFoundError:
|
165
|
-
|
166
|
-
print("MAPAT.med was not found at location
|
167
|
-
print("
|
183
|
+
MediLink_ConfigLoader.log("MAPAT.med was not found at location: {}".format(MAPAT_MED_PATH), level="WARNING")
|
184
|
+
print("MAPAT.med was not found at location: {}".format(MAPAT_MED_PATH))
|
185
|
+
print("Continuing with empty patient cache...")
|
186
|
+
except Exception as e:
|
187
|
+
MediLink_ConfigLoader.log("Error loading patient cache: {}".format(e), level="ERROR")
|
188
|
+
print("Error loading patient cache: {}".format(e))
|
189
|
+
|
190
|
+
return patient_cache
|
191
|
+
|
192
|
+
def set_patient_caches(medicare_cache, private_cache):
|
193
|
+
"""
|
194
|
+
Store both patient caches for later use based on user selection.
|
195
|
+
|
196
|
+
Args:
|
197
|
+
medicare_cache: Dict of Medicare patient IDs and names
|
198
|
+
private_cache: Dict of Private patient IDs and names
|
199
|
+
"""
|
200
|
+
global _medicare_patients_cache, _private_patients_cache
|
201
|
+
_medicare_patients_cache = medicare_cache
|
202
|
+
_private_patients_cache = private_cache
|
203
|
+
|
204
|
+
def select_active_cache(is_medicare):
|
205
|
+
"""
|
206
|
+
Select which patient cache to use based on Medicare selection.
|
207
|
+
|
208
|
+
Args:
|
209
|
+
is_medicare: True for Medicare patients, False for Private patients
|
210
|
+
"""
|
211
|
+
global _current_cache, _medicare_patients_cache, _private_patients_cache
|
212
|
+
_current_cache = _medicare_patients_cache if is_medicare else _private_patients_cache
|
213
|
+
|
214
|
+
def check_existing_patients(selected_patient_ids, MAPAT_MED_PATH):
|
215
|
+
"""
|
216
|
+
Check which selected patients already exist in the system using cached data.
|
217
|
+
This is now much faster as it uses in-memory cache instead of file I/O.
|
218
|
+
|
219
|
+
Args:
|
220
|
+
selected_patient_ids: List of patient IDs to check
|
221
|
+
MAPAT_MED_PATH: Path to MAPAT.MED file (for fallback if cache not available)
|
168
222
|
|
169
|
-
|
170
|
-
|
223
|
+
Returns:
|
224
|
+
tuple: (existing_patients, patients_to_process)
|
225
|
+
"""
|
226
|
+
global _current_cache
|
227
|
+
|
228
|
+
# Use current cache if available, otherwise fallback to loading file
|
229
|
+
if _current_cache is not None:
|
230
|
+
existing_patients_dict = _current_cache
|
231
|
+
else:
|
232
|
+
# Fallback: load from file if cache not available
|
233
|
+
existing_patients_dict = load_existing_patient_ids(MAPAT_MED_PATH)
|
234
|
+
|
235
|
+
existing_patients = []
|
236
|
+
patients_to_process = []
|
237
|
+
|
238
|
+
# Use cached data for O(1) lookups instead of file I/O
|
239
|
+
for patient_id in selected_patient_ids:
|
240
|
+
if patient_id in existing_patients_dict:
|
241
|
+
patient_name = existing_patients_dict[patient_id]
|
242
|
+
existing_patients.append((patient_id, patient_name))
|
243
|
+
else:
|
244
|
+
patients_to_process.append(patient_id)
|
171
245
|
|
172
246
|
return existing_patients, patients_to_process
|
173
247
|
|
@@ -309,6 +309,103 @@ def detect_date_format(date_str):
|
|
309
309
|
|
310
310
|
return None
|
311
311
|
|
312
|
+
class OptimizedDate:
|
313
|
+
"""
|
314
|
+
Optimized date object that pre-computes all common format variations
|
315
|
+
to avoid redundant datetime conversions throughout the application.
|
316
|
+
"""
|
317
|
+
def __init__(self, datetime_obj):
|
318
|
+
self.datetime = datetime_obj
|
319
|
+
# Pre-compute all common format variations
|
320
|
+
self._display_short = datetime_obj.strftime('%m-%d') # For table display
|
321
|
+
self._display_full = datetime_obj.strftime('%m-%d-%Y') # Full format
|
322
|
+
self._medisoft_format = datetime_obj.strftime('%m%d%Y') # For Medisoft entry
|
323
|
+
self._iso_format = datetime_obj.strftime('%Y-%m-%d') # For sorting/comparison
|
324
|
+
|
325
|
+
@property
|
326
|
+
def display_short(self):
|
327
|
+
"""Short display format: MM-DD"""
|
328
|
+
return self._display_short
|
329
|
+
|
330
|
+
@property
|
331
|
+
def display_full(self):
|
332
|
+
"""Full display format: MM-DD-YYYY"""
|
333
|
+
return self._display_full
|
334
|
+
|
335
|
+
@property
|
336
|
+
def medisoft_format(self):
|
337
|
+
"""Medisoft entry format: MMDDYYYY"""
|
338
|
+
return self._medisoft_format
|
339
|
+
|
340
|
+
@property
|
341
|
+
def iso_format(self):
|
342
|
+
"""ISO format for sorting: YYYY-MM-DD"""
|
343
|
+
return self._iso_format
|
344
|
+
|
345
|
+
def __str__(self):
|
346
|
+
return self._display_full
|
347
|
+
|
348
|
+
def __repr__(self):
|
349
|
+
return "OptimizedDate({})".format(self._display_full)
|
350
|
+
|
351
|
+
def __eq__(self, other):
|
352
|
+
if isinstance(other, OptimizedDate):
|
353
|
+
return self.datetime == other.datetime
|
354
|
+
elif hasattr(other, 'strftime'): # datetime object
|
355
|
+
return self.datetime == other
|
356
|
+
return False
|
357
|
+
|
358
|
+
def __lt__(self, other):
|
359
|
+
if isinstance(other, OptimizedDate):
|
360
|
+
return self.datetime < other.datetime
|
361
|
+
elif hasattr(other, 'strftime'): # datetime object
|
362
|
+
return self.datetime < other
|
363
|
+
return NotImplemented
|
364
|
+
|
365
|
+
def __gt__(self, other):
|
366
|
+
if isinstance(other, OptimizedDate):
|
367
|
+
return self.datetime > other.datetime
|
368
|
+
elif hasattr(other, 'strftime'): # datetime object
|
369
|
+
return self.datetime > other
|
370
|
+
return NotImplemented
|
371
|
+
|
372
|
+
def strftime(self, format_str):
|
373
|
+
"""Fallback for any custom format needs"""
|
374
|
+
return self.datetime.strftime(format_str)
|
375
|
+
|
376
|
+
@classmethod
|
377
|
+
def from_string(cls, date_str, cleaned=False):
|
378
|
+
"""
|
379
|
+
Create OptimizedDate from string, with optional pre-cleaning.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
date_str: Date string to parse
|
383
|
+
cleaned: If True, assumes string is already cleaned
|
384
|
+
|
385
|
+
Returns:
|
386
|
+
OptimizedDate object or None if parsing fails
|
387
|
+
"""
|
388
|
+
if not cleaned:
|
389
|
+
date_str = clean_surgery_date_string(date_str)
|
390
|
+
if not date_str:
|
391
|
+
return None
|
392
|
+
|
393
|
+
# Try standard format first (most common)
|
394
|
+
try:
|
395
|
+
return cls(datetime.strptime(date_str, '%m/%d/%Y'))
|
396
|
+
except ValueError:
|
397
|
+
pass
|
398
|
+
|
399
|
+
# Try alternative formats
|
400
|
+
formats = ['%m-%d-%Y', '%m/%d/%y', '%m-%d-%y', '%Y/%m/%d', '%Y-%m-%d']
|
401
|
+
for fmt in formats:
|
402
|
+
try:
|
403
|
+
return cls(datetime.strptime(date_str, fmt))
|
404
|
+
except ValueError:
|
405
|
+
continue
|
406
|
+
|
407
|
+
return None
|
408
|
+
|
312
409
|
def clean_surgery_date_string(date_str):
|
313
410
|
"""
|
314
411
|
Cleans and normalizes surgery date strings to handle damaged data.
|
MediBot/__init__.py
CHANGED
MediCafe/__init__.py
CHANGED
@@ -202,7 +202,7 @@ def write_output_file(document_segments, output_directory, endpoint_key, input_f
|
|
202
202
|
|
203
203
|
# Write the document to the output file
|
204
204
|
try:
|
205
|
-
with open(new_output_file_path, 'w') as output_file:
|
205
|
+
with open(new_output_file_path, 'w', encoding='utf-8') as output_file:
|
206
206
|
output_file.write('\n'.join(document_segments))
|
207
207
|
MediLink_ConfigLoader.log("Successfully converted and saved to {}".format(new_output_file_path), config, level="INFO")
|
208
208
|
return new_output_file_path
|