medicafe 0.250819.0__tar.gz → 0.250819.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/__init__.py +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/MediLink_ConfigLoader.py +6 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/__init__.py +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_837p_encoder_library.py +56 -46
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_main.py +10 -44
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/__init__.py +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/PKG-INFO +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/PKG-INFO +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/setup.py +1 -1
- {medicafe-0.250819.0 → medicafe-0.250819.2}/LICENSE +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MANIFEST.in +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot.bat +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Charges.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Crosswalk_Library.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Crosswalk_Utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Post.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Preprocessor.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_Preprocessor_lib.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_UI.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_dataformat_library.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_docx_decoder.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/MediBot_smart_import.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/get_medicafe_version.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/update_json.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediBot/update_medicafe.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/__main__.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/api_core.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/api_core_backup.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/api_factory.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/api_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/core_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/graphql_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/logging_config.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/logging_demo.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/migration_helpers.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/smart_import.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediCafe/submission_index.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/InsuranceTypeService.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_837p_cob_library.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_837p_encoder.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_837p_utilities.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_API_Generator.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Azure.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_ClaimStatus.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_DataMgmt.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Decoder.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Deductible.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Deductible_Validator.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Display_Utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Down.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Gmail.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Mailer.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Parser.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_PatientProcessor.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Scan.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Scheduler.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_UI.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_Up.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_insurance_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/MediLink_smart_import.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/Soumit_api.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/gmail_http_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/gmail_oauth_utils.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/insurance_type_integration_test.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/openssl.cnf +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/test.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/test_cob_library.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/test_timing.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/test_validation.py +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/MediLink/webapp.html +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/README.md +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/SOURCES.txt +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/dependency_links.txt +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/entry_points.txt +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/not-zip-safe +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/requires.txt +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/medicafe.egg-info/top_level.txt +0 -0
- {medicafe-0.250819.0 → medicafe-0.250819.2}/setup.cfg +0 -0
@@ -6,6 +6,7 @@ except ImportError:
|
|
6
6
|
yaml = None
|
7
7
|
from datetime import datetime
|
8
8
|
from collections import OrderedDict
|
9
|
+
from functools import reduce
|
9
10
|
|
10
11
|
# Global configuration cache to prevent repeated loading
|
11
12
|
_CONFIG_CACHE = None
|
@@ -166,6 +167,11 @@ def clear_config_cache():
|
|
166
167
|
_CONFIG_CACHE = None
|
167
168
|
_CROSSWALK_CACHE = None
|
168
169
|
|
170
|
+
def require_config_value(key_path, use_cache=True):
|
171
|
+
# TODO This needs expanding a little bit but generally this type of functionality is good to have at this level.
|
172
|
+
config = _CONFIG_CACHE if use_cache and _CONFIG_CACHE else load_configuration()[0]
|
173
|
+
return reduce(lambda d, k: d[k], key_path.split('.'), config)
|
174
|
+
|
169
175
|
# Logs messages with optional error type and claim data.
|
170
176
|
def log(message, config=None, level="INFO", error_type=None, claim=None, verbose=False, console_output=False):
|
171
177
|
|
@@ -199,23 +199,23 @@ def create_nm1_billing_provider_segment(config, endpoint):
|
|
199
199
|
billing_provider_entity_code = endpoint_config.get('billing_provider_entity_code', '85')
|
200
200
|
billing_provider_npi_qualifier = endpoint_config.get('billing_provider_npi_qualifier', 'XX')
|
201
201
|
# Resolve required values with TestMode-aware enforcement
|
202
|
-
billing_provider_lastname =
|
202
|
+
billing_provider_lastname = legacy_require_config_value(
|
203
203
|
[config, endpoint_config],
|
204
|
-
'billing_provider_lastname',
|
205
|
-
|
204
|
+
['billing_provider_lastname', 'default_billing_provider_name'],
|
205
|
+
'DEFAULT NAME',
|
206
206
|
'2010AA Billing Provider Last Name',
|
207
207
|
config,
|
208
208
|
endpoint
|
209
209
|
)
|
210
210
|
billing_provider_firstname = config.get('billing_provider_firstname', '')
|
211
|
-
billing_provider_npi =
|
211
|
+
billing_provider_npi = legacy_require_config_value(
|
212
212
|
[endpoint_config, config],
|
213
|
-
'billing_provider_npi',
|
214
|
-
|
213
|
+
['billing_provider_npi', 'default_billing_provider_npi'],
|
214
|
+
'DEFAULT NPI',
|
215
215
|
'2010AA Billing Provider NPI',
|
216
216
|
config,
|
217
217
|
endpoint
|
218
|
-
)
|
218
|
+
) # BUG This is stupid. The NPI is the same. Maybe the first and last name registration might vary by endpoint, but the NPI wont.
|
219
219
|
|
220
220
|
# Determine billing_entity_type_qualifier based on the presence of billing_provider_firstname
|
221
221
|
billing_entity_type_qualifier = '1' if billing_provider_firstname else '2'
|
@@ -233,17 +233,17 @@ def create_nm1_billing_provider_segment(config, endpoint):
|
|
233
233
|
# Construct address segments
|
234
234
|
address_segments = []
|
235
235
|
if config.get('billing_provider_address'):
|
236
|
-
addr =
|
237
|
-
city =
|
238
|
-
state =
|
239
|
-
zip_code =
|
236
|
+
addr = legacy_require_config_value([config], 'billing_provider_address', 'NO ADDRESS', '2010AA Billing Address', config, endpoint)
|
237
|
+
city = legacy_require_config_value([config], 'billing_provider_city', 'NO CITY', '2010AA Billing City', config, endpoint)
|
238
|
+
state = legacy_require_config_value([config], 'billing_provider_state', 'NO STATE', '2010AA Billing State', config, endpoint)
|
239
|
+
zip_code = legacy_require_config_value([config], 'billing_provider_zip', 'NO ZIP', '2010AA Billing ZIP', config, endpoint)
|
240
240
|
# N3 segment for address line
|
241
241
|
address_segments.append("N3*{}~".format(addr))
|
242
242
|
# N4 segment for City, State, ZIP
|
243
243
|
address_segments.append("N4*{}*{}*{}~".format(city, state, zip_code))
|
244
244
|
|
245
245
|
# Assuming Tax ID is part of the same loop, otherwise move REF segment to the correct loop
|
246
|
-
billing_tin =
|
246
|
+
billing_tin = legacy_require_config_value([config], 'billing_provider_tin', 'NO TAX ID', '2010AA Billing TIN', config, endpoint)
|
247
247
|
ref_segment = "REF*EI*{}~".format(billing_tin)
|
248
248
|
|
249
249
|
# Construct PRV segment if provider taxonomy is needed
|
@@ -265,12 +265,12 @@ def create_service_facility_location_npi_segment(config):
|
|
265
265
|
Constructs segments for the service facility location, including the NM1 segment for identification
|
266
266
|
and accompanying N3 and N4 segments for address details.
|
267
267
|
"""
|
268
|
-
facility_npi =
|
269
|
-
facility_name =
|
270
|
-
address_line_1 =
|
271
|
-
city =
|
272
|
-
state =
|
273
|
-
zip_code =
|
268
|
+
facility_npi = legacy_require_config_value([config], 'service_facility_npi', 'DEFAULT FACILITY NPI', '2310C Service Facility NPI', config)
|
269
|
+
facility_name = legacy_require_config_value([config], 'service_facility_name', 'DEFAULT FACILITY NAME', '2310C Service Facility Name', config)
|
270
|
+
address_line_1 = legacy_require_config_value([config], 'service_facility_address', 'NO ADDRESS', '2310C Service Facility Address', config)
|
271
|
+
city = legacy_require_config_value([config], 'service_facility_city', 'NO CITY', '2310C Service Facility City', config)
|
272
|
+
state = legacy_require_config_value([config], 'service_facility_state', 'NO STATE', '2310C Service Facility State', config)
|
273
|
+
zip_code = legacy_require_config_value([config], 'service_facility_zip', 'NO ZIP', '2310C Service Facility ZIP', config)
|
274
274
|
|
275
275
|
# NM1 segment for facility identification
|
276
276
|
nm1_segment = "NM1*77*2*{}*****XX*{}~".format(facility_name, facility_npi)
|
@@ -290,20 +290,20 @@ def create_1000A_submitter_name_segment(patient_data, config, endpoint):
|
|
290
290
|
submitter_id_qualifier = endpoint_config.get('submitter_id_qualifier', '46') # '46' for ETIN or 'XX' for NPI
|
291
291
|
|
292
292
|
# Required submitter name
|
293
|
-
submitter_name =
|
293
|
+
submitter_name = legacy_require_config_value([endpoint_config, config], 'nm_103_value', 'DEFAULT NAME', '1000A Submitter Name', config, endpoint)
|
294
294
|
|
295
295
|
# Extract payer_id from patient_data
|
296
296
|
payer_id = patient_data.get('payer_id', '')
|
297
297
|
|
298
298
|
# Check if payer_id is Florida Blue (00590 or BCBSF) and assign submitter_id accordingly
|
299
299
|
if payer_id in ['00590', 'BCBSF']:
|
300
|
-
submitter_id =
|
300
|
+
submitter_id = legacy_require_config_value([endpoint_config], 'nm_109_bcbsf', 'DEFAULT BCBSF ID', '1000A Submitter ID (BCBSF)', config, endpoint)
|
301
301
|
else:
|
302
|
-
submitter_id =
|
302
|
+
submitter_id = legacy_require_config_value([endpoint_config], 'nm_109_value', 'DEFAULT ID', '1000A Submitter ID', config, endpoint)
|
303
303
|
|
304
304
|
# Submitter contact details (required)
|
305
|
-
contact_name =
|
306
|
-
contact_telephone_number =
|
305
|
+
contact_name = legacy_require_config_value([config], 'submitter_name', 'NONE', '1000A Submitter Contact Name', config, endpoint)
|
306
|
+
contact_telephone_number = legacy_require_config_value([config], 'submitter_tel', 'NONE', '1000A Submitter Contact Phone', config, endpoint)
|
307
307
|
|
308
308
|
# Get submitter first name to determine entity type qualifier
|
309
309
|
submitter_first_name = config.get('submitter_first_name', '')
|
@@ -333,8 +333,8 @@ def create_1000B_receiver_name_segment(config, endpoint):
|
|
333
333
|
endpoint_config = config.get('endpoints', {}).get(endpoint.upper(), {})
|
334
334
|
receiver_entity_code = '40'
|
335
335
|
receiver_id_qualifier = endpoint_config.get('receiver_id_qualifier', '46')
|
336
|
-
receiver_name =
|
337
|
-
receiver_edi =
|
336
|
+
receiver_name = legacy_require_config_value([endpoint_config], 'receiver_name', 'DEFAULT RECEIVER NAME', '1000B Receiver Name', config, endpoint)
|
337
|
+
receiver_edi = legacy_require_config_value([endpoint_config], 'receiver_edi', 'DEFAULT EDI', '1000B Receiver EDI', config, endpoint)
|
338
338
|
return "NM1*{entity_code}*2*{receiver_name}*****{id_qualifier}*{receiver_edi}~".format(
|
339
339
|
entity_code=receiver_entity_code,
|
340
340
|
receiver_name=receiver_name,
|
@@ -347,11 +347,11 @@ def create_nm1_payto_address_segments(config):
|
|
347
347
|
Constructs the NM1 segment for the Pay-To Address, N3 for street address, and N4 for city, state, and ZIP.
|
348
348
|
This is used if the Pay-To Address is different from the Billing Provider Address.
|
349
349
|
"""
|
350
|
-
payto_provider_name =
|
351
|
-
payto_address =
|
352
|
-
payto_city =
|
353
|
-
payto_state =
|
354
|
-
payto_zip =
|
350
|
+
payto_provider_name = legacy_require_config_value([config], 'payto_provider_name', 'DEFAULT PAY-TO NAME', '2010AB Pay-To Name', config)
|
351
|
+
payto_address = legacy_require_config_value([config], 'payto_address', 'DEFAULT PAY-TO ADDRESS', '2010AB Pay-To Address', config)
|
352
|
+
payto_city = legacy_require_config_value([config], 'payto_city', 'DEFAULT PAY-TO CITY', '2010AB Pay-To City', config)
|
353
|
+
payto_state = legacy_require_config_value([config], 'payto_state', 'DEFAULT PAY-TO STATE', '2010AB Pay-To State', config)
|
354
|
+
payto_zip = legacy_require_config_value([config], 'payto_zip', 'DEFAULT PAY-TO ZIP', '2010AB Pay-To ZIP', config)
|
355
355
|
|
356
356
|
nm1_segment = "NM1*87*2*{}~".format(payto_provider_name)
|
357
357
|
n3_segment = "N3*{}~".format(payto_address)
|
@@ -1528,37 +1528,47 @@ DEFAULT_PLACEHOLDER_VALUES = set([
|
|
1528
1528
|
'NO STATE', 'NO ZIP', 'NO TAX ID', 'NONE'
|
1529
1529
|
])
|
1530
1530
|
|
1531
|
-
|
1532
|
-
|
1533
|
-
|
1534
|
-
|
1535
|
-
|
1536
|
-
|
1537
|
-
|
1538
|
-
|
1539
|
-
|
1531
|
+
string_types = (str,)
|
1532
|
+
|
1533
|
+
def _get_value_from_sources(source_dicts, keys):
|
1534
|
+
# Accept a single key or an ordered list/tuple of keys; scan in priority order
|
1535
|
+
if not isinstance(keys, (list, tuple)):
|
1536
|
+
keys = [keys]
|
1537
|
+
for key in keys:
|
1538
|
+
for d in source_dicts:
|
1539
|
+
try:
|
1540
|
+
if isinstance(d, dict) and key in d:
|
1541
|
+
val = d.get(key)
|
1542
|
+
if val not in [None, '']:
|
1543
|
+
return val
|
1544
|
+
except Exception:
|
1545
|
+
pass
|
1540
1546
|
return None
|
1541
1547
|
|
1542
|
-
def
|
1548
|
+
def legacy_require_config_value(source_dicts, key, default_value, context_label, config, endpoint=None, allow_default_in_test=True):
|
1543
1549
|
"""
|
1544
1550
|
Fetch a configuration value from one or more dicts.
|
1551
|
+
- `key` may be a string or a list/tuple of keys to try in order (primary, secondary, ...).
|
1545
1552
|
- If found and equals a known placeholder, allow only in TestMode.
|
1546
1553
|
- If missing, allow default only in TestMode (when allow_default_in_test is True).
|
1547
1554
|
- Otherwise raise ValueError to prevent generating malformed 837p.
|
1555
|
+
|
1556
|
+
TODO Eventually we should get this functionality into configloader in MediCafe so that we can use it in other places.
|
1548
1557
|
"""
|
1549
1558
|
value = _get_value_from_sources(source_dicts, key)
|
1559
|
+
key_label = key if isinstance(key, string_types) else "/".join([k for k in key if isinstance(k, string_types)])
|
1550
1560
|
# Found a value
|
1551
1561
|
if value not in [None, '']:
|
1552
1562
|
if isinstance(value, str) and value in DEFAULT_PLACEHOLDER_VALUES:
|
1553
1563
|
if _is_test_mode(config):
|
1554
1564
|
try:
|
1555
|
-
MediLink_ConfigLoader.log("TEST MODE: Placeholder used for {} -> {}".format(
|
1565
|
+
MediLink_ConfigLoader.log("TEST MODE: Placeholder used for {} -> {}".format(key_label, value), level="WARNING")
|
1556
1566
|
except Exception:
|
1557
1567
|
pass
|
1558
|
-
print("TEST MODE: Using placeholder '{}' for {} ({})".format(value,
|
1568
|
+
print("TEST MODE: Using placeholder '{}' for {} ({})".format(value, key_label, context_label))
|
1559
1569
|
return value
|
1560
1570
|
else:
|
1561
|
-
msg = "Missing real value for '{}' in {}. Found placeholder '{}'. Endpoint: {}".format(
|
1571
|
+
msg = "Missing real value for '{}' in {}. Found placeholder '{}'. Endpoint: {}".format(key_label, context_label, value, (endpoint or ''))
|
1562
1572
|
try:
|
1563
1573
|
MediLink_ConfigLoader.log(msg, level="CRITICAL")
|
1564
1574
|
except Exception:
|
@@ -1568,12 +1578,12 @@ def require_config_value(source_dicts, key, default_value, context_label, config
|
|
1568
1578
|
# Missing value entirely
|
1569
1579
|
if allow_default_in_test and _is_test_mode(config):
|
1570
1580
|
try:
|
1571
|
-
MediLink_ConfigLoader.log("TEST MODE: Default used for {} -> {}".format(
|
1581
|
+
MediLink_ConfigLoader.log("TEST MODE: Default used for {} -> {}".format(key_label, default_value), level="WARNING")
|
1572
1582
|
except Exception:
|
1573
1583
|
pass
|
1574
|
-
print("TEST MODE: Using default '{}' for {} ({})".format(default_value,
|
1584
|
+
print("TEST MODE: Using default '{}' for {} ({})".format(default_value, key_label, context_label))
|
1575
1585
|
return default_value
|
1576
|
-
msg = "Required configuration '{}' missing for {}. Endpoint: {}".format(
|
1586
|
+
msg = "Required configuration '{}' missing for {}. Endpoint: {}".format(key_label, context_label, (endpoint or ''))
|
1577
1587
|
try:
|
1578
1588
|
MediLink_ConfigLoader.log(msg, level="CRITICAL")
|
1579
1589
|
except Exception:
|
@@ -34,13 +34,6 @@ import MediLink_PatientProcessor # Import patient processing functions
|
|
34
34
|
# Use core utilities for standardized config loader
|
35
35
|
MediLink_ConfigLoader = get_shared_config_loader()
|
36
36
|
|
37
|
-
try:
|
38
|
-
from tqdm import tqdm
|
39
|
-
except ImportError:
|
40
|
-
# Fallback for when tqdm is not available
|
41
|
-
def tqdm(iterable, **kwargs):
|
42
|
-
return iterable
|
43
|
-
|
44
37
|
import_time = time.time()
|
45
38
|
if PERFORMANCE_LOGGING:
|
46
39
|
print("Import phase completed in {:.2f} seconds".format(import_time - start_time))
|
@@ -92,8 +85,8 @@ def main_menu():
|
|
92
85
|
Initializes the main menu loop and handles the overall program flow,
|
93
86
|
including loading configurations and managing user input for menu selections.
|
94
87
|
"""
|
88
|
+
global _last_ack_updated_at, _scheduled_ack_checks
|
95
89
|
menu_start_time = time.time()
|
96
|
-
print("Main menu function started...")
|
97
90
|
|
98
91
|
# Load configuration settings and display the initial welcome message.
|
99
92
|
config_start_time = time.time()
|
@@ -149,6 +142,13 @@ def main_menu():
|
|
149
142
|
ack_result = False
|
150
143
|
pass
|
151
144
|
|
145
|
+
# TODO: Once we start building out the whole submission tracking persist structure,
|
146
|
+
# this boot-time scan should check when the last acknowledgement check was run
|
147
|
+
# and skip if it was run recently (e.g., within the last day) to avoid
|
148
|
+
# constantly running it on every startup. The submission tracking system should
|
149
|
+
# store the timestamp of the last successful acknowledgement check and use it
|
150
|
+
# to determine if a new scan is needed.
|
151
|
+
|
152
152
|
# Clear screen before showing menu header
|
153
153
|
try:
|
154
154
|
os.system('cls' if os.name == 'nt' else 'clear')
|
@@ -204,27 +204,7 @@ def main_menu():
|
|
204
204
|
if PERFORMANCE_LOGGING:
|
205
205
|
print("Main menu initialization completed in {:.2f} seconds".format(menu_init_end - menu_start_time))
|
206
206
|
|
207
|
-
|
208
|
-
try:
|
209
|
-
from datetime import datetime, timedelta
|
210
|
-
current_date = datetime.now()
|
211
|
-
start_date = current_date - timedelta(days=15) # Default to 15-day range
|
212
|
-
end_date = current_date - timedelta(days=1)
|
213
|
-
def validate_date_range(start_date, end_date):
|
214
|
-
if start_date > end_date:
|
215
|
-
raise ValueError("Start date cannot be after end date.")
|
216
|
-
if start_date < (current_date - timedelta(days=30)): # Ensure it's not too far in the past
|
217
|
-
raise ValueError("Start date must be within the last 30 days.")
|
218
|
-
if end_date < (current_date - timedelta(days=30)): # Ensure it's not too far in the past
|
219
|
-
raise ValueError("End date must be within the last 30 days.")
|
220
|
-
except ImportError:
|
221
|
-
print("Date validation requires the 'datetime' module. Please ensure it's installed.")
|
222
|
-
# Fallback to a safe date range within 30 days
|
223
|
-
end_date = current_date - timedelta(days=1)
|
224
|
-
start_date = end_date - timedelta(days=15) # 15-day range as fallback
|
225
|
-
|
226
|
-
end_date_str = end_date.strftime('%m/%d/%Y')
|
227
|
-
start_date_str = start_date.strftime('%m/%d/%Y')
|
207
|
+
|
228
208
|
|
229
209
|
while True:
|
230
210
|
# Run any due scheduled ack checks before showing menu
|
@@ -294,7 +274,7 @@ def main_menu():
|
|
294
274
|
|
295
275
|
# UX hint: suggest deeper United details
|
296
276
|
try:
|
297
|
-
print("Tip: For United details, run the United Claims Status
|
277
|
+
print("Tip: For United details, run the United Claims Status checker.")
|
298
278
|
except Exception:
|
299
279
|
pass
|
300
280
|
elif choice == '2':
|
@@ -335,20 +315,6 @@ def main_menu():
|
|
335
315
|
break
|
336
316
|
elif choice == '4':
|
337
317
|
_tools_menu(config, medi)
|
338
|
-
elif choice.lower() == 'tools:index':
|
339
|
-
# Optional maintenance: rebuild submission index now (synchronous)
|
340
|
-
try:
|
341
|
-
receipts_root = medi.get('local_claims_path', None)
|
342
|
-
if not receipts_root:
|
343
|
-
print("No receipts folder configured.")
|
344
|
-
continue
|
345
|
-
from MediCafe.submission_index import build_initial_index
|
346
|
-
receipts_root = os.path.normpath(receipts_root)
|
347
|
-
print("Rebuilding submission index... (this may take a while)")
|
348
|
-
count = build_initial_index(receipts_root)
|
349
|
-
print("Index rebuild complete. Indexed {} records.".format(count))
|
350
|
-
except Exception as e:
|
351
|
-
print("Index rebuild error: {}".format(e))
|
352
318
|
else:
|
353
319
|
# Display an error message if the user's choice does not match any valid option.
|
354
320
|
MediLink_UI.display_invalid_choice()
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|