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.
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/setup.py +1 -1
- {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
- {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
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/tests/test_cache_aware_service.py +5 -4
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/tests/test_shared_cache.py +4 -3
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/LICENCE +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/README.md +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/pyproject.toml +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/setup.cfg +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/__init__.py +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/cache/__init__.py +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/dependencies/__init__.py +0 -0
- {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
- {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
- {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
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/__init__.py +0 -0
- {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
- {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
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/models/subscription.py +0 -0
- {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
- {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
- {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
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/__init__.py +0 -0
- {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
- {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
- {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
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/fastapiservicemon.py +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/services/servicemon.py +0 -0
- {ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/src/ipulse_shared_core_ftredge/utils/__init__.py +0 -0
- {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
- {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
- {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
- {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
- {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
- {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:
|
|
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='
|
|
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=[
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
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 {
|
|
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
|
-
|
|
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
|
-
|
|
198
|
-
|
|
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"
|
|
233
|
+
"updated_by": "credit_service"
|
|
203
234
|
}
|
|
204
235
|
|
|
205
|
-
if
|
|
206
|
-
update_data["sbscrptn_based_insight_credits"] = firestore.Increment(-
|
|
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
|
|
210
|
-
update_data["extra_insight_credits"] = firestore.Increment(-
|
|
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
|
-
|
|
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:
|
|
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
|
-
|
|
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
|
|
217
|
-
self.assertIsNone(self.collection_cache.get(
|
|
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."""
|
{ipulse_shared_core_ftredge-14.0.1 → ipulse_shared_core_ftredge-15.0.1}/tests/test_shared_cache.py
RENAMED
|
@@ -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["
|
|
142
|
-
self.assertIn("stats_key2", stats["
|
|
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__":
|
|
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
|