ipulse-shared-core-ftredge 14.0.1__tar.gz → 15.0.1__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.

Potentially problematic release.


This version of ipulse-shared-core-ftredge might be problematic. Click here for more details.

Files changed (40) hide show
  1. {ipulse_shared_core_ftredge-14.0.1/src/ipulse_shared_core_ftredge.egg-info → ipulse_shared_core_ftredge-15.0.1}/PKG-INFO +1 -1
  2. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/setup.py +1 -1
  3. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/cache/shared_cache.py +1 -1
  4. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/credit_service.py +59 -23
  5. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1/src/ipulse_shared_core_ftredge.egg-info}/PKG-INFO +1 -1
  6. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/tests/test_cache_aware_service.py +5 -4
  7. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/tests/test_shared_cache.py +4 -3
  8. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/LICENCE +0 -0
  9. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/README.md +0 -0
  10. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/pyproject.toml +0 -0
  11. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/setup.cfg +0 -0
  12. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/__init__.py +0 -0
  13. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/cache/__init__.py +0 -0
  14. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/__init__.py +0 -0
  15. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py +0 -0
  16. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/auth_protected_router.py +0 -0
  17. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +0 -0
  18. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/firestore_client.py +0 -0
  19. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/__init__.py +0 -0
  20. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/base_api_response.py +0 -0
  21. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/base_data_model.py +0 -0
  22. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/organization_profile.py +0 -0
  23. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/subscription.py +0 -0
  24. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/user_auth.py +0 -0
  25. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/user_profile.py +0 -0
  26. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/user_profile_update.py +0 -0
  27. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/user_status.py +0 -0
  28. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/__init__.py +0 -0
  29. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/base_firestore_service.py +0 -0
  30. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/base_service_exceptions.py +0 -0
  31. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/cache_aware_firestore_service.py +0 -0
  32. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/fastapiservicemon.py +0 -0
  33. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/servicemon.py +0 -0
  34. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/utils/__init__.py +0 -0
  35. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/utils/custom_json_encoder.py +0 -0
  36. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/utils/json_encoder.py +0 -0
  37. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge.egg-info/SOURCES.txt +0 -0
  38. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge.egg-info/dependency_links.txt +0 -0
  39. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge.egg-info/requires.txt +0 -0
  40. {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipulse_shared_core_ftredge
3
- Version: 14.0.1
3
+ Version: 15.0.1
4
4
  Summary: Shared Core models and Logger util for the Pulse platform project. Using AI for financial advisory and investment management.
5
5
  Home-page: https://github.com/TheFutureEdge/ipulse_shared_core
6
6
  Author: Russlan Ramdowar
@@ -3,7 +3,7 @@ from setuptools import setup, find_packages
3
3
 
4
4
  setup(
5
5
  name='ipulse_shared_core_ftredge',
6
- version='14.0.1',
6
+ version='15.0.1',
7
7
  package_dir={'': 'src'}, # Specify the source directory
8
8
  packages=find_packages(where='src'), # Look for packages in 'src'
9
9
  install_requires=[
@@ -39,7 +39,7 @@ class SharedCache(Generic[T]):
39
39
  self._timestamps: Dict[str, float] = {}
40
40
 
41
41
  # Thread-safe attributes
42
- self.lock = threading.Lock()
42
+ self.lock = threading.RLock() # Changed from threading.Lock()
43
43
  self.hits = 0
44
44
  self.misses = 0
45
45
  self.sets = 0
@@ -138,7 +138,7 @@ class CreditService:
138
138
  additional_info={"credits_to_charge": required_credits_for_resource}
139
139
  ) from e
140
140
 
141
- async def charge_credits(self, user_uid: str, credits_to_charge: Optional[float], operation_details: str) -> bool:
141
+ async def charge_credits(self, user_uid: str, credits_to_charge: Optional[float], operation_details: str) -> Tuple[bool, Optional[Dict[str, float]]]:
142
142
  """
143
143
  Charge a user's credits for an operation.
144
144
 
@@ -148,7 +148,9 @@ class CreditService:
148
148
  operation_details: Details about the operation (for logging).
149
149
 
150
150
  Returns:
151
- Whether the charging was successful.
151
+ Tuple of (success_status: bool, updated_credits: Optional[Dict[str, float]])
152
+ where updated_credits contains 'sbscrptn_based_insight_credits' and 'extra_insight_credits'
153
+ if charging was successful.
152
154
 
153
155
  Raises:
154
156
  ValidationError: If credits_to_charge is None (pricing not properly configured).
@@ -164,7 +166,12 @@ class CreditService:
164
166
 
165
167
  if credits_to_charge == 0:
166
168
  self.logger.info(f"No credits to charge for user {user_uid}, operation: {operation_details}")
167
- return True
169
+ # If no charge, current credits are unchanged. Fetch them if needed by caller.
170
+ # For simplicity here, returning None for updated_credits as no transaction occurred.
171
+ # The caller (CreditProcessor) will handle fetching if it needs the current state.
172
+ # Alternatively, we could fetch here, but it's cleaner if CreditProcessor handles it.
173
+ return True, None
174
+
168
175
 
169
176
  try:
170
177
  userstatus_id = f"{self.user_status_doc_prefix}{user_uid}"
@@ -173,60 +180,89 @@ class CreditService:
173
180
  transaction = self.db.transaction()
174
181
 
175
182
  @firestore.transactional
176
- def update_credits_transaction(transaction_obj, current_user_ref):
183
+ def update_credits_transaction(transaction_obj, current_user_ref) -> Tuple[bool, Optional[Dict[str, float]]]:
177
184
  user_doc = current_user_ref.get(transaction=transaction_obj)
178
185
  if not user_doc.exists:
179
186
  self.logger.warning(
180
187
  f"Cannot charge credits - user status not found for {user_uid} in {self.users_status_collection_name}"
181
188
  )
182
- return False
189
+ return False, None
183
190
 
184
191
  userstatus = user_doc.to_dict()
185
192
 
186
- subscription_credits = userstatus.get("sbscrptn_based_insight_credits", 0)
187
- extra_credits = userstatus.get("extra_insight_credits", 0)
188
- total_credits = subscription_credits + extra_credits
193
+ current_subscription_credits = userstatus.get("sbscrptn_based_insight_credits", 0.0)
194
+ current_extra_credits = userstatus.get("extra_insight_credits", 0.0)
195
+ total_available_credits = current_subscription_credits + current_extra_credits
189
196
 
190
- if total_credits < credits_to_charge:
197
+ if total_available_credits < credits_to_charge:
191
198
  self.logger.warning(
192
199
  f"Insufficient credits for user {user_uid} during transaction: "
193
- f"has {total_credits}, needs {credits_to_charge}"
200
+ f"has {total_available_credits}, needs {credits_to_charge}"
201
+ )
202
+ # Return current credits if charge fails due to insufficient funds
203
+ return False, {
204
+ "sbscrptn_based_insight_credits": current_subscription_credits,
205
+ "extra_insight_credits": current_extra_credits
206
+ }
207
+
208
+ # Calculate how much to deduct from each type of credit
209
+ subscription_credits_deducted = min(current_subscription_credits, credits_to_charge)
210
+ remaining_charge = credits_to_charge - subscription_credits_deducted
211
+ extra_credits_deducted = min(current_extra_credits, remaining_charge)
212
+
213
+ # This check should ideally not be needed if total_available_credits was sufficient,
214
+ # but as a safeguard:
215
+ if (subscription_credits_deducted + extra_credits_deducted) < credits_to_charge:
216
+ self.logger.error(
217
+ f"Credit calculation error for user {user_uid}. "
218
+ f"Required: {credits_to_charge}, Calculated deduction: {subscription_credits_deducted + extra_credits_deducted}"
194
219
  )
195
- return False
220
+ # This case implies a logic flaw or race condition if not for the initial check.
221
+ # Return current credits as charge effectively failed.
222
+ return False, {
223
+ "sbscrptn_based_insight_credits": current_subscription_credits,
224
+ "extra_insight_credits": current_extra_credits
225
+ }
226
+
196
227
 
197
- subscription_credits_to_charge = min(subscription_credits, credits_to_charge)
198
- extra_credits_to_charge = credits_to_charge - subscription_credits_to_charge
228
+ new_subscription_credits = current_subscription_credits - subscription_credits_deducted
229
+ new_extra_credits = current_extra_credits - extra_credits_deducted
199
230
 
200
231
  update_data = {
201
232
  "updated_at": datetime.now(timezone.utc).isoformat(),
202
- "updated_by": "credit_service" # Consider making this configurable or more generic
233
+ "updated_by": "credit_service"
203
234
  }
204
235
 
205
- if subscription_credits_to_charge > 0:
206
- update_data["sbscrptn_based_insight_credits"] = firestore.Increment(-subscription_credits_to_charge)
236
+ if subscription_credits_deducted > 0:
237
+ update_data["sbscrptn_based_insight_credits"] = firestore.Increment(-subscription_credits_deducted)
207
238
  update_data["sbscrptn_based_insight_credits_updtd_on"] = datetime.now(timezone.utc).isoformat()
208
239
 
209
- if extra_credits_to_charge > 0:
210
- update_data["extra_insight_credits"] = firestore.Increment(-extra_credits_to_charge)
240
+ if extra_credits_deducted > 0:
241
+ update_data["extra_insight_credits"] = firestore.Increment(-extra_credits_deducted)
211
242
  update_data["extra_insight_credits_updtd_on"] = datetime.now(timezone.utc).isoformat()
212
243
 
213
244
  transaction_obj.update(current_user_ref, update_data)
214
- return True
215
245
 
216
- success = update_credits_transaction(transaction, user_ref)
246
+ # Return success and the calculated new credit balances
247
+ return True, {
248
+ "sbscrptn_based_insight_credits": new_subscription_credits,
249
+ "extra_insight_credits": new_extra_credits
250
+ }
251
+
252
+ success, updated_credits_dict = update_credits_transaction(transaction, user_ref)
217
253
 
218
254
  if success:
219
255
  self.logger.info(
220
256
  f"Successfully charged {credits_to_charge} credits for user {user_uid}. "
221
- f"Operation: {operation_details}"
257
+ f"Operation: {operation_details}. New balances: {updated_credits_dict}"
222
258
  )
223
259
  else:
224
260
  self.logger.warning(
225
261
  f"Failed to charge {credits_to_charge} credits for user {user_uid} (transaction outcome). "
226
- f"Operation: {operation_details}"
262
+ f"Operation: {operation_details}. Current balances: {updated_credits_dict}"
227
263
  )
228
264
 
229
- return success
265
+ return success, updated_credits_dict
230
266
 
231
267
  except Exception as e:
232
268
  self.logger.error(f"Error charging credits for user {user_uid}: {str(e)}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipulse_shared_core_ftredge
3
- Version: 14.0.1
3
+ Version: 15.0.1
4
4
  Summary: Shared Core models and Logger util for the Pulse platform project. Using AI for financial advisory and investment management.
5
5
  Home-page: https://github.com/TheFutureEdge/ipulse_shared_core
6
6
  Author: Russlan Ramdowar
@@ -208,13 +208,14 @@ class TestCacheAwareFirestoreService(unittest.TestCase):
208
208
  """Test _invalidate_collection_cache method."""
209
209
  # Prepare cached data
210
210
  test_docs = [{"id": "doc1", "name": "Doc 1"}, {"id": "doc2", "name": "Doc 2"}]
211
- self.collection_cache.set("all_documents", test_docs)
211
+ cache_key_to_invalidate = "all_documents"
212
+ self.collection_cache.set(cache_key_to_invalidate, test_docs)
212
213
 
213
214
  # Execute invalidation
214
- self.service._invalidate_collection_cache()
215
+ self.service._invalidate_collection_cache(cache_key_to_invalidate)
215
216
 
216
- # Verify cache was invalidated
217
- self.assertIsNone(self.collection_cache.get("all_documents"))
217
+ # Verify cache is empty for that key
218
+ self.assertIsNone(self.collection_cache.get(cache_key_to_invalidate))
218
219
 
219
220
  def test_invalidate_collection_cache_custom_key(self):
220
221
  """Test _invalidate_collection_cache method with custom key."""
@@ -42,7 +42,7 @@ class TestSharedCache(unittest.TestCase):
42
42
  self.assertEqual(self.cache.get("expiring_key"), "expiring_value")
43
43
 
44
44
  # Wait for TTL to expire
45
- time.sleep(0.6) # Slightly longer than TTL
45
+ time.sleep(0.6) # Slightly longer than TTL (0.5s)
46
46
 
47
47
  # Verify value is no longer cached
48
48
  self.assertIsNone(self.cache.get("expiring_key"))
@@ -138,8 +138,9 @@ class TestSharedCache(unittest.TestCase):
138
138
  self.assertEqual(stats["enabled"], True)
139
139
  self.assertEqual(stats["ttl_seconds"], 0.5)
140
140
  self.assertEqual(stats["item_count"], 2)
141
- self.assertIn("stats_key1", stats["keys"])
142
- self.assertIn("stats_key2", stats["keys"])
141
+ self.assertIn("stats_key1", stats["first_20_keys"])
142
+ self.assertIn("stats_key2", stats["first_20_keys"])
143
+ self.assertEqual(stats["total_keys"], 2)
143
144
 
144
145
 
145
146
  if __name__ == "__main__":