medicafe 0.250728.9__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 (57) 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_837p_cob_library.py +28 -28
  29. MediLink/MediLink_837p_encoder.py +33 -34
  30. MediLink/MediLink_837p_encoder_library.py +226 -150
  31. MediLink/MediLink_837p_utilities.py +129 -5
  32. MediLink/MediLink_API_Generator.py +83 -60
  33. MediLink/MediLink_API_v3.py +1 -1
  34. MediLink/MediLink_ClaimStatus.py +177 -31
  35. MediLink/MediLink_DataMgmt.py +378 -63
  36. MediLink/MediLink_Decoder.py +20 -1
  37. MediLink/MediLink_Deductible.py +155 -28
  38. MediLink/MediLink_Display_Utils.py +72 -0
  39. MediLink/MediLink_Down.py +127 -5
  40. MediLink/MediLink_Gmail.py +712 -653
  41. MediLink/MediLink_PatientProcessor.py +257 -0
  42. MediLink/MediLink_UI.py +85 -71
  43. MediLink/MediLink_Up.py +28 -4
  44. MediLink/MediLink_insurance_utils.py +227 -230
  45. MediLink/MediLink_main.py +248 -0
  46. MediLink/MediLink_smart_import.py +264 -0
  47. MediLink/__init__.py +93 -1
  48. MediLink/insurance_type_integration_test.py +13 -3
  49. MediLink/test.py +1 -1
  50. MediLink/test_timing.py +59 -0
  51. {medicafe-0.250728.9.dist-info → medicafe-0.250805.0.dist-info}/METADATA +1 -1
  52. medicafe-0.250805.0.dist-info/RECORD +81 -0
  53. medicafe-0.250805.0.dist-info/entry_points.txt +2 -0
  54. {medicafe-0.250728.9.dist-info → medicafe-0.250805.0.dist-info}/top_level.txt +1 -0
  55. medicafe-0.250728.9.dist-info/RECORD +0 -59
  56. {medicafe-0.250728.9.dist-info → medicafe-0.250805.0.dist-info}/LICENSE +0 -0
  57. {medicafe-0.250728.9.dist-info → medicafe-0.250805.0.dist-info}/WHEEL +0 -0
@@ -0,0 +1,427 @@
1
+ # MediCafe/api_core.py
2
+ """
3
+ Core API functionality for MediCafe.
4
+ Moved from MediLink to centralize shared API operations.
5
+
6
+ COMPATIBILITY: Python 3.4.4 and Windows XP compatible
7
+ """
8
+
9
+ import time
10
+ import json
11
+ import os
12
+ import traceback
13
+
14
+ try:
15
+ import yaml
16
+ except ImportError:
17
+ yaml = None
18
+
19
+ try:
20
+ import requests
21
+ except ImportError:
22
+ requests = None
23
+
24
+ # Use core utilities for standardized imports
25
+ from MediCafe.core_utils import get_shared_config_loader
26
+ MediLink_ConfigLoader = get_shared_config_loader()
27
+
28
+ """
29
+ Core API client classes and utilities for MediCafe.
30
+ This module provides the foundation for all API operations across the project.
31
+ """
32
+
33
+ class ConfigLoader:
34
+ @staticmethod
35
+ def load_configuration(config_path=os.path.join(os.path.dirname(__file__), '..', 'json', 'config.json'),
36
+ crosswalk_path=os.path.join(os.path.dirname(__file__), '..', 'json', 'crosswalk.json')):
37
+ return MediLink_ConfigLoader.load_configuration(config_path, crosswalk_path)
38
+
39
+ @staticmethod
40
+ def load_swagger_file(swagger_path):
41
+ try:
42
+ print("Attempting to load Swagger file: {}".format(swagger_path))
43
+ with open(swagger_path, 'r') as swagger_file:
44
+ if swagger_path.endswith('.yaml') or swagger_path.endswith('.yml'):
45
+ print("Parsing YAML file: {}".format(swagger_path))
46
+ swagger_data = yaml.safe_load(swagger_file)
47
+ elif swagger_path.endswith('.json'):
48
+ print("Parsing JSON file: {}".format(swagger_path))
49
+ swagger_data = json.load(swagger_file)
50
+ else:
51
+ raise ValueError("Unsupported Swagger file format.")
52
+ print("Successfully loaded Swagger file: {}".format(swagger_path))
53
+ return swagger_data
54
+ except ValueError as e:
55
+ print("Error parsing Swagger file {}: {}".format(swagger_path, e))
56
+ MediLink_ConfigLoader.log("Error parsing Swagger file {}: {}".format(swagger_path, e), level="ERROR")
57
+ except FileNotFoundError:
58
+ print("Swagger file not found: {}".format(swagger_path))
59
+ MediLink_ConfigLoader.log("Swagger file not found: {}".format(swagger_path), level="ERROR")
60
+ except Exception as e:
61
+ print("Unexpected error loading Swagger file {}: {}".format(swagger_path, e))
62
+ MediLink_ConfigLoader.log("Unexpected error loading Swagger file {}: {}".format(swagger_path, e), level="ERROR")
63
+ return None
64
+
65
+ # Function to ensure numeric type
66
+ def ensure_numeric(value):
67
+ if isinstance(value, str):
68
+ try:
69
+ value = float(value)
70
+ except ValueError:
71
+ raise ValueError("Cannot convert {} to a numeric type".format(value))
72
+ return value
73
+
74
+ class TokenCache:
75
+ def __init__(self):
76
+ self.tokens = {}
77
+
78
+ def get(self, endpoint_name, current_time):
79
+ token_info = self.tokens.get(endpoint_name, {})
80
+ if token_info:
81
+ expires_at = token_info['expires_at']
82
+ # Log cache hit and expiration time
83
+ log_message = "Token for {} expires at {}. Current time: {}".format(endpoint_name, expires_at, current_time)
84
+ MediLink_ConfigLoader.log(log_message, level="DEBUG")
85
+
86
+ if expires_at > current_time:
87
+ return token_info['access_token']
88
+
89
+ # Log cache miss
90
+ # Token refresh flow validation has been implemented in get_access_token() to prevent unnecessary token pickup
91
+ log_message = "No valid token found for {}".format(endpoint_name)
92
+ MediLink_ConfigLoader.log(log_message, level="INFO")
93
+
94
+ return None
95
+
96
+ def set(self, endpoint_name, access_token, expires_in, current_time):
97
+ current_time = ensure_numeric(current_time)
98
+ expires_in = ensure_numeric(expires_in)
99
+
100
+ # Log the expires_in value to debug
101
+ log_message = "Token expires in: {} seconds for {}".format(expires_in, endpoint_name)
102
+ MediLink_ConfigLoader.log(log_message, level="INFO")
103
+
104
+ # Adjust expiration time by subtracting a buffer of 120 seconds
105
+ buffer_seconds = 120
106
+ adjusted_expires_in = expires_in - buffer_seconds
107
+
108
+ if adjusted_expires_in <= 0:
109
+ MediLink_ConfigLoader.log("Warning: Token expiration time too short after buffer adjustment", level="WARNING")
110
+ adjusted_expires_in = 60 # Minimum 60 seconds
111
+
112
+ expires_at = current_time + adjusted_expires_in
113
+
114
+ self.tokens[endpoint_name] = {
115
+ 'access_token': access_token,
116
+ 'expires_at': expires_at,
117
+ 'expires_in': expires_in
118
+ }
119
+
120
+ log_message = "Token cached for {} with expiration at {}".format(endpoint_name, expires_at)
121
+ MediLink_ConfigLoader.log(log_message, level="INFO")
122
+
123
+ class BaseAPIClient:
124
+ def __init__(self, config):
125
+ self.config = config
126
+ self.token_cache = TokenCache()
127
+
128
+ def get_access_token(self, endpoint_name):
129
+ raise NotImplementedError("Subclasses should implement this!")
130
+
131
+ def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
132
+ raise NotImplementedError("Subclasses should implement this!")
133
+
134
+ class APIClient(BaseAPIClient):
135
+ def __init__(self):
136
+ config, _ = MediLink_ConfigLoader.load_configuration()
137
+ super().__init__(config)
138
+
139
+ # Add enhanced features if available
140
+ try:
141
+ from MediCafe.api_utils import APICircuitBreaker, APICache, APIRateLimiter
142
+ from MediLink.MediLink_insurance_utils import get_feature_flag
143
+
144
+ # Initialize enhancements if enabled
145
+ enable_circuit_breaker = get_feature_flag('api_circuit_breaker', default=False)
146
+ enable_caching = get_feature_flag('api_caching', default=False)
147
+ enable_rate_limiting = get_feature_flag('api_rate_limiting', default=False)
148
+
149
+ self.circuit_breaker = APICircuitBreaker() if enable_circuit_breaker else None
150
+ self.api_cache = APICache() if enable_caching else None
151
+ self.rate_limiter = APIRateLimiter() if enable_rate_limiting else None
152
+
153
+ if any([enable_circuit_breaker, enable_caching, enable_rate_limiting]):
154
+ MediLink_ConfigLoader.log("Enhanced API client initialized with circuit_breaker={}, caching={}, rate_limiting={}".format(
155
+ enable_circuit_breaker, enable_caching, enable_rate_limiting), level="INFO")
156
+ except ImportError:
157
+ MediLink_ConfigLoader.log("API enhancements not available, using standard client", level="DEBUG")
158
+ self.circuit_breaker = None
159
+ self.api_cache = None
160
+ self.rate_limiter = None
161
+
162
+ def get_access_token(self, endpoint_name):
163
+ MediLink_ConfigLoader.log("[Get Access Token] Called for {}".format(endpoint_name), level="DEBUG")
164
+ current_time = time.time()
165
+ cached_token = self.token_cache.get(endpoint_name, current_time)
166
+
167
+ if cached_token:
168
+ expires_at = self.token_cache.tokens[endpoint_name]['expires_at']
169
+ MediLink_ConfigLoader.log("Cached token expires at {}".format(expires_at), level="DEBUG")
170
+ return cached_token
171
+
172
+ # Validate that we actually need a token before fetching
173
+ # Check if the endpoint configuration exists and is valid
174
+ try:
175
+ endpoint_config = self.config['MediLink_Config']['endpoints'][endpoint_name]
176
+ if not endpoint_config:
177
+ MediLink_ConfigLoader.log("No configuration found for endpoint: {}".format(endpoint_name), level="ERROR")
178
+ return None
179
+
180
+ # Validate required configuration fields
181
+ required_fields = ['token_url', 'client_id', 'client_secret']
182
+ missing_fields = [field for field in required_fields if field not in endpoint_config]
183
+ if missing_fields:
184
+ MediLink_ConfigLoader.log("Missing required configuration fields for {}: {}".format(endpoint_name, missing_fields), level="ERROR")
185
+ return None
186
+
187
+ except KeyError:
188
+ MediLink_ConfigLoader.log("Endpoint {} not found in configuration".format(endpoint_name), level="ERROR")
189
+ return None
190
+ except Exception as e:
191
+ MediLink_ConfigLoader.log("Error validating endpoint configuration for {}: {}".format(endpoint_name, str(e)), level="ERROR")
192
+ return None
193
+
194
+ # If no valid token, fetch a new one
195
+ token_url = endpoint_config['token_url']
196
+ data = {
197
+ 'grant_type': 'client_credentials',
198
+ 'client_id': endpoint_config['client_id'],
199
+ 'client_secret': endpoint_config['client_secret']
200
+ }
201
+
202
+ # Add scope if specified in the configuration
203
+ if 'scope' in endpoint_config:
204
+ data['scope'] = endpoint_config['scope']
205
+
206
+ headers = {'Content-Type': 'application/x-www-form-urlencoded'}
207
+
208
+ try:
209
+ response = requests.post(token_url, headers=headers, data=data)
210
+ response.raise_for_status()
211
+ token_data = response.json()
212
+ access_token = token_data['access_token']
213
+ expires_in = token_data.get('expires_in', 3600)
214
+
215
+ self.token_cache.set(endpoint_name, access_token, expires_in, current_time)
216
+ MediLink_ConfigLoader.log("Obtained NEW token for endpoint: {}".format(endpoint_name), level="INFO")
217
+ return access_token
218
+ except requests.exceptions.RequestException as e:
219
+ MediLink_ConfigLoader.log("Failed to obtain token for {}: {}".format(endpoint_name, str(e)), level="ERROR")
220
+ return None
221
+ except (KeyError, ValueError) as e:
222
+ MediLink_ConfigLoader.log("Invalid token response for {}: {}".format(endpoint_name, str(e)), level="ERROR")
223
+ return None
224
+
225
+ def make_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
226
+ # Try enhanced API call if available
227
+ if hasattr(self, '_make_enhanced_api_call'):
228
+ try:
229
+ return self._make_enhanced_api_call(endpoint_name, call_type, url_extension, params, data, headers)
230
+ except Exception as e:
231
+ MediLink_ConfigLoader.log("Enhanced API call failed, falling back to standard: {}".format(str(e)), level="WARNING")
232
+
233
+ # Fall back to standard API call
234
+ return self._make_standard_api_call(endpoint_name, call_type, url_extension, params, data, headers)
235
+
236
+ def _make_enhanced_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
237
+ """Enhanced API call with circuit breaker, caching, and rate limiting."""
238
+ if self.circuit_breaker:
239
+ return self.circuit_breaker.call(self._make_standard_api_call, endpoint_name, call_type, url_extension, params, data, headers)
240
+ else:
241
+ return self._make_standard_api_call(endpoint_name, call_type, url_extension, params, data, headers)
242
+
243
+ def _make_standard_api_call(self, endpoint_name, call_type, url_extension="", params=None, data=None, headers=None):
244
+ """Standard API call implementation."""
245
+ try:
246
+ # Get access token
247
+ access_token = self.get_access_token(endpoint_name)
248
+ if not access_token:
249
+ raise Exception("Failed to obtain access token for endpoint: {}".format(endpoint_name))
250
+
251
+ # Get endpoint configuration
252
+ endpoint_config = self.config['MediLink_Config']['endpoints'][endpoint_name]
253
+ base_url = endpoint_config['base_url']
254
+ api_url = base_url + url_extension
255
+
256
+ # Prepare headers
257
+ if headers is None:
258
+ headers = {}
259
+
260
+ headers.update({
261
+ 'Authorization': 'Bearer {}'.format(access_token),
262
+ 'Content-Type': 'application/json'
263
+ })
264
+
265
+ # Make the request
266
+ def make_request():
267
+ if call_type.upper() == 'GET':
268
+ response = requests.get(api_url, headers=headers, params=params)
269
+ elif call_type.upper() == 'POST':
270
+ response = requests.post(api_url, headers=headers, params=params, json=data)
271
+ elif call_type.upper() == 'PUT':
272
+ response = requests.put(api_url, headers=headers, params=params, json=data)
273
+ elif call_type.upper() == 'DELETE':
274
+ response = requests.delete(api_url, headers=headers, params=params)
275
+ else:
276
+ raise ValueError("Unsupported HTTP method: {}".format(call_type))
277
+
278
+ response.raise_for_status()
279
+ return response.json()
280
+
281
+ # Apply rate limiting if available
282
+ if self.rate_limiter:
283
+ return self.rate_limiter.call(make_request)
284
+ else:
285
+ return make_request()
286
+
287
+ except Exception as e:
288
+ MediLink_ConfigLoader.log("API call failed for {}: {}".format(endpoint_name, str(e)), level="ERROR")
289
+ raise
290
+
291
+ # Core API utility functions
292
+ def fetch_payer_name_from_api(client, payer_id, config, primary_endpoint='AVAILITY'):
293
+ """
294
+ Fetch payer name from API using the provided client.
295
+
296
+ Args:
297
+ client: API client instance
298
+ payer_id: Payer ID to look up
299
+ config: Configuration dictionary
300
+ primary_endpoint: Primary endpoint to use for lookup
301
+
302
+ Returns:
303
+ str: Payer name if found, None otherwise
304
+ """
305
+ try:
306
+ # Try primary endpoint first
307
+ response = client.make_api_call(
308
+ primary_endpoint,
309
+ 'GET',
310
+ '/payers/{}'.format(payer_id)
311
+ )
312
+
313
+ if response and 'name' in response:
314
+ return response['name']
315
+
316
+ # Try alternative endpoints if primary fails
317
+ alternative_endpoints = ['ELIGIBILITY', 'CLAIMS']
318
+ for endpoint in alternative_endpoints:
319
+ try:
320
+ response = client.make_api_call(
321
+ endpoint,
322
+ 'GET',
323
+ '/payers/{}'.format(payer_id)
324
+ )
325
+
326
+ if response and 'name' in response:
327
+ return response['name']
328
+ except Exception as e:
329
+ MediLink_ConfigLoader.log("Failed to fetch payer name from {}: {}".format(endpoint, str(e)), level="DEBUG")
330
+ continue
331
+
332
+ return None
333
+
334
+ except Exception as e:
335
+ MediLink_ConfigLoader.log("Failed to fetch payer name for {}: {}".format(payer_id, str(e)), level="ERROR")
336
+ return None
337
+
338
+ def get_eligibility_v3(client, payer_id, provider_last_name, search_option, date_of_birth, member_id, npi,
339
+ first_name=None, last_name=None, payer_label=None, payer_name=None, service_start=None, service_end=None,
340
+ middle_name=None, gender=None, ssn=None, city=None, state=None, zip=None, group_number=None,
341
+ service_type_code=None, provider_first_name=None, tax_id_number=None, provider_name_id=None,
342
+ corporate_tax_owner_id=None, corporate_tax_owner_name=None, organization_name=None,
343
+ organization_id=None, identify_service_level_deductible=True):
344
+ """
345
+ Get eligibility information using v3 API.
346
+
347
+ Args:
348
+ client: API client instance
349
+ payer_id: Payer ID
350
+ provider_last_name: Provider's last name
351
+ search_option: Search option
352
+ date_of_birth: Date of birth
353
+ member_id: Member ID
354
+ npi: National Provider Identifier
355
+ **kwargs: Additional optional parameters
356
+
357
+ Returns:
358
+ dict: Eligibility response data
359
+ """
360
+ # Ensure all required parameters have values
361
+ required_params = {
362
+ 'payer_id': payer_id,
363
+ 'provider_last_name': provider_last_name,
364
+ 'search_option': search_option,
365
+ 'date_of_birth': date_of_birth,
366
+ 'member_id': member_id,
367
+ 'npi': npi
368
+ }
369
+
370
+ # Validate required parameters
371
+ for param_name, param_value in required_params.items():
372
+ if not param_value:
373
+ raise ValueError("Required parameter '{}' is missing or empty".format(param_name))
374
+
375
+ # Build request data
376
+ request_data = {
377
+ 'payer_id': payer_id,
378
+ 'provider_last_name': provider_last_name,
379
+ 'search_option': search_option,
380
+ 'date_of_birth': date_of_birth,
381
+ 'member_id': member_id,
382
+ 'npi': npi,
383
+ 'identify_service_level_deductible': identify_service_level_deductible
384
+ }
385
+
386
+ # Add optional parameters if provided
387
+ optional_params = {
388
+ 'first_name': first_name,
389
+ 'last_name': last_name,
390
+ 'payer_label': payer_label,
391
+ 'payer_name': payer_name,
392
+ 'service_start': service_start,
393
+ 'service_end': service_end,
394
+ 'middle_name': middle_name,
395
+ 'gender': gender,
396
+ 'ssn': ssn,
397
+ 'city': city,
398
+ 'state': state,
399
+ 'zip': zip,
400
+ 'group_number': group_number,
401
+ 'service_type_code': service_type_code,
402
+ 'provider_first_name': provider_first_name,
403
+ 'tax_id_number': tax_id_number,
404
+ 'provider_name_id': provider_name_id,
405
+ 'corporate_tax_owner_id': corporate_tax_owner_id,
406
+ 'corporate_tax_owner_name': corporate_tax_owner_name,
407
+ 'organization_name': organization_name,
408
+ 'organization_id': organization_id
409
+ }
410
+
411
+ for param_name, param_value in optional_params.items():
412
+ if param_value is not None:
413
+ request_data[param_name] = param_value
414
+
415
+ try:
416
+ response = client.make_api_call(
417
+ 'ELIGIBILITY',
418
+ 'POST',
419
+ '/eligibility/v3',
420
+ data=request_data
421
+ )
422
+
423
+ return response
424
+
425
+ except Exception as e:
426
+ MediLink_ConfigLoader.log("Eligibility v3 request failed: {}".format(str(e)), level="ERROR")
427
+ raise