medicafe 0.250728.7__py3-none-any.whl → 0.250728.9__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.

@@ -0,0 +1,305 @@
1
+ # MediLink_api_utils.py
2
+ # Enhanced API utilities for circuit breaker, caching, and rate limiting
3
+ # Extracted from enhanced implementations for safe production integration
4
+ # Python 3.4.4 compatible implementation
5
+
6
+ import time
7
+
8
+ try:
9
+ from MediLink import MediLink_ConfigLoader
10
+ except ImportError:
11
+ import MediLink_ConfigLoader
12
+
13
+ # Circuit breaker pattern for API resilience
14
+ class APICircuitBreaker:
15
+ """Circuit breaker pattern for API resilience"""
16
+ def __init__(self, failure_threshold=5, timeout=300):
17
+ self.failure_count = 0
18
+ self.failure_threshold = failure_threshold
19
+ self.timeout = timeout
20
+ self.last_failure_time = None
21
+ self.state = 'CLOSED' # CLOSED, OPEN, HALF_OPEN
22
+
23
+ def can_execute(self):
24
+ """Check if circuit breaker allows execution"""
25
+ if self.state == 'OPEN':
26
+ if time.time() - self.last_failure_time > self.timeout:
27
+ self.state = 'HALF_OPEN'
28
+ MediLink_ConfigLoader.log("Circuit breaker moving to HALF_OPEN state", level="INFO")
29
+ return True
30
+ else:
31
+ return False
32
+ return True
33
+
34
+ def record_success(self):
35
+ """Record successful API call"""
36
+ if self.state == 'HALF_OPEN':
37
+ self.state = 'CLOSED'
38
+ self.failure_count = 0
39
+ MediLink_ConfigLoader.log("Circuit breaker reset to CLOSED state", level="INFO")
40
+
41
+ def record_failure(self):
42
+ """Record failed API call"""
43
+ self.failure_count += 1
44
+ self.last_failure_time = time.time()
45
+ if self.failure_count >= self.failure_threshold:
46
+ self.state = 'OPEN'
47
+ MediLink_ConfigLoader.log("Circuit breaker OPENED due to {} failures".format(self.failure_count), level="ERROR")
48
+
49
+ def call_with_breaker(self, api_function, *args, **kwargs):
50
+ """Call API function with circuit breaker protection"""
51
+ if not self.can_execute():
52
+ raise Exception("API circuit breaker is OPEN - too many failures")
53
+
54
+ try:
55
+ result = api_function(*args, **kwargs)
56
+ self.record_success()
57
+ return result
58
+ except Exception as e:
59
+ self.record_failure()
60
+ raise e
61
+
62
+ # Cache for API responses to reduce redundant calls
63
+ class APICache:
64
+ """Simple time-based cache for API responses"""
65
+ def __init__(self, cache_duration=3600): # 1 hour cache
66
+ self.cache = {}
67
+ self.cache_duration = cache_duration
68
+
69
+ def _generate_cache_key(self, *args, **kwargs):
70
+ """Generate cache key from function arguments"""
71
+ key_parts = []
72
+ for arg in args:
73
+ key_parts.append(str(arg))
74
+ for k, v in sorted(kwargs.items()):
75
+ key_parts.append("{}:{}".format(k, v))
76
+ return "|".join(key_parts)
77
+
78
+ def get(self, *args, **kwargs):
79
+ """Get cached result if available and not expired"""
80
+ cache_key = self._generate_cache_key(*args, **kwargs)
81
+ if cache_key in self.cache:
82
+ cached_data = self.cache[cache_key]
83
+ if time.time() - cached_data['timestamp'] < self.cache_duration:
84
+ MediLink_ConfigLoader.log("Cache hit for key: {}".format(cache_key[:50]), level="DEBUG")
85
+ return cached_data['result']
86
+ return None
87
+
88
+ def set(self, result, *args, **kwargs):
89
+ """Cache result"""
90
+ cache_key = self._generate_cache_key(*args, **kwargs)
91
+ self.cache[cache_key] = {
92
+ 'result': result,
93
+ 'timestamp': time.time()
94
+ }
95
+ MediLink_ConfigLoader.log("Cached result for key: {}".format(cache_key[:50]), level="DEBUG")
96
+
97
+ def clear_expired(self):
98
+ """Remove expired cache entries"""
99
+ now = time.time()
100
+ expired_keys = []
101
+ for key, data in self.cache.items():
102
+ if now - data['timestamp'] >= self.cache_duration:
103
+ expired_keys.append(key)
104
+
105
+ for key in expired_keys:
106
+ del self.cache[key]
107
+
108
+ if expired_keys:
109
+ MediLink_ConfigLoader.log("Cleared {} expired cache entries".format(len(expired_keys)), level="DEBUG")
110
+
111
+ # Rate limiter to prevent API overload
112
+ class APIRateLimiter:
113
+ """Rate limiter to prevent API overload"""
114
+ def __init__(self, max_calls_per_minute=60):
115
+ self.max_calls = max_calls_per_minute
116
+ self.calls = []
117
+
118
+ def can_make_call(self):
119
+ """Check if we can make another API call within rate limits"""
120
+ now = time.time()
121
+ # Remove calls older than 1 minute
122
+ self.calls = [call_time for call_time in self.calls if now - call_time < 60]
123
+ can_call = len(self.calls) < self.max_calls
124
+ if not can_call:
125
+ MediLink_ConfigLoader.log("Rate limit reached: {} calls in last minute".format(len(self.calls)), level="WARNING")
126
+ return can_call
127
+
128
+ def record_call(self):
129
+ """Record that an API call was made"""
130
+ self.calls.append(time.time())
131
+
132
+ def wait_if_needed(self):
133
+ """Wait if rate limit would be exceeded"""
134
+ if not self.can_make_call():
135
+ # Wait until oldest call expires
136
+ if self.calls:
137
+ wait_time = 60 - (time.time() - self.calls[0])
138
+ if wait_time > 0:
139
+ MediLink_ConfigLoader.log("Rate limit reached, waiting {:.1f} seconds".format(wait_time), level="INFO")
140
+ time.sleep(wait_time)
141
+
142
+ # Enhanced API client wrapper
143
+ class EnhancedAPIWrapper:
144
+ """Wrapper that adds circuit breaker, caching, and rate limiting to any API client"""
145
+
146
+ def __init__(self, base_client, enable_circuit_breaker=True, enable_caching=True, enable_rate_limiting=True):
147
+ self.base_client = base_client
148
+
149
+ # Initialize enhancement components
150
+ self.circuit_breaker = APICircuitBreaker() if enable_circuit_breaker else None
151
+ self.cache = APICache() if enable_caching else None
152
+ self.rate_limiter = APIRateLimiter() if enable_rate_limiting else None
153
+
154
+ MediLink_ConfigLoader.log("Enhanced API wrapper initialized with circuit_breaker={}, caching={}, rate_limiting={}".format(
155
+ enable_circuit_breaker, enable_caching, enable_rate_limiting), level="INFO")
156
+
157
+ def make_enhanced_call(self, method_name, *args, **kwargs):
158
+ """Make API call with all enhancements applied"""
159
+ # Check cache first
160
+ if self.cache:
161
+ cached_result = self.cache.get(method_name, *args, **kwargs)
162
+ if cached_result is not None:
163
+ return cached_result
164
+
165
+ # Check rate limits
166
+ if self.rate_limiter:
167
+ self.rate_limiter.wait_if_needed()
168
+
169
+ # Get the method from base client
170
+ if not hasattr(self.base_client, method_name):
171
+ raise AttributeError("Base client does not have method: {}".format(method_name))
172
+
173
+ method = getattr(self.base_client, method_name)
174
+
175
+ # Execute with circuit breaker protection
176
+ if self.circuit_breaker:
177
+ result = self.circuit_breaker.call_with_breaker(method, *args, **kwargs)
178
+ else:
179
+ result = method(*args, **kwargs)
180
+
181
+ # Record rate limit call
182
+ if self.rate_limiter:
183
+ self.rate_limiter.record_call()
184
+
185
+ # Cache result
186
+ if self.cache:
187
+ self.cache.set(result, method_name, *args, **kwargs)
188
+
189
+ return result
190
+
191
+ # Utility functions for API enhancement integration
192
+ def create_enhanced_api_client(base_client_class, *args, **kwargs):
193
+ """
194
+ Factory function to create enhanced API client from base client class.
195
+ Returns base client wrapped with enhancements.
196
+ """
197
+ try:
198
+ # Create base client
199
+ base_client = base_client_class(*args, **kwargs)
200
+
201
+ # Wrap with enhancements
202
+ enhanced_client = EnhancedAPIWrapper(base_client)
203
+
204
+ MediLink_ConfigLoader.log("Created enhanced API client from {}".format(base_client_class.__name__), level="INFO")
205
+ return enhanced_client
206
+
207
+ except Exception as e:
208
+ MediLink_ConfigLoader.log("Failed to create enhanced API client: {}".format(str(e)), level="ERROR")
209
+ # Return base client as fallback
210
+ return base_client_class(*args, **kwargs)
211
+
212
+ def safe_api_call(api_function, *args, **kwargs):
213
+ """
214
+ Safe wrapper for any API call with error handling and fallback.
215
+ Returns (result, success_flag, error_message)
216
+ """
217
+ try:
218
+ result = api_function(*args, **kwargs)
219
+ return result, True, None
220
+ except Exception as e:
221
+ error_msg = str(e)
222
+ MediLink_ConfigLoader.log("API call failed: {}".format(error_msg), level="ERROR")
223
+ return None, False, error_msg
224
+
225
+ def validate_api_response(response, required_fields=None):
226
+ """
227
+ Validate API response has required structure and fields.
228
+ Returns True if valid, False otherwise.
229
+ """
230
+ if not response:
231
+ return False
232
+
233
+ if required_fields:
234
+ for field in required_fields:
235
+ if field not in response:
236
+ MediLink_ConfigLoader.log("Missing required field in API response: {}".format(field), level="WARNING")
237
+ return False
238
+
239
+ return True
240
+
241
+ # API health monitoring
242
+ class APIHealthMonitor:
243
+ """Monitor API health and performance metrics"""
244
+
245
+ def __init__(self):
246
+ self.metrics = {
247
+ 'total_calls': 0,
248
+ 'successful_calls': 0,
249
+ 'failed_calls': 0,
250
+ 'cache_hits': 0,
251
+ 'circuit_breaker_trips': 0,
252
+ 'rate_limit_hits': 0,
253
+ 'start_time': time.time()
254
+ }
255
+
256
+ def record_call(self, success=True):
257
+ """Record API call outcome"""
258
+ self.metrics['total_calls'] += 1
259
+ if success:
260
+ self.metrics['successful_calls'] += 1
261
+ else:
262
+ self.metrics['failed_calls'] += 1
263
+
264
+ def record_cache_hit(self):
265
+ """Record cache hit"""
266
+ self.metrics['cache_hits'] += 1
267
+
268
+ def record_circuit_breaker_trip(self):
269
+ """Record circuit breaker trip"""
270
+ self.metrics['circuit_breaker_trips'] += 1
271
+
272
+ def record_rate_limit_hit(self):
273
+ """Record rate limit hit"""
274
+ self.metrics['rate_limit_hits'] += 1
275
+
276
+ def get_health_summary(self):
277
+ """Get health summary statistics"""
278
+ total_calls = self.metrics['total_calls']
279
+ if total_calls == 0:
280
+ return {'status': 'NO_CALLS', 'metrics': self.metrics}
281
+
282
+ success_rate = self.metrics['successful_calls'] / float(total_calls)
283
+ cache_hit_rate = self.metrics['cache_hits'] / float(total_calls)
284
+
285
+ runtime = time.time() - self.metrics['start_time']
286
+ calls_per_minute = (total_calls / runtime) * 60 if runtime > 0 else 0
287
+
288
+ summary = {
289
+ 'status': 'HEALTHY' if success_rate > 0.95 else 'DEGRADED' if success_rate > 0.80 else 'UNHEALTHY',
290
+ 'success_rate': success_rate,
291
+ 'cache_hit_rate': cache_hit_rate,
292
+ 'calls_per_minute': calls_per_minute,
293
+ 'runtime_seconds': runtime,
294
+ 'metrics': self.metrics
295
+ }
296
+
297
+ MediLink_ConfigLoader.log("API Health Summary: {}".format(str(summary)), level="INFO")
298
+ return summary
299
+
300
+ # Global API health monitor instance
301
+ _global_health_monitor = APIHealthMonitor()
302
+
303
+ def get_api_health_monitor():
304
+ """Get global API health monitor instance"""
305
+ return _global_health_monitor
@@ -0,0 +1,231 @@
1
+ # MediLink_insurance_utils.py
2
+ # Insurance type enhancement utilities extracted from enhanced implementations
3
+ # Provides safe helper functions for production integration
4
+ # Python 3.4.4 compatible implementation
5
+
6
+ import time
7
+ import json
8
+
9
+ try:
10
+ from MediLink import MediLink_ConfigLoader
11
+ from MediLink import MediLink_UI
12
+ except ImportError:
13
+ import MediLink_ConfigLoader
14
+ import MediLink_UI
15
+
16
+ # Safe tqdm import with fallback
17
+ try:
18
+ from tqdm import tqdm
19
+ except ImportError:
20
+ # Fallback for when tqdm is not available
21
+ def tqdm(iterable, desc="Processing", **kwargs):
22
+ if desc:
23
+ print("{}...".format(desc))
24
+ return iterable
25
+
26
+ # Feature flag system
27
+ def get_feature_flag(flag_name, default=False):
28
+ """Get feature flag from config or return default"""
29
+ try:
30
+ config, _ = MediLink_ConfigLoader.load_configuration()
31
+ feature_flags = config.get("MediLink_Config", {}).get("feature_flags", {})
32
+ return feature_flags.get(flag_name, default)
33
+ except Exception as e:
34
+ MediLink_ConfigLoader.log("Error reading feature flag {}: {}".format(flag_name, str(e)), level="WARNING")
35
+ return default
36
+
37
+ # Enhanced insurance type validation
38
+ def validate_insurance_type_from_config(insurance_type_code, payer_id=""):
39
+ """
40
+ Validate insurance type against configuration.
41
+ Returns validated type or safe fallback.
42
+ """
43
+ try:
44
+ config, _ = MediLink_ConfigLoader.load_configuration()
45
+ insurance_options = config.get('MediLink_Config', {}).get('insurance_options', {})
46
+
47
+ if not insurance_type_code:
48
+ MediLink_ConfigLoader.log("Empty insurance type code for payer {}, using default PPO".format(payer_id), level="INFO")
49
+ return '12' # Default to PPO
50
+
51
+ # Clean and normalize input
52
+ insurance_type_code = str(insurance_type_code).strip().upper()
53
+
54
+ # Basic format validation
55
+ if len(insurance_type_code) > 3 or not insurance_type_code.isalnum():
56
+ MediLink_ConfigLoader.log("Invalid insurance type format '{}' for payer {}, using PPO fallback".format(
57
+ insurance_type_code, payer_id), level="WARNING")
58
+ return '12'
59
+
60
+ # Configuration lookup - insurance type exists in config
61
+ if insurance_type_code in insurance_options:
62
+ MediLink_ConfigLoader.log("Validated insurance type '{}' for payer {}".format(
63
+ insurance_type_code, payer_id), level="DEBUG")
64
+ return insurance_type_code
65
+
66
+ # Unknown type - log and fallback
67
+ MediLink_ConfigLoader.log("Unknown insurance type '{}' for payer {} - using PPO fallback".format(
68
+ insurance_type_code, payer_id), level="WARNING")
69
+
70
+ return '12' # Safe fallback to PPO
71
+
72
+ except Exception as e:
73
+ MediLink_ConfigLoader.log("Insurance validation error: {} - using PPO fallback".format(str(e)), level="ERROR")
74
+ return '12'
75
+
76
+ # Monitoring and statistics
77
+ def generate_insurance_assignment_summary(detailed_patient_data):
78
+ """Generate basic summary statistics for insurance type assignments"""
79
+ if not detailed_patient_data:
80
+ return {}
81
+
82
+ # Collect basic statistics
83
+ insurance_type_counts = {}
84
+
85
+ for data in detailed_patient_data:
86
+ insurance_type = data.get('insurance_type', 'UNKNOWN')
87
+ insurance_type_counts[insurance_type] = insurance_type_counts.get(insurance_type, 0) + 1
88
+
89
+ summary = {
90
+ 'total_patients': len(detailed_patient_data),
91
+ 'insurance_types': insurance_type_counts
92
+ }
93
+
94
+ # Log summary
95
+ MediLink_ConfigLoader.log("Insurance Assignment Summary: {}".format(json.dumps(summary)), level="INFO")
96
+
97
+ return summary
98
+
99
+ # Safe wrapper for insurance type selection
100
+ def safe_insurance_type_selection(parsed_data, fallback_function):
101
+ """
102
+ Safe wrapper that attempts enhanced selection with fallback to original function.
103
+ Provides comprehensive error handling and logging.
104
+ """
105
+ patient_name = parsed_data.get('LAST', 'UNKNOWN')
106
+
107
+ try:
108
+ # Check if enhanced mode is enabled
109
+ enhanced_mode = get_feature_flag('enhanced_insurance_selection', default=False)
110
+
111
+ if enhanced_mode:
112
+ # Try enhanced selection (would be implemented here)
113
+ MediLink_ConfigLoader.log("Attempting enhanced insurance selection", level="DEBUG")
114
+ # For now, just call fallback - actual enhancement would go here
115
+ result = fallback_function(parsed_data)
116
+ MediLink_ConfigLoader.log("Insurance decision: type={}, method=ENHANCED".format(result), level="INFO")
117
+ return result
118
+ else:
119
+ # Use standard selection
120
+ result = fallback_function(parsed_data)
121
+ MediLink_ConfigLoader.log("Insurance decision: type={}, method=MANUAL".format(result), level="INFO")
122
+ return result
123
+
124
+ except Exception as e:
125
+ # Error handling with safe fallback
126
+ MediLink_ConfigLoader.log("Insurance selection error: {}. Using PPO default.".format(str(e)), level="ERROR")
127
+
128
+ MediLink_ConfigLoader.log("Insurance decision: type=12, method=ERROR_FALLBACK, error={}".format(str(e)), level="INFO")
129
+ return '12' # Safe fallback
130
+
131
+ # Configuration validation
132
+ def validate_insurance_configuration():
133
+ """
134
+ Validate insurance configuration for production readiness.
135
+ Returns True if valid, raises exception if invalid.
136
+ """
137
+ try:
138
+ config, _ = MediLink_ConfigLoader.load_configuration()
139
+ insurance_options = config['MediLink_Config'].get('insurance_options', {})
140
+
141
+ if not insurance_options:
142
+ raise ValueError("Missing insurance_options in MediLink_Config")
143
+
144
+ # Validate insurance options format
145
+ for code, description in insurance_options.items():
146
+ if not isinstance(code, str) or len(code) < 1:
147
+ raise ValueError("Invalid insurance code format: {}".format(code))
148
+ if not isinstance(description, str):
149
+ raise ValueError("Invalid insurance description for code: {}".format(code))
150
+
151
+ MediLink_ConfigLoader.log("Insurance configuration validation passed: {} options loaded".format(
152
+ len(insurance_options)), level="INFO")
153
+ return True
154
+
155
+ except Exception as e:
156
+ MediLink_ConfigLoader.log("Insurance configuration validation failed: {}".format(str(e)), level="ERROR")
157
+ raise e
158
+
159
+ # Production readiness check
160
+ def check_production_readiness():
161
+ """
162
+ Check if system is ready for production deployment.
163
+ Returns list of warnings/errors that need attention.
164
+ """
165
+ issues = []
166
+
167
+ try:
168
+ # Check configuration
169
+ validate_insurance_configuration()
170
+ except Exception as e:
171
+ issues.append("Configuration invalid: {}".format(str(e)))
172
+
173
+ # Check for test mode flags
174
+ try:
175
+ config, _ = MediLink_ConfigLoader.load_configuration()
176
+ test_mode = config.get("MediLink_Config", {}).get("TestMode", False)
177
+ if test_mode:
178
+ issues.append("TestMode is enabled - should be disabled for production")
179
+ except Exception as e:
180
+ issues.append("Cannot check test mode: {}".format(str(e)))
181
+
182
+ return issues
183
+
184
+ # Enhanced error handling wrapper
185
+ def with_insurance_error_handling(func):
186
+ """
187
+ Decorator for insurance-related functions to add consistent error handling.
188
+ Python 3.4.4 compatible implementation.
189
+ """
190
+ def wrapper(*args, **kwargs):
191
+ try:
192
+ return func(*args, **kwargs)
193
+ except Exception as e:
194
+ MediLink_ConfigLoader.log("Insurance function error in {}: {}".format(func.__name__, str(e)), level="ERROR")
195
+ # Return safe defaults based on function type
196
+ if 'insurance_type' in func.__name__:
197
+ return '12' # Default PPO for insurance type functions
198
+ else:
199
+ raise e # Re-raise for non-insurance type functions
200
+ return wrapper
201
+
202
+ # Utility for batch processing with progress tracking
203
+ def process_patients_with_progress(patients, processing_function, description="Processing patients"):
204
+ """
205
+ Process patients with progress tracking and error collection.
206
+ Returns processed results and error summary.
207
+ """
208
+ processed_results = []
209
+ errors = []
210
+
211
+ iterator = tqdm(patients, desc=description)
212
+
213
+ for i, patient in enumerate(iterator):
214
+ try:
215
+ result = processing_function(patient)
216
+ processed_results.append(result)
217
+ except Exception as e:
218
+ error_info = {
219
+ 'patient_index': i,
220
+ 'patient_id': patient.get('patient_id', 'UNKNOWN'),
221
+ 'error': str(e)
222
+ }
223
+ errors.append(error_info)
224
+ MediLink_ConfigLoader.log("Error processing patient {}: {}".format(
225
+ patient.get('patient_id', i), str(e)), level="ERROR")
226
+
227
+ if errors:
228
+ MediLink_ConfigLoader.log("Batch processing completed with {} errors out of {} patients".format(
229
+ len(errors), len(patients)), level="WARNING")
230
+
231
+ return processed_results, errors
MediLink/__init__.py CHANGED
@@ -0,0 +1 @@
1
+ from .MediLink import enrich_with_insurance_type