ipulse-shared-core-ftredge 20.0.1__py3-none-any.whl → 23.1.1__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 ipulse-shared-core-ftredge might be problematic. Click here for more details.
- ipulse_shared_core_ftredge/cache/shared_cache.py +1 -2
- ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py +60 -23
- ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +128 -157
- ipulse_shared_core_ftredge/exceptions/base_exceptions.py +35 -4
- ipulse_shared_core_ftredge/models/__init__.py +3 -7
- ipulse_shared_core_ftredge/models/base_data_model.py +17 -19
- ipulse_shared_core_ftredge/models/catalog/__init__.py +10 -0
- ipulse_shared_core_ftredge/models/catalog/subscriptionplan.py +274 -0
- ipulse_shared_core_ftredge/models/catalog/usertype.py +177 -0
- ipulse_shared_core_ftredge/models/user/__init__.py +5 -0
- ipulse_shared_core_ftredge/models/user/user_permissions.py +66 -0
- ipulse_shared_core_ftredge/models/user/user_subscription.py +348 -0
- ipulse_shared_core_ftredge/models/{user_auth.py → user/userauth.py} +19 -10
- ipulse_shared_core_ftredge/models/{user_profile.py → user/userprofile.py} +53 -21
- ipulse_shared_core_ftredge/models/user/userstatus.py +479 -0
- ipulse_shared_core_ftredge/monitoring/__init__.py +0 -2
- ipulse_shared_core_ftredge/monitoring/tracemon.py +6 -6
- ipulse_shared_core_ftredge/services/__init__.py +11 -13
- ipulse_shared_core_ftredge/services/base/__init__.py +3 -1
- ipulse_shared_core_ftredge/services/base/base_firestore_service.py +77 -16
- ipulse_shared_core_ftredge/services/{cache_aware_firestore_service.py → base/cache_aware_firestore_service.py} +46 -32
- ipulse_shared_core_ftredge/services/catalog/__init__.py +14 -0
- ipulse_shared_core_ftredge/services/catalog/catalog_subscriptionplan_service.py +277 -0
- ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py +376 -0
- ipulse_shared_core_ftredge/services/charging_processors.py +25 -25
- ipulse_shared_core_ftredge/services/user/__init__.py +5 -25
- ipulse_shared_core_ftredge/services/user/user_core_service.py +536 -510
- ipulse_shared_core_ftredge/services/user/user_multistep_operations.py +796 -0
- ipulse_shared_core_ftredge/services/user/user_permissions_operations.py +392 -0
- ipulse_shared_core_ftredge/services/user/user_subscription_operations.py +488 -0
- ipulse_shared_core_ftredge/services/user/userauth_operations.py +928 -0
- ipulse_shared_core_ftredge/services/user/userprofile_operations.py +166 -0
- ipulse_shared_core_ftredge/services/user/userstatus_operations.py +476 -0
- ipulse_shared_core_ftredge/services/{charging_service.py → user_charging_service.py} +9 -9
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/METADATA +3 -4
- ipulse_shared_core_ftredge-23.1.1.dist-info/RECORD +50 -0
- ipulse_shared_core_ftredge/models/subscription.py +0 -190
- ipulse_shared_core_ftredge/models/user_status.py +0 -495
- ipulse_shared_core_ftredge/monitoring/microservmon.py +0 -526
- ipulse_shared_core_ftredge/services/user/iam_management_operations.py +0 -326
- ipulse_shared_core_ftredge/services/user/subscription_management_operations.py +0 -384
- ipulse_shared_core_ftredge/services/user/user_account_operations.py +0 -479
- ipulse_shared_core_ftredge/services/user/user_auth_operations.py +0 -305
- ipulse_shared_core_ftredge/services/user/user_holistic_operations.py +0 -436
- ipulse_shared_core_ftredge-20.0.1.dist-info/RECORD +0 -42
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/WHEEL +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/licenses/LICENCE +0 -0
- {ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/top_level.txt +0 -0
|
@@ -4,12 +4,12 @@ from typing import Dict, Any, Optional, Tuple
|
|
|
4
4
|
from datetime import datetime, timezone
|
|
5
5
|
from google.cloud import firestore
|
|
6
6
|
from ipulse_shared_core_ftredge.exceptions import ServiceError, ResourceNotFoundError, ValidationError
|
|
7
|
-
from ipulse_shared_core_ftredge.models
|
|
7
|
+
from ipulse_shared_core_ftredge.models import UserStatus
|
|
8
8
|
|
|
9
9
|
# Default Firestore timeout if not provided by the consuming application
|
|
10
10
|
DEFAULT_FIRESTORE_TIMEOUT = 15.0
|
|
11
11
|
|
|
12
|
-
class
|
|
12
|
+
class UserChargingService:
|
|
13
13
|
"""
|
|
14
14
|
Service class for charging operations.
|
|
15
15
|
Designed to be project-agnostic and directly uses UserStatus model constants.
|
|
@@ -32,13 +32,13 @@ class ChargingService:
|
|
|
32
32
|
self.db = db
|
|
33
33
|
# Use UserStatus constants directly
|
|
34
34
|
self.users_status_collection_name = UserStatus.COLLECTION_NAME
|
|
35
|
-
self.
|
|
35
|
+
self.userstatus_doc_prefix = f"{UserStatus.OBJ_REF}_" # Append underscore to OBJ_REF
|
|
36
36
|
self.logger = logger or logging.getLogger(__name__)
|
|
37
37
|
self.timeout = firestore_timeout
|
|
38
38
|
|
|
39
39
|
self.logger.info(
|
|
40
40
|
f"ChargingService initialized using UserStatus constants. Collection: {self.users_status_collection_name}, "
|
|
41
|
-
f"Doc Prefix: {self.
|
|
41
|
+
f"Doc Prefix: {self.userstatus_doc_prefix}, Timeout: {self.timeout}s"
|
|
42
42
|
)
|
|
43
43
|
|
|
44
44
|
async def verify_credits(
|
|
@@ -57,7 +57,7 @@ class ChargingService:
|
|
|
57
57
|
(keys: 'sbscrptn_based_insight_credits', 'extra_insight_credits')
|
|
58
58
|
|
|
59
59
|
Returns:
|
|
60
|
-
Tuple of (has_enough_credits,
|
|
60
|
+
Tuple of (has_enough_credits, userstatus_data) where userstatus_data
|
|
61
61
|
will be a dict with keys 'sbscrptn_based_insight_credits' and 'extra_insight_credits'.
|
|
62
62
|
|
|
63
63
|
Raises:
|
|
@@ -174,7 +174,7 @@ class ChargingService:
|
|
|
174
174
|
|
|
175
175
|
|
|
176
176
|
try:
|
|
177
|
-
userstatus_id = f"{self.
|
|
177
|
+
userstatus_id = f"{self.userstatus_doc_prefix}{user_uid}"
|
|
178
178
|
user_ref = self.db.collection(self.users_status_collection_name).document(userstatus_id)
|
|
179
179
|
|
|
180
180
|
transaction = self.db.transaction()
|
|
@@ -277,7 +277,7 @@ class ChargingService:
|
|
|
277
277
|
async def _get_userstatus(self, user_uid: str) -> Dict[str, Any]:
|
|
278
278
|
"""Get a user's status document."""
|
|
279
279
|
try:
|
|
280
|
-
userstatus_id = f"{self.
|
|
280
|
+
userstatus_id = f"{self.userstatus_doc_prefix}{user_uid}"
|
|
281
281
|
doc_ref = self.db.collection(self.users_status_collection_name).document(userstatus_id)
|
|
282
282
|
|
|
283
283
|
# Using the timeout value set during initialization
|
|
@@ -285,7 +285,7 @@ class ChargingService:
|
|
|
285
285
|
|
|
286
286
|
if not doc.exists:
|
|
287
287
|
raise ResourceNotFoundError(
|
|
288
|
-
resource_type="
|
|
288
|
+
resource_type="userstatus", # Generic resource type
|
|
289
289
|
resource_id=userstatus_id,
|
|
290
290
|
additional_info={"collection": self.users_status_collection_name}
|
|
291
291
|
)
|
|
@@ -299,7 +299,7 @@ class ChargingService:
|
|
|
299
299
|
raise ServiceError(
|
|
300
300
|
operation="getting user status",
|
|
301
301
|
error=e,
|
|
302
|
-
resource_type="
|
|
302
|
+
resource_type="userstatus",
|
|
303
303
|
resource_id=user_uid,
|
|
304
304
|
additional_info={"collection": self.users_status_collection_name}
|
|
305
305
|
) from e
|
{ipulse_shared_core_ftredge-20.0.1.dist-info → ipulse_shared_core_ftredge-23.1.1.dist-info}/METADATA
RENAMED
|
@@ -1,19 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipulse_shared_core_ftredge
|
|
3
|
-
Version:
|
|
3
|
+
Version: 23.1.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
|
|
7
7
|
Classifier: Programming Language :: Python :: 3
|
|
8
8
|
Classifier: License :: OSI Approved :: MIT License
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12
|
|
11
11
|
License-File: LICENCE
|
|
12
12
|
Requires-Dist: pydantic[email]~=2.5
|
|
13
13
|
Requires-Dist: python-dateutil~=2.8
|
|
14
14
|
Requires-Dist: fastapi~=0.115.8
|
|
15
|
-
Requires-Dist:
|
|
16
|
-
Requires-Dist: ipulse_shared_base_ftredge==7.2.0
|
|
15
|
+
Requires-Dist: ipulse_shared_base_ftredge==10.2.1
|
|
17
16
|
Dynamic: author
|
|
18
17
|
Dynamic: classifier
|
|
19
18
|
Dynamic: home-page
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
ipulse_shared_core_ftredge/__init__.py,sha256=-KbdF_YW8pgf7pVv9qh_cA1xrNm_B9zigHYDo7ZA4eU,42
|
|
2
|
+
ipulse_shared_core_ftredge/cache/__init__.py,sha256=i2fPojmZiBwAoY5ovnnnME9USl4bi8MRPYkAgEfACfI,136
|
|
3
|
+
ipulse_shared_core_ftredge/cache/shared_cache.py,sha256=BDJtkTsdfmVjKaUkbBXOhJ2Oib7Li0UCsPjWX7FLIPU,12940
|
|
4
|
+
ipulse_shared_core_ftredge/dependencies/__init__.py,sha256=HGsR8HUguKTfjz_BorCILS4izX8CAjG-apE0kIPE0Yo,68
|
|
5
|
+
ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py,sha256=iwNqOEFHEF0qK7vfRPRXpAexTwF5UOLsfdir1wEo9_E,3646
|
|
6
|
+
ipulse_shared_core_ftredge/dependencies/auth_protected_router.py,sha256=em5D5tE7OkgZmuCtYCKuUAnIZCgRJhCF8Ye5QmtGWlk,1807
|
|
7
|
+
ipulse_shared_core_ftredge/dependencies/authz_for_apis.py,sha256=t2hz1wAVxCT0lCDVf6gmcYeLuUCR7mIXYsThKg0cIlM,14992
|
|
8
|
+
ipulse_shared_core_ftredge/dependencies/firestore_client.py,sha256=VbTb121nsc9EZPd1RDEsHBLW5pIiVw6Wdo2JFL4afMg,714
|
|
9
|
+
ipulse_shared_core_ftredge/exceptions/__init__.py,sha256=Cb_RsIie4DbT_NLwFVwjw4riDKsNNRQEuAvHvYa-Zco,1038
|
|
10
|
+
ipulse_shared_core_ftredge/exceptions/base_exceptions.py,sha256=117YsiCbYLLBu_D0IffYFVSX2yh-pisALMtoSMwj6xI,5338
|
|
11
|
+
ipulse_shared_core_ftredge/exceptions/user_exceptions.py,sha256=I-nm21MKrUYEoybpRODeYNzc184HfgHvRZQm_xux4VY,6824
|
|
12
|
+
ipulse_shared_core_ftredge/models/__init__.py,sha256=oaBL_BZWd7hIDu2K7yxtVKxtkOD9UF9r9_V2ZIPQ8Yk,350
|
|
13
|
+
ipulse_shared_core_ftredge/models/base_api_response.py,sha256=OwuWI2PsMSLDkFt643u35ZhW5AHFEMMAGnGprmUO0fA,2380
|
|
14
|
+
ipulse_shared_core_ftredge/models/base_data_model.py,sha256=GZ7KTT5FanHTgvmaHHTxawzAJtuixkbyb-SuL-mjWys,2193
|
|
15
|
+
ipulse_shared_core_ftredge/models/catalog/__init__.py,sha256=9oKJ74_mTtmj-0iDnRBiPI8m8QJ2J9wvx4ZWaZw3zRk,208
|
|
16
|
+
ipulse_shared_core_ftredge/models/catalog/subscriptionplan.py,sha256=WxKWzTmHJlvFQj6Kq69iWMoFkx_veiPhonFo8dUGzZw,9148
|
|
17
|
+
ipulse_shared_core_ftredge/models/catalog/usertype.py,sha256=E_qQCq7ytiFca6umaX_-_a6TuDh83YwSKtFKdeU4ErM,6584
|
|
18
|
+
ipulse_shared_core_ftredge/models/user/__init__.py,sha256=TheOLldY6v-OK9i-A5mQNIxjHhBFpuOJ43mi-swcN_o,196
|
|
19
|
+
ipulse_shared_core_ftredge/models/user/user_permissions.py,sha256=CUWDBPLPmKN3o43BTZAt0zJvm_ekjJA46iV6rVNp-oc,2411
|
|
20
|
+
ipulse_shared_core_ftredge/models/user/user_subscription.py,sha256=83ncQUHUYF6S19KdZM7-nLEVjD4VPIO88ZRVkXeNnE8,13514
|
|
21
|
+
ipulse_shared_core_ftredge/models/user/userauth.py,sha256=PbS-XSLxDl1feskI0iCziGvmMiLuF8o_ZTspAx1B0j0,3679
|
|
22
|
+
ipulse_shared_core_ftredge/models/user/userprofile.py,sha256=7VbE4qiKpDxZsNTk-IJKA32QxW0JOo8KWPkj8h9J2-Y,6945
|
|
23
|
+
ipulse_shared_core_ftredge/models/user/userstatus.py,sha256=9BGaKiKL3JeSNQ8ZRRc0taLgSu0uxtBu5NRVsCbVXW4,18776
|
|
24
|
+
ipulse_shared_core_ftredge/monitoring/__init__.py,sha256=gUoJjT0wj-cQYnMWheWbh1mmRHmaeojmnBZTj7KPNus,61
|
|
25
|
+
ipulse_shared_core_ftredge/monitoring/tracemon.py,sha256=Trku0qrwWvEcvKsBWiYokd_G3fcH-5uP2wRVgcgIz_k,11596
|
|
26
|
+
ipulse_shared_core_ftredge/services/__init__.py,sha256=9AkMLCHNswhuNbQuJZaEVz4zt4F84PxfJLyU_bYk4Js,565
|
|
27
|
+
ipulse_shared_core_ftredge/services/charging_processors.py,sha256=9Re24dyXdjKYbqwx6uNLu3JBzIaw87TAV7Oe__M1QnA,16308
|
|
28
|
+
ipulse_shared_core_ftredge/services/user_charging_service.py,sha256=C3wMfgBXOz4RM1RLc7up2_pIPAnZv8ZYu-lLrkofTmc,14625
|
|
29
|
+
ipulse_shared_core_ftredge/services/base/__init__.py,sha256=zhyrHQMM0cLJr4spk2b6VsgJXuWBy7hUBzhrq_Seg9k,389
|
|
30
|
+
ipulse_shared_core_ftredge/services/base/base_firestore_service.py,sha256=leZFwxb1ruheypqudpKnuNtRQXtO4KNeoJk6ZACozHc,19512
|
|
31
|
+
ipulse_shared_core_ftredge/services/base/cache_aware_firestore_service.py,sha256=ya5Asff9BQodYnJVAw6M_Pm8WtVRPpEK7izFlZ2MyjA,10016
|
|
32
|
+
ipulse_shared_core_ftredge/services/catalog/__init__.py,sha256=ctc2nDGwsW_Ji4lk9pys3oyNwR_V-gHSbSHawym5fKQ,385
|
|
33
|
+
ipulse_shared_core_ftredge/services/catalog/catalog_subscriptionplan_service.py,sha256=X5xAi9sOk_F1ky0ECwPVlwIPPsN2PrZC6bN_pASGDjQ,9702
|
|
34
|
+
ipulse_shared_core_ftredge/services/catalog/catalog_usertype_service.py,sha256=C_VWxZ5iPcybjsSXdmZHyqS--rI3KY8pp7JDIy_L7S8,12833
|
|
35
|
+
ipulse_shared_core_ftredge/services/user/__init__.py,sha256=jmkD5XzAmaD8QV2UsgB5xynGcfsXliWtRtN2pt6kzbA,884
|
|
36
|
+
ipulse_shared_core_ftredge/services/user/user_core_service.py,sha256=o0trN4yTbyh-BJbXnin9XmS8hW5jOQW6RdegdQ2sRNo,28269
|
|
37
|
+
ipulse_shared_core_ftredge/services/user/user_multistep_operations.py,sha256=0MfMaKLGpVsgfD-Vgaa4s2dKk9nNoH6snWx_skkPy_o,39705
|
|
38
|
+
ipulse_shared_core_ftredge/services/user/user_permissions_operations.py,sha256=FByszIWo-qooLVXFTw0tGLWksIJEqHUPc_ZGwue0_pM,15753
|
|
39
|
+
ipulse_shared_core_ftredge/services/user/user_subscription_operations.py,sha256=z98EO67wHlnDj1V7JK14yq6yaIoTVcX5X5v4-taZFHw,21651
|
|
40
|
+
ipulse_shared_core_ftredge/services/user/userauth_operations.py,sha256=9l2uBAcAxbUnilK8MZ7IlHzaGiaPuqx7nIC51mAyR9w,36120
|
|
41
|
+
ipulse_shared_core_ftredge/services/user/userprofile_operations.py,sha256=_qyIEAQYCTV-subgP-5naMs_26apCpauomE6qmCCVWs,7333
|
|
42
|
+
ipulse_shared_core_ftredge/services/user/userstatus_operations.py,sha256=sW4Q-aMG1mjKvqVKK5AA93-G57FPBCkxO7rPfCkhBd8,22726
|
|
43
|
+
ipulse_shared_core_ftredge/utils/__init__.py,sha256=JnxUb8I2MRjJC7rBPXSrpwBIQDEOku5O9JsiTi3oun8,56
|
|
44
|
+
ipulse_shared_core_ftredge/utils/custom_json_encoder.py,sha256=DblQLD0KOSNDyQ58wQRogBrShIXzPIZUw_oGOBATnJY,1366
|
|
45
|
+
ipulse_shared_core_ftredge/utils/json_encoder.py,sha256=QkcaFneVv3-q-s__Dz4OiUWYnM6jgHDJrDMdPv09RCA,2093
|
|
46
|
+
ipulse_shared_core_ftredge-23.1.1.dist-info/licenses/LICENCE,sha256=YBtYAXNqCCOo9Mr2hfkbSPAM9CeAr2j1VZBSwQTrNwE,1060
|
|
47
|
+
ipulse_shared_core_ftredge-23.1.1.dist-info/METADATA,sha256=keIkjScj_B7Wix1MqoCIY8aR5JRAVkbdJwFd3HWnslg,782
|
|
48
|
+
ipulse_shared_core_ftredge-23.1.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
49
|
+
ipulse_shared_core_ftredge-23.1.1.dist-info/top_level.txt,sha256=8sgYrptpexkA_6_HyGvho26cVFH9kmtGvaK8tHbsGHk,27
|
|
50
|
+
ipulse_shared_core_ftredge-23.1.1.dist-info/RECORD,,
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
from datetime import datetime, timezone
|
|
2
|
-
from dateutil.relativedelta import relativedelta
|
|
3
|
-
import uuid
|
|
4
|
-
from typing import Set, Optional, ClassVar, Dict, Any, List, Union
|
|
5
|
-
from pydantic import Field, ConfigDict
|
|
6
|
-
from ipulse_shared_base_ftredge import Layer, Module, list_as_lower_strings, Subject, SubscriptionPlan, SubscriptionStatus
|
|
7
|
-
from ipulse_shared_base_ftredge.enums.enums_iam import IAMUnitType
|
|
8
|
-
from .base_data_model import BaseDataModel
|
|
9
|
-
# ORIGINAL AUTHOR ="russlan.ramdowar;russlan@ftredge.com"
|
|
10
|
-
# CLASS_ORGIN_DATE=datetime(2024, 2, 12, 20, 5)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
DEFAULT_SUBSCRIPTION_PLAN = SubscriptionPlan.FREE
|
|
14
|
-
DEFAULT_SUBSCRIPTION_STATUS = SubscriptionStatus.ACTIVE
|
|
15
|
-
|
|
16
|
-
############################################ !!!!! ALWAYS UPDATE SCHEMA VERSION , IF SCHEMA IS BEING MODIFIED !!! ############################################
|
|
17
|
-
class Subscription(BaseDataModel):
|
|
18
|
-
"""
|
|
19
|
-
Represents a single subscription cycle with enhanced flexibility and tracking.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
23
|
-
|
|
24
|
-
VERSION: ClassVar[float] = 3.0 # Incremented version for direct fields instead of computed
|
|
25
|
-
DOMAIN: ClassVar[str] = "_".join(list_as_lower_strings(Layer.PULSE_APP, Module.CORE.name, Subject.SUBSCRIPTION.name))
|
|
26
|
-
OBJ_REF: ClassVar[str] = "subscription"
|
|
27
|
-
|
|
28
|
-
# System-managed fields (read-only)
|
|
29
|
-
schema_version: float = Field(
|
|
30
|
-
default=VERSION,
|
|
31
|
-
description="Version of this Class == version of DB Schema",
|
|
32
|
-
frozen=True
|
|
33
|
-
)
|
|
34
|
-
|
|
35
|
-
# Unique identifier for this specific subscription instance - now auto-generated
|
|
36
|
-
uuid: str = Field(
|
|
37
|
-
default_factory=lambda: str(uuid.uuid4()),
|
|
38
|
-
description="Unique identifier for this subscription instance"
|
|
39
|
-
)
|
|
40
|
-
|
|
41
|
-
# Plan identification
|
|
42
|
-
plan_name: SubscriptionPlan = Field(
|
|
43
|
-
..., # Required field, no default
|
|
44
|
-
description="Subscription Plan Name"
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
plan_version: int = Field(
|
|
48
|
-
..., # Required field, no default
|
|
49
|
-
description="Version of the subscription plan"
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
# Direct field instead of computed
|
|
53
|
-
plan_id: str = Field(
|
|
54
|
-
..., # Required field, no default
|
|
55
|
-
description="Combined plan identifier (plan_name_plan_version)"
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
# Cycle duration fields
|
|
59
|
-
cycle_start_date: datetime = Field(
|
|
60
|
-
..., # Required field, no default
|
|
61
|
-
description="Subscription Cycle Start Date"
|
|
62
|
-
)
|
|
63
|
-
|
|
64
|
-
# Direct field instead of computed
|
|
65
|
-
cycle_end_date: datetime = Field(
|
|
66
|
-
..., # Required field, no default
|
|
67
|
-
description="Subscription Cycle End Date"
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# Fields for cycle calculation
|
|
71
|
-
validity_time_length: int = Field(
|
|
72
|
-
..., # Required field, no default
|
|
73
|
-
description="Length of subscription validity period (e.g., 1, 3, 12)"
|
|
74
|
-
)
|
|
75
|
-
|
|
76
|
-
validity_time_unit: str = Field(
|
|
77
|
-
..., # Required field, no default
|
|
78
|
-
description="Unit of subscription validity ('minute', 'hour', 'day', 'week', 'month', 'year')"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
# Renewal and status fields
|
|
82
|
-
auto_renew: bool = Field(
|
|
83
|
-
..., # Required field, no default
|
|
84
|
-
description="Auto-renewal status"
|
|
85
|
-
)
|
|
86
|
-
|
|
87
|
-
status: SubscriptionStatus = Field(
|
|
88
|
-
..., # Required field, no default
|
|
89
|
-
description="Subscription Status (active, trial, pending_confirmation, etc.)"
|
|
90
|
-
)
|
|
91
|
-
|
|
92
|
-
# IAM permissions structure
|
|
93
|
-
default_iam_domain_permissions: Dict[str, Dict[str, List[str]]] = Field(
|
|
94
|
-
..., # Required field, no default
|
|
95
|
-
description="IAM domain permissions granted by this subscription (domain -> IAM unit type -> list of unit references)"
|
|
96
|
-
)
|
|
97
|
-
|
|
98
|
-
fallback_plan_id: Optional[str] = Field(
|
|
99
|
-
..., # Required field (can be None), no default
|
|
100
|
-
description="ID of the plan to fall back to if this subscription expires"
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
price_paid_usd: float = Field(
|
|
104
|
-
..., # Required field, no default
|
|
105
|
-
description="Amount paid for this subscription in USD"
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
payment_ref: Optional[str] = Field(
|
|
109
|
-
default=None,
|
|
110
|
-
description="Reference to payment transaction"
|
|
111
|
-
)
|
|
112
|
-
|
|
113
|
-
# Credit management fields
|
|
114
|
-
subscription_based_insight_credits_per_update: int = Field(
|
|
115
|
-
default=0,
|
|
116
|
-
description="Number of insight credits to add on each update"
|
|
117
|
-
)
|
|
118
|
-
|
|
119
|
-
subscription_based_insight_credits_update_freq_h: int = Field(
|
|
120
|
-
default=24,
|
|
121
|
-
description="Frequency of insight credits update in hours"
|
|
122
|
-
)
|
|
123
|
-
|
|
124
|
-
extra_insight_credits_per_cycle: int = Field(
|
|
125
|
-
default=0,
|
|
126
|
-
description="Additional insight credits granted per subscription cycle"
|
|
127
|
-
)
|
|
128
|
-
|
|
129
|
-
voting_credits_per_update: int = Field(
|
|
130
|
-
default=0,
|
|
131
|
-
description="Number of voting credits to add on each update"
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
voting_credits_update_freq_h: int = Field(
|
|
135
|
-
default=62,
|
|
136
|
-
description="Frequency of voting credits update in hours"
|
|
137
|
-
)
|
|
138
|
-
|
|
139
|
-
# General metadata for extensibility
|
|
140
|
-
metadata: Dict[str, Any] = Field(
|
|
141
|
-
default_factory=dict,
|
|
142
|
-
description="Additional metadata for the subscription"
|
|
143
|
-
)
|
|
144
|
-
|
|
145
|
-
# Helper method to calculate cycle end date
|
|
146
|
-
@classmethod
|
|
147
|
-
def calculate_cycle_end_date(cls, start_date: datetime, validity_length: int, validity_unit: str) -> datetime:
|
|
148
|
-
"""Calculate the end date based on start date and validity period."""
|
|
149
|
-
if validity_unit == "minute":
|
|
150
|
-
return start_date + relativedelta(minutes=validity_length)
|
|
151
|
-
elif validity_unit == "hour":
|
|
152
|
-
return start_date + relativedelta(hours=validity_length)
|
|
153
|
-
elif validity_unit == "day":
|
|
154
|
-
return start_date + relativedelta(days=validity_length)
|
|
155
|
-
elif validity_unit == "week":
|
|
156
|
-
return start_date + relativedelta(weeks=validity_length)
|
|
157
|
-
elif validity_unit == "year":
|
|
158
|
-
return start_date + relativedelta(years=validity_length)
|
|
159
|
-
else: # Default to months
|
|
160
|
-
return start_date + relativedelta(months=validity_length)
|
|
161
|
-
|
|
162
|
-
# Methods for subscription management
|
|
163
|
-
def is_active(self) -> bool:
|
|
164
|
-
"""Check if the subscription is currently active."""
|
|
165
|
-
now = datetime.now(timezone.utc)
|
|
166
|
-
return (
|
|
167
|
-
self.status == SubscriptionStatus.ACTIVE and
|
|
168
|
-
self.cycle_start_date <= now <= self.cycle_end_date
|
|
169
|
-
)
|
|
170
|
-
|
|
171
|
-
def is_expired(self) -> bool:
|
|
172
|
-
"""Check if the subscription has expired."""
|
|
173
|
-
now = datetime.now(timezone.utc)
|
|
174
|
-
return now > self.cycle_end_date
|
|
175
|
-
|
|
176
|
-
def days_remaining(self) -> int:
|
|
177
|
-
"""Calculate the number of days remaining in the subscription."""
|
|
178
|
-
now = datetime.now(timezone.utc)
|
|
179
|
-
if now > self.cycle_end_date:
|
|
180
|
-
return 0
|
|
181
|
-
|
|
182
|
-
# Get time difference
|
|
183
|
-
time_diff = self.cycle_end_date - now
|
|
184
|
-
|
|
185
|
-
# If there's any time remaining but less than a day, return 1
|
|
186
|
-
if time_diff.days == 0 and time_diff.seconds > 0:
|
|
187
|
-
return 1
|
|
188
|
-
|
|
189
|
-
# Otherwise return the number of complete days
|
|
190
|
-
return time_diff.days
|