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.

Files changed (58) hide show
  1. MediBot/MediBot.bat +233 -19
  2. MediBot/MediBot.py +138 -46
  3. MediBot/MediBot_Crosswalk_Library.py +127 -623
  4. MediBot/MediBot_Crosswalk_Utils.py +618 -0
  5. MediBot/MediBot_Preprocessor.py +72 -17
  6. MediBot/MediBot_Preprocessor_lib.py +470 -76
  7. MediBot/MediBot_UI.py +32 -17
  8. MediBot/MediBot_dataformat_library.py +68 -20
  9. MediBot/MediBot_docx_decoder.py +120 -19
  10. MediBot/MediBot_smart_import.py +180 -0
  11. MediBot/__init__.py +89 -0
  12. MediBot/get_medicafe_version.py +25 -0
  13. MediBot/update_json.py +35 -6
  14. MediBot/update_medicafe.py +19 -1
  15. MediCafe/MediLink_ConfigLoader.py +160 -0
  16. MediCafe/__init__.py +171 -0
  17. MediCafe/__main__.py +222 -0
  18. MediCafe/api_core.py +1098 -0
  19. MediCafe/api_core_backup.py +427 -0
  20. MediCafe/api_factory.py +306 -0
  21. MediCafe/api_utils.py +356 -0
  22. MediCafe/core_utils.py +450 -0
  23. MediCafe/graphql_utils.py +445 -0
  24. MediCafe/logging_config.py +123 -0
  25. MediCafe/logging_demo.py +61 -0
  26. MediCafe/migration_helpers.py +463 -0
  27. MediCafe/smart_import.py +436 -0
  28. MediLink/MediLink.py +66 -26
  29. MediLink/MediLink_837p_cob_library.py +28 -28
  30. MediLink/MediLink_837p_encoder.py +33 -34
  31. MediLink/MediLink_837p_encoder_library.py +243 -151
  32. MediLink/MediLink_837p_utilities.py +129 -5
  33. MediLink/MediLink_API_Generator.py +83 -60
  34. MediLink/MediLink_API_v3.py +1 -1
  35. MediLink/MediLink_ClaimStatus.py +177 -31
  36. MediLink/MediLink_DataMgmt.py +405 -72
  37. MediLink/MediLink_Decoder.py +20 -1
  38. MediLink/MediLink_Deductible.py +155 -28
  39. MediLink/MediLink_Display_Utils.py +72 -0
  40. MediLink/MediLink_Down.py +127 -5
  41. MediLink/MediLink_Gmail.py +712 -653
  42. MediLink/MediLink_PatientProcessor.py +257 -0
  43. MediLink/MediLink_UI.py +85 -61
  44. MediLink/MediLink_Up.py +28 -4
  45. MediLink/MediLink_insurance_utils.py +227 -264
  46. MediLink/MediLink_main.py +248 -0
  47. MediLink/MediLink_smart_import.py +264 -0
  48. MediLink/__init__.py +93 -0
  49. MediLink/insurance_type_integration_test.py +66 -76
  50. MediLink/test.py +1 -1
  51. MediLink/test_timing.py +59 -0
  52. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/METADATA +1 -1
  53. medicafe-0.250805.0.dist-info/RECORD +81 -0
  54. medicafe-0.250805.0.dist-info/entry_points.txt +2 -0
  55. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/top_level.txt +1 -0
  56. medicafe-0.250728.8.dist-info/RECORD +0 -59
  57. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/LICENSE +0 -0
  58. {medicafe-0.250728.8.dist-info → medicafe-0.250805.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,436 @@
1
+ """
2
+ MediCafe Smart Import System
3
+
4
+ This module provides intelligent import management for the entire MediCafe ecosystem,
5
+ allowing MediBot and MediLink modules to request components without worrying about
6
+ circular imports or complex dependency management.
7
+
8
+ Usage Examples:
9
+ # Simple component requests
10
+ from MediCafe.smart_import import get_components
11
+ api_core, logging_config = get_components('api_core', 'logging_config')
12
+
13
+ # Get everything for a specific module type
14
+ from MediCafe.smart_import import setup_for_medibot
15
+ components = setup_for_medibot('preprocessor')
16
+
17
+ # Use components
18
+ api_core = components['api_core']
19
+ logging = components['logging_config']
20
+
21
+ # Get API suite for any module
22
+ from MediCafe.smart_import import get_api_access
23
+ api_suite = get_api_access()
24
+ """
25
+
26
+ import sys
27
+ import importlib
28
+ import inspect
29
+ # from typing import Any, Dict, List, Optional, Union, Tuple, Set # Removed for Python 3.4.4 compatibility
30
+ # from functools import lru_cache # Removed as not used
31
+ import threading
32
+ # from pathlib import Path # Replaced with os.path for Python 3.4.4 compatibility
33
+ import os
34
+ import warnings
35
+
36
+ # Define typing aliases for Python 3.4.4 compatibility
37
+ try:
38
+ from typing import Any, Dict, List, Optional, Union, Tuple, Set
39
+ except ImportError:
40
+ # Python 3.4.4 compatibility - define as object
41
+ Any = object
42
+ Dict = dict
43
+ List = list
44
+ Optional = object
45
+ Union = object
46
+ Tuple = tuple
47
+ Set = set
48
+
49
+ class ComponentRegistry:
50
+ """Registry for managing all available components and their dependencies."""
51
+
52
+ def __init__(self):
53
+ self._components = {}
54
+ self._modules = {}
55
+ self._dependencies = {}
56
+ self._loading_stack = []
57
+ self._lock = threading.RLock()
58
+ self._module_configs = {}
59
+ self._failed_loads = set()
60
+
61
+ def register_component(self, name, component, dependencies=None):
62
+ """Register a component with optional dependencies."""
63
+ with self._lock:
64
+ self._components[name] = component
65
+ self._dependencies[name] = dependencies or []
66
+
67
+ def register_module_config(self, module_name, config):
68
+ """Register configuration for a specific module's needs."""
69
+ self._module_configs[module_name] = config
70
+
71
+ def get_component(self, name, silent_fail=False):
72
+ """Get a component, loading it if necessary."""
73
+ with self._lock:
74
+ # Return cached component if available
75
+ if name in self._components:
76
+ return self._components[name]
77
+
78
+ # Skip if previously failed and silent_fail is True
79
+ if silent_fail and name in self._failed_loads:
80
+ return None
81
+
82
+ # Check for circular imports
83
+ if name in self._loading_stack:
84
+ error_msg = "Circular import detected: {} -> {}".format(' -> '.join(self._loading_stack), name)
85
+ if silent_fail:
86
+ warnings.warn(error_msg)
87
+ return None
88
+ raise ImportError(error_msg)
89
+
90
+ self._loading_stack.append(name)
91
+ try:
92
+ component = self._load_component(name)
93
+ self._components[name] = component
94
+ return component
95
+ except ImportError as e:
96
+ self._failed_loads.add(name)
97
+ if silent_fail:
98
+ return None
99
+ raise e
100
+ finally:
101
+ if name in self._loading_stack:
102
+ self._loading_stack.remove(name)
103
+
104
+ def _load_component(self, name: str) -> Any:
105
+ """Load a component dynamically."""
106
+ # Component loading mappings
107
+ component_mappings = {
108
+ # MediCafe core components
109
+ 'api_core': 'MediCafe.api_core',
110
+ 'api_factory': 'MediCafe.api_factory',
111
+ 'api_utils': 'MediCafe.api_utils',
112
+ 'core_utils': 'MediCafe.core_utils',
113
+ 'graphql_utils': 'MediCafe.graphql_utils',
114
+ 'logging_config': 'MediCafe.logging_config',
115
+ 'medicafe_config_loader': 'MediCafe.MediLink_ConfigLoader',
116
+
117
+ # MediBot components
118
+ 'medibot_main': 'MediBot.MediBot',
119
+ 'medibot_preprocessor': 'MediBot.MediBot_Preprocessor',
120
+ 'medibot_preprocessor_lib': 'MediBot.MediBot_Preprocessor_lib',
121
+ 'medibot_crosswalk_library': 'MediBot.MediBot_Crosswalk_Library',
122
+ 'medibot_crosswalk_utils': 'MediBot.MediBot_Crosswalk_Utils',
123
+ 'medibot_dataformat_library': 'MediBot.MediBot_dataformat_library',
124
+ 'medibot_docx_decoder': 'MediBot.MediBot_docx_decoder',
125
+ 'medibot_ui': 'MediBot.MediBot_UI',
126
+ 'medibot_charges': 'MediBot.MediBot_Charges',
127
+ 'medibot_post': 'MediBot.MediBot_Post',
128
+ 'get_medicafe_version': 'MediBot.get_medicafe_version',
129
+ 'update_medicafe': 'MediBot.update_medicafe',
130
+ 'update_json': 'MediBot.update_json',
131
+
132
+ # MediLink components
133
+ 'medilink_main': 'MediLink.MediLink_main',
134
+ 'medilink_azure': 'MediLink.MediLink_Azure',
135
+ 'medilink_claim_status': 'MediLink.MediLink_ClaimStatus',
136
+ 'medilink_datamgmt': 'MediLink.MediLink_DataMgmt',
137
+ 'medilink_decoder': 'MediLink.MediLink_Decoder',
138
+ 'medilink_deductible': 'MediLink.MediLink_Deductible',
139
+ 'medilink_deductible_validator': 'MediLink.MediLink_Deductible_Validator',
140
+ 'medilink_display_utils': 'MediLink.MediLink_Display_Utils',
141
+ 'medilink_down': 'MediLink.MediLink_Down',
142
+ 'medilink_gmail': 'MediLink.MediLink_Gmail',
143
+ 'medilink_mailer': 'MediLink.MediLink_Mailer',
144
+ 'medilink_parser': 'MediLink.MediLink_Parser',
145
+ 'medilink_patient_processor': 'MediLink.MediLink_PatientProcessor',
146
+ 'medilink_scan': 'MediLink.MediLink_Scan',
147
+ 'medilink_scheduler': 'MediLink.MediLink_Scheduler',
148
+ 'medilink_ui': 'MediLink.MediLink_UI',
149
+ 'medilink_up': 'MediLink.MediLink_Up',
150
+ 'medilink_insurance_utils': 'MediLink.MediLink_insurance_utils',
151
+ 'medilink_837p_cob_library': 'MediLink.MediLink_837p_cob_library',
152
+ 'medilink_837p_encoder': 'MediLink.MediLink_837p_encoder',
153
+ 'medilink_837p_encoder_library': 'MediLink.MediLink_837p_encoder_library',
154
+ 'medilink_837p_utilities': 'MediLink.MediLink_837p_utilities',
155
+ 'medilink_api_generator': 'MediLink.MediLink_API_Generator',
156
+ 'soumit_api': 'MediLink.Soumit_api',
157
+ }
158
+
159
+ if name not in component_mappings:
160
+ raise ImportError("Unknown component: {}. Available components: {}".format(name, list(component_mappings.keys())))
161
+
162
+ module_path = component_mappings[name]
163
+ try:
164
+ module = importlib.import_module(module_path)
165
+ return module
166
+ except ImportError as e:
167
+ raise ImportError("Failed to load component '{}' from '{}': {}".format(name, module_path, e))
168
+
169
+ # Global registry instance
170
+ _registry = ComponentRegistry()
171
+
172
+ class ComponentProvider:
173
+ """Main interface for requesting components."""
174
+
175
+ def __init__(self, registry: ComponentRegistry):
176
+ self.registry = registry
177
+ self._setup_module_configs()
178
+
179
+ def _setup_module_configs(self):
180
+ """Setup predefined configurations for known module types."""
181
+ configs = {
182
+ # MediBot configurations
183
+ 'medibot_preprocessor': {
184
+ 'core_dependencies': ['api_core', 'logging_config', 'core_utils'],
185
+ 'optional_dependencies': ['api_factory', 'api_utils'],
186
+ 'shared_resources': ['medibot_dataformat_library', 'medibot_crosswalk_utils']
187
+ },
188
+ 'medibot_ui': {
189
+ 'core_dependencies': ['medibot_main', 'logging_config'],
190
+ 'optional_dependencies': ['core_utils', 'api_core'],
191
+ 'shared_resources': ['medibot_preprocessor', 'medibot_crosswalk_library']
192
+ },
193
+ 'medibot_crosswalk': {
194
+ 'core_dependencies': ['logging_config', 'core_utils'],
195
+ 'optional_dependencies': ['api_core'],
196
+ 'shared_resources': ['medibot_crosswalk_library', 'medibot_crosswalk_utils', 'medibot_dataformat_library']
197
+ },
198
+ 'medibot_document_processing': {
199
+ 'core_dependencies': ['logging_config', 'core_utils'],
200
+ 'optional_dependencies': ['api_core'],
201
+ 'shared_resources': ['medibot_docx_decoder', 'medibot_preprocessor_lib']
202
+ },
203
+
204
+ # MediLink configurations
205
+ 'medilink_main': {
206
+ 'core_dependencies': ['api_core', 'logging_config', 'core_utils'],
207
+ 'optional_dependencies': ['api_factory', 'graphql_utils'],
208
+ 'shared_resources': ['medilink_datamgmt', 'medilink_parser']
209
+ },
210
+ 'medilink_claim_processing': {
211
+ 'core_dependencies': ['api_core', 'logging_config', 'medilink_datamgmt'],
212
+ 'optional_dependencies': ['graphql_utils', 'api_utils'],
213
+ 'shared_resources': ['medilink_837p_encoder', 'medilink_837p_utilities', 'medilink_claim_status']
214
+ },
215
+ 'medilink_deductible_processing': {
216
+ 'core_dependencies': ['api_core', 'logging_config', 'medilink_datamgmt'],
217
+ 'optional_dependencies': ['graphql_utils'],
218
+ 'shared_resources': ['medilink_deductible', 'medilink_deductible_validator', 'medilink_insurance_utils']
219
+ },
220
+ 'medilink_communication': {
221
+ 'core_dependencies': ['logging_config', 'core_utils'],
222
+ 'optional_dependencies': ['api_core'],
223
+ 'shared_resources': ['medilink_gmail', 'medilink_mailer', 'medilink_display_utils']
224
+ },
225
+ 'medilink_data_management': {
226
+ 'core_dependencies': ['api_core', 'logging_config'],
227
+ 'optional_dependencies': ['graphql_utils'],
228
+ 'shared_resources': ['medilink_datamgmt', 'medilink_up', 'medilink_down', 'medilink_parser']
229
+ },
230
+
231
+ # General configurations
232
+ 'general_api_access': {
233
+ 'core_dependencies': ['api_core', 'api_factory'],
234
+ 'optional_dependencies': ['api_utils', 'graphql_utils'],
235
+ 'shared_resources': ['logging_config', 'core_utils']
236
+ },
237
+ 'logging_only': {
238
+ 'core_dependencies': ['logging_config'],
239
+ 'optional_dependencies': [],
240
+ 'shared_resources': []
241
+ },
242
+ 'utilities_only': {
243
+ 'core_dependencies': ['core_utils'],
244
+ 'optional_dependencies': ['api_utils'],
245
+ 'shared_resources': ['logging_config']
246
+ }
247
+ }
248
+
249
+ for module_name, config in configs.items():
250
+ self.registry.register_module_config(module_name, config)
251
+
252
+ def __call__(self, *component_names, **kwargs):
253
+ """Get one or more components by name."""
254
+ silent_fail = kwargs.get('silent_fail', False)
255
+ if len(component_names) == 1:
256
+ return self.registry.get_component(component_names[0], silent_fail=silent_fail)
257
+
258
+ results = []
259
+ for name in component_names:
260
+ component = self.registry.get_component(name, silent_fail=silent_fail)
261
+ results.append(component)
262
+ return tuple(results)
263
+
264
+ def for_module(self, module_type: str, silent_fail: bool = False) -> Dict[str, Any]:
265
+ """Get all components needed for a specific module type."""
266
+ if module_type not in self.registry._module_configs:
267
+ available_types = list(self.registry._module_configs.keys())
268
+ raise ValueError("Unknown module type: {}. Available types: {}".format(module_type, available_types))
269
+
270
+ config = self.registry._module_configs[module_type]
271
+ components = {}
272
+
273
+ # Load core dependencies (these must succeed unless silent_fail is True)
274
+ for dep in config.get('core_dependencies', []):
275
+ component = self.registry.get_component(dep, silent_fail=silent_fail)
276
+ if component is not None:
277
+ components[dep] = component
278
+
279
+ # Load optional dependencies (always fail silently)
280
+ for dep in config.get('optional_dependencies', []):
281
+ try:
282
+ component = self.registry.get_component(dep, silent_fail=True)
283
+ if component is not None:
284
+ components[dep] = component
285
+ except ImportError:
286
+ pass
287
+
288
+ # Load shared resources (always fail silently)
289
+ for dep in config.get('shared_resources', []):
290
+ try:
291
+ component = self.registry.get_component(dep, silent_fail=True)
292
+ if component is not None:
293
+ components[dep] = component
294
+ except ImportError:
295
+ pass
296
+
297
+ return components
298
+
299
+ def get_api_suite(self) -> Dict[str, Any]:
300
+ """Get the complete API access suite."""
301
+ return self.for_module('general_api_access')
302
+
303
+ def get_medibot_suite(self, module_type: str = 'medibot_preprocessor') -> Dict[str, Any]:
304
+ """Get components specifically for MediBot operations."""
305
+ return self.for_module(module_type)
306
+
307
+ def get_medilink_suite(self, module_type: str = 'medilink_main') -> Dict[str, Any]:
308
+ """Get components specifically for MediLink operations."""
309
+ return self.for_module(module_type)
310
+
311
+ def list_available_components(self) -> List[str]:
312
+ """List all available component names."""
313
+ # Get component mappings from registry's _load_component method
314
+ component_mappings = {
315
+ 'api_core', 'api_factory', 'api_utils', 'core_utils', 'graphql_utils', 'logging_config',
316
+ 'medicafe_config_loader', 'medibot_main', 'medibot_preprocessor', 'medibot_preprocessor_lib',
317
+ 'medibot_crosswalk_library', 'medibot_crosswalk_utils', 'medibot_dataformat_library',
318
+ 'medibot_docx_decoder', 'medibot_ui', 'medibot_charges', 'medibot_post',
319
+ 'get_medicafe_version', 'update_medicafe', 'update_json', 'medilink_main',
320
+ 'medilink_azure', 'medilink_claim_status', 'medilink_datamgmt', 'medilink_decoder',
321
+ 'medilink_deductible', 'medilink_deductible_validator', 'medilink_display_utils',
322
+ 'medilink_down', 'medilink_gmail', 'medilink_mailer', 'medilink_parser',
323
+ 'medilink_patient_processor', 'medilink_scan', 'medilink_scheduler', 'medilink_ui',
324
+ 'medilink_up', 'medilink_insurance_utils', 'medilink_837p_cob_library',
325
+ 'medilink_837p_encoder', 'medilink_837p_encoder_library', 'medilink_837p_utilities',
326
+ 'medilink_api_generator', 'soumit_api'
327
+ }
328
+ return sorted(list(component_mappings))
329
+
330
+ def list_available_module_types(self) -> List[str]:
331
+ """List all available module type configurations."""
332
+ return sorted(list(self.registry._module_configs.keys()))
333
+
334
+ # Global component provider
335
+ get_components = ComponentProvider(_registry)
336
+
337
+ # Convenience functions for common use cases
338
+ def get_api_access():
339
+ """Get standard API access components."""
340
+ return get_components.get_api_suite()
341
+
342
+ def get_logging():
343
+ """Get logging configuration."""
344
+ return get_components('logging_config')
345
+
346
+ def get_core_utils():
347
+ """Get core utilities."""
348
+ return get_components('core_utils')
349
+
350
+ def setup_for_medibot(module_type: str = 'medibot_preprocessor'):
351
+ """Setup everything needed for MediBot operations."""
352
+ return get_components.get_medibot_suite(module_type)
353
+
354
+ def setup_for_medilink(module_type: str = 'medilink_main'):
355
+ """Setup everything needed for MediLink operations."""
356
+ return get_components.get_medilink_suite(module_type)
357
+
358
+ # Helper functions for discovery
359
+ def list_components():
360
+ """List all available component names."""
361
+ return get_components.list_available_components()
362
+
363
+ def list_module_types():
364
+ """List all available module type configurations."""
365
+ return get_components.list_available_module_types()
366
+
367
+ def describe_module_type(module_type: str):
368
+ """Describe what components a module type provides."""
369
+ if module_type in _registry._module_configs:
370
+ config = _registry._module_configs[module_type]
371
+ print("Module Type: {}".format(module_type))
372
+ print("Core Dependencies: {}".format(config.get('core_dependencies', [])))
373
+ print("Optional Dependencies: {}".format(config.get('optional_dependencies', [])))
374
+ print("Shared Resources: {}".format(config.get('shared_resources', [])))
375
+ else:
376
+ print("Unknown module type: {}".format(module_type))
377
+ print("Available types: {}".format(list_module_types()))
378
+
379
+ # Module initialization and validation
380
+ def validate_setup():
381
+ """Validate that the import system is working correctly."""
382
+ try:
383
+ # Test core components
384
+ logging_config = get_components('logging_config')
385
+ core_utils = get_components('core_utils')
386
+
387
+ print("[+] MediCafe Smart Import System initialized successfully")
388
+ print("[+] Available components: {}".format(len(list_components())))
389
+ print("[+] Available module types: {}".format(len(list_module_types())))
390
+ return True
391
+ except Exception as e:
392
+ print("[-] MediCafe Smart Import System validation failed: {}".format(e))
393
+ return False
394
+
395
+ # Advanced usage examples and documentation
396
+ def show_usage_examples():
397
+ """Show usage examples for the smart import system."""
398
+ examples = """
399
+ MediCafe Smart Import System - Usage Examples
400
+
401
+ 1. Basic component import:
402
+ from MediCafe.smart_import import get_components
403
+ api_core, logging_config = get_components('api_core', 'logging_config')
404
+
405
+ 2. Setup for MediBot:
406
+ from MediCafe.smart_import import setup_for_medibot
407
+ components = setup_for_medibot('medibot_preprocessor')
408
+ api_core = components['api_core']
409
+
410
+ 3. Setup for MediLink:
411
+ from MediCafe.smart_import import setup_for_medilink
412
+ components = setup_for_medilink('medilink_claim_processing')
413
+ datamgmt = components['medilink_datamgmt']
414
+
415
+ 4. Get API access suite:
416
+ from MediCafe.smart_import import get_api_access
417
+ api_suite = get_api_access()
418
+ api_core = api_suite['api_core']
419
+ api_factory = api_suite['api_factory']
420
+
421
+ 5. Discovery functions:
422
+ from MediCafe.smart_import import list_components, list_module_types
423
+ print("Available components:", list_components())
424
+ print("Available module types:", list_module_types())
425
+ """
426
+ print(examples)
427
+
428
+ # Auto-validation on import (silent)
429
+ if __name__ != '__main__':
430
+ try:
431
+ # Silent validation - don't print success messages on import
432
+ logging_config = get_components('logging_config', silent_fail=True)
433
+ if logging_config:
434
+ pass # Successfully validated
435
+ except:
436
+ pass # Silent fail on import
MediLink/MediLink.py CHANGED
@@ -33,17 +33,25 @@ def collect_detailed_patient_data(selected_files, config, crosswalk):
33
33
  """
34
34
  Collects detailed patient data from the selected files.
35
35
 
36
+ DATA FLOW CLARIFICATION:
37
+ This function processes fixed-width files through extract_and_suggest_endpoint(),
38
+ which creates data structures with 'patient_id' field (sourced from 'CHART' field).
39
+ This is DIFFERENT from MediBot's parse_z_dat() flow which uses 'PATID' field.
40
+
36
41
  :param selected_files: List of selected file paths.
37
42
  :param config: Configuration settings loaded from a JSON file.
38
43
  :param crosswalk: Crosswalk data for mapping purposes.
39
- :return: A list of detailed patient data.
44
+ :return: A list of detailed patient data with 'patient_id' field populated.
40
45
  """
41
46
  detailed_patient_data = []
42
47
  for file_path in selected_files:
48
+ # IMPORTANT: extract_and_suggest_endpoint creates data with 'patient_id' field
49
+ # sourced from the 'CHART' field in fixed-width files
43
50
  detailed_data = extract_and_suggest_endpoint(file_path, config, crosswalk)
44
51
  detailed_patient_data.extend(detailed_data) # Accumulate detailed data for processing
45
52
 
46
53
  # Enrich the detailed patient data with insurance type
54
+ # NOTE: This receives data from extract_and_suggest_endpoint with 'patient_id' field
47
55
  detailed_patient_data = enrich_with_insurance_type(detailed_patient_data, insurance_options)
48
56
 
49
57
  # Display summaries and provide an option for bulk edit
@@ -56,8 +64,16 @@ def enrich_with_insurance_type(detailed_patient_data, patient_insurance_type_map
56
64
  Enriches the detailed patient data with insurance type based on patient ID.
57
65
  Enhanced with optional API integration and comprehensive logging.
58
66
 
67
+ DATA FLOW CLARIFICATION:
68
+ This function receives data from collect_detailed_patient_data() -> extract_and_suggest_endpoint().
69
+ The patient ID field is 'patient_id' (NOT 'PATID').
70
+
71
+ IMPORTANT: Do not confuse with MediBot's parse_z_dat() flow which uses 'PATID'.
72
+ MediLink flow: fixed-width files -> extract_and_suggest_endpoint() -> 'patient_id' field (from 'CHART')
73
+ MediBot flow: Z.dat files -> parse_z_dat() -> 'PATID' field
74
+
59
75
  Parameters:
60
- - detailed_patient_data: List of dictionaries containing detailed patient data.
76
+ - detailed_patient_data: List of dictionaries containing detailed patient data with 'patient_id' field.
61
77
  - patient_insurance_mapping: Dictionary mapping patient IDs to their insurance types.
62
78
 
63
79
  Returns:
@@ -70,48 +86,60 @@ def enrich_with_insurance_type(detailed_patient_data, patient_insurance_type_map
70
86
  UHC and which ones are not yet supported so they know which ones they need to edit. It is possible that we may want to isolate the
71
87
  patient data that is already pulled from UHC so that the user can see which ones are already using the enriched data.
72
88
  """
73
- # Import enhancement utilities
89
+ # Enhanced mode check with graceful degradation
90
+ enhanced_mode = False
91
+
74
92
  try:
75
93
  from MediLink_insurance_utils import (
76
94
  get_feature_flag,
77
- enrich_patient_data_with_metadata,
78
- generate_insurance_assignment_summary,
79
95
  validate_insurance_type_from_config
80
96
  )
81
97
  enhanced_mode = get_feature_flag('enhanced_insurance_enrichment', default=False)
82
- except ImportError:
83
- MediLink_ConfigLoader.log("Insurance utils not available, using legacy mode", level="DEBUG")
98
+ MediLink_ConfigLoader.log("Insurance enhancement utilities loaded successfully", level="DEBUG")
99
+ except ImportError as e:
100
+ MediLink_ConfigLoader.log("Insurance utils not available: {}. Using legacy mode.".format(str(e)), level="INFO")
101
+ enhanced_mode = False
102
+ except Exception as e:
103
+ MediLink_ConfigLoader.log("Error initializing insurance enhancements: {}. Using legacy mode.".format(str(e)), level="ERROR")
84
104
  enhanced_mode = False
85
105
 
86
106
  if patient_insurance_type_mapping is None:
87
- MediLink_ConfigLoader.log("No Patient:Insurance-Type mapping available.", level="WARNING")
107
+ MediLink_ConfigLoader.log("No Patient:Insurance-Type mapping available.", level="INFO")
88
108
  patient_insurance_type_mapping = {}
89
109
 
90
- # Enhanced mode with metadata and validation
110
+ # Enhanced mode with validation
91
111
  if enhanced_mode:
92
112
  MediLink_ConfigLoader.log("Using enhanced insurance type enrichment", level="INFO")
93
113
 
94
114
  for data in detailed_patient_data:
95
- patient_id = data.get('PATID') # I think this is the right name?
96
-
97
- # Get insurance type with validation
98
- raw_insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO)
99
- validated_insurance_type = validate_insurance_type_from_config(raw_insurance_type, patient_id)
100
-
101
- # Add enhanced metadata
102
- data['insurance_type'] = validated_insurance_type
103
- data['insurance_type_source'] = 'MANUAL' if patient_id in patient_insurance_type_mapping else 'DEFAULT'
104
- data['insurance_type_validated'] = True
105
- data['insurance_type_timestamp'] = time.time()
106
-
107
- # Generate summary statistics
108
- generate_insurance_assignment_summary(detailed_patient_data)
115
+ # FIELD NAME CLARIFICATION: Use 'patient_id' field created by extract_and_suggest_endpoint()
116
+ # This field contains the value from the 'CHART' field in the original fixed-width file
117
+ patient_id = data.get('patient_id')
118
+ if patient_id:
119
+ raw_insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO/SBR09)
120
+ validated_insurance_type = validate_insurance_type_from_config(raw_insurance_type, patient_id)
121
+ data['insurance_type'] = validated_insurance_type
122
+ data['insurance_type_source'] = 'MANUAL' if patient_id in patient_insurance_type_mapping else 'DEFAULT'
123
+ else:
124
+ # Handle case where patient_id is missing or empty
125
+ MediLink_ConfigLoader.log("No patient_id found in data record", level="WARNING")
126
+ data['insurance_type'] = '12' # SBR09 default PPO
127
+ data['insurance_type_source'] = 'DEFAULT_FALLBACK'
109
128
 
110
129
  else:
111
130
  # Legacy mode (preserve existing behavior exactly)
131
+ MediLink_ConfigLoader.log("Using legacy insurance type enrichment", level="INFO")
112
132
  for data in detailed_patient_data:
113
- patient_id = data.get('PATID') # I think this is the right name?
114
- insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO)
133
+ # FIELD NAME CLARIFICATION: Use 'patient_id' field created by extract_and_suggest_endpoint()
134
+ # This field contains the value from the 'CHART' field in the original fixed-width file
135
+ patient_id = data.get('patient_id')
136
+ if patient_id:
137
+ insurance_type = patient_insurance_type_mapping.get(patient_id, '12') # Default to '12' (PPO/SBR09)
138
+ else:
139
+ # Handle case where patient_id is missing or empty
140
+ MediLink_ConfigLoader.log("No patient_id found in data record", level="WARNING")
141
+ insurance_type = '12' # Default when no patient ID available
142
+
115
143
  data['insurance_type'] = insurance_type
116
144
 
117
145
  return detailed_patient_data
@@ -123,6 +151,16 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
123
151
  an endpoint based on insurance provider information found in the crosswalk and prepares
124
152
  detailed patient data for processing.
125
153
 
154
+ DATA FLOW CLARIFICATION:
155
+ This function is the PRIMARY data source for MediLink patient processing.
156
+ It creates the 'patient_id' field by extracting the 'CHART' field from fixed-width files.
157
+
158
+ IMPORTANT: This is DIFFERENT from MediBot's parse_z_dat() which extracts 'PATID'.
159
+
160
+ Field mapping for MediLink flow:
161
+ - Fixed-width file 'CHART' field -> detailed_data['patient_id']
162
+ - This 'patient_id' is then used by enrich_with_insurance_type()
163
+
126
164
  Parameters:
127
165
  - file_path: Path to the fixed-width file.
128
166
  - crosswalk: Crosswalk dictionary loaded from a JSON file.
@@ -177,7 +215,9 @@ def extract_and_suggest_endpoint(file_path, config, crosswalk):
177
215
  detailed_data = parsed_data.copy() # Copy parsed_data to avoid modifying the original dictionary
178
216
  detailed_data.update({
179
217
  'file_path': file_path,
180
- 'patient_id': parsed_data.get('CHART'),
218
+ # CRITICAL FIELD MAPPING: 'CHART' field from fixed-width file becomes 'patient_id'
219
+ # This is the field that enrich_with_insurance_type() will use
220
+ 'patient_id': parsed_data.get('CHART'), # ← This is the key field mapping for MediLink flow
181
221
  'surgery_date': parsed_data.get('DATE'),
182
222
  'patient_name': ' '.join([parsed_data.get(key, '') for key in ['FIRST', 'MIDDLE', 'LAST']]),
183
223
  'amount': parsed_data.get('AMOUNT'),