ipulse-shared-core-ftredge 8.1.1__tar.gz → 10.1.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-8.1.1/src/ipulse_shared_core_ftredge.egg-info → ipulse_shared_core_ftredge-10.1.1}/PKG-INFO +2 -2
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/setup.py +2 -2
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/base_api_response.py +12 -4
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/subscription.py +44 -33
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/user_profile.py +5 -5
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/user_status.py +26 -16
- ipulse_shared_core_ftredge-10.1.1/src/ipulse_shared_core_ftredge/utils/custom_json_encoder.py +46 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1/src/ipulse_shared_core_ftredge.egg-info}/PKG-INFO +2 -2
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge.egg-info/SOURCES.txt +1 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge.egg-info/requires.txt +1 -1
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/LICENCE +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/README.md +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/pyproject.toml +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/setup.cfg +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/__init__.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/dependencies/__init__.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/dependencies/auth_firebase_token_validation.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/dependencies/auth_protected_router.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/dependencies/firestore_client.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/__init__.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/base_data_model.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/organization_profile.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/user_auth.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/models/user_profile_update.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/services/__init__.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/services/base_firestore_service.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/services/base_service_exceptions.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/services/fastapiservicemon.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/services/servicemon.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/utils/__init__.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge/utils/json_encoder.py +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.1}/src/ipulse_shared_core_ftredge.egg-info/dependency_links.txt +0 -0
- {ipulse_shared_core_ftredge-8.1.1 → ipulse_shared_core_ftredge-10.1.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: 10.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
|
|
@@ -13,7 +13,7 @@ Requires-Dist: pydantic[email]~=2.5
|
|
|
13
13
|
Requires-Dist: python-dateutil~=2.8
|
|
14
14
|
Requires-Dist: fastapi~=0.115.8
|
|
15
15
|
Requires-Dist: pytest
|
|
16
|
-
Requires-Dist: ipulse_shared_base_ftredge>=
|
|
16
|
+
Requires-Dist: ipulse_shared_base_ftredge>=6.4.1
|
|
17
17
|
Dynamic: author
|
|
18
18
|
Dynamic: classifier
|
|
19
19
|
Dynamic: home-page
|
|
@@ -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='10.1.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=[
|
|
@@ -12,7 +12,7 @@ setup(
|
|
|
12
12
|
'python-dateutil~=2.8',
|
|
13
13
|
'fastapi~=0.115.8',
|
|
14
14
|
'pytest',
|
|
15
|
-
'ipulse_shared_base_ftredge>=
|
|
15
|
+
'ipulse_shared_base_ftredge>=6.4.1',
|
|
16
16
|
],
|
|
17
17
|
author='Russlan Ramdowar',
|
|
18
18
|
description='Shared Core models and Logger util for the Pulse platform project. Using AI for financial advisory and investment management.',
|
|
@@ -3,10 +3,7 @@ import datetime as dt
|
|
|
3
3
|
import json
|
|
4
4
|
from pydantic import BaseModel, ConfigDict
|
|
5
5
|
from fastapi.responses import JSONResponse
|
|
6
|
-
from ipulse_shared_core_ftredge.utils
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
from ipulse_shared_core_ftredge.utils import CustomJSONEncoder
|
|
10
7
|
|
|
11
8
|
|
|
12
9
|
T = TypeVar('T')
|
|
@@ -30,6 +27,17 @@ class PaginatedAPIResponse(BaseAPIResponse, Generic[T]):
|
|
|
30
27
|
|
|
31
28
|
class CustomJSONResponse(JSONResponse):
|
|
32
29
|
def render(self, content) -> bytes:
|
|
30
|
+
# Handle Pydantic models to exclude computed fields
|
|
31
|
+
if isinstance(content, dict) and "data" in content and hasattr(content["data"], "model_dump"):
|
|
32
|
+
# If content["data"] is a Pydantic model, use model_dump with exclude_unset=True
|
|
33
|
+
# and exclude_computed=True to prevent serialization of computed fields
|
|
34
|
+
content = dict(content) # Create a copy to avoid modifying the original
|
|
35
|
+
content["data"] = content["data"].model_dump(
|
|
36
|
+
exclude_unset=True,
|
|
37
|
+
exclude_computed=True
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# Use the CustomJSONEncoder for serialization
|
|
33
41
|
return json.dumps(
|
|
34
42
|
content,
|
|
35
43
|
ensure_ascii=False,
|
|
@@ -2,7 +2,7 @@ from datetime import datetime, timezone
|
|
|
2
2
|
from dateutil.relativedelta import relativedelta
|
|
3
3
|
import uuid
|
|
4
4
|
from typing import Set, Optional, ClassVar, Dict, Any, List, Union
|
|
5
|
-
from pydantic import Field, ConfigDict
|
|
5
|
+
from pydantic import Field, ConfigDict
|
|
6
6
|
from ipulse_shared_base_ftredge import Layer, Module, list_as_lower_strings, Subject, SubscriptionPlan, SubscriptionStatus
|
|
7
7
|
from ipulse_shared_base_ftredge.enums.enums_iam import IAMUnitType
|
|
8
8
|
from .base_data_model import BaseDataModel
|
|
@@ -21,7 +21,7 @@ class Subscription(BaseDataModel):
|
|
|
21
21
|
|
|
22
22
|
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
23
23
|
|
|
24
|
-
VERSION: ClassVar[float] =
|
|
24
|
+
VERSION: ClassVar[float] = 3.0 # Incremented version for direct fields instead of computed
|
|
25
25
|
DOMAIN: ClassVar[str] = "_".join(list_as_lower_strings(Layer.PULSE_APP, Module.CORE.name, Subject.SUBSCRIPTION.name))
|
|
26
26
|
OBJ_REF: ClassVar[str] = "subscription"
|
|
27
27
|
|
|
@@ -49,14 +49,11 @@ class Subscription(BaseDataModel):
|
|
|
49
49
|
description="Version of the subscription plan"
|
|
50
50
|
)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
Example: "free_subscription_1"
|
|
58
|
-
"""
|
|
59
|
-
return f"{self.plan_name.value}_{self.plan_version}"
|
|
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
|
+
)
|
|
60
57
|
|
|
61
58
|
# Cycle duration fields
|
|
62
59
|
cycle_start_date: datetime = Field(
|
|
@@ -64,7 +61,13 @@ class Subscription(BaseDataModel):
|
|
|
64
61
|
description="Subscription Cycle Start Date"
|
|
65
62
|
)
|
|
66
63
|
|
|
67
|
-
#
|
|
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
|
|
68
71
|
validity_time_length: int = Field(
|
|
69
72
|
..., # Required field, no default
|
|
70
73
|
description="Length of subscription validity period (e.g., 1, 3, 12)"
|
|
@@ -75,23 +78,6 @@ class Subscription(BaseDataModel):
|
|
|
75
78
|
description="Unit of subscription validity ('minute', 'hour', 'day', 'week', 'month', 'year')"
|
|
76
79
|
)
|
|
77
80
|
|
|
78
|
-
# Computed cycle_end_date based on start date and validity
|
|
79
|
-
@computed_field
|
|
80
|
-
def cycle_end_date(self) -> datetime:
|
|
81
|
-
"""Calculate the end date based on start date and validity period."""
|
|
82
|
-
if self.validity_time_unit == "minute":
|
|
83
|
-
return self.cycle_start_date + relativedelta(minutes=self.validity_time_length)
|
|
84
|
-
elif self.validity_time_unit == "hour":
|
|
85
|
-
return self.cycle_start_date + relativedelta(hours=self.validity_time_length)
|
|
86
|
-
elif self.validity_time_unit == "day":
|
|
87
|
-
return self.cycle_start_date + relativedelta(days=self.validity_time_length)
|
|
88
|
-
elif self.validity_time_unit == "week":
|
|
89
|
-
return self.cycle_start_date + relativedelta(weeks=self.validity_time_length)
|
|
90
|
-
elif self.validity_time_unit == "year":
|
|
91
|
-
return self.cycle_start_date + relativedelta(years=self.validity_time_length)
|
|
92
|
-
else: # Default to months
|
|
93
|
-
return self.cycle_start_date + relativedelta(months=self.validity_time_length)
|
|
94
|
-
|
|
95
81
|
# Renewal and status fields
|
|
96
82
|
auto_renew: bool = Field(
|
|
97
83
|
..., # Required field, no default
|
|
@@ -103,9 +89,8 @@ class Subscription(BaseDataModel):
|
|
|
103
89
|
description="Subscription Status (active, trial, pending_confirmation, etc.)"
|
|
104
90
|
)
|
|
105
91
|
|
|
106
|
-
#
|
|
107
|
-
|
|
108
|
-
iam_domain_permissions: Dict[str, Dict[str, List[str]]] = Field(
|
|
92
|
+
# IAM permissions structure
|
|
93
|
+
default_iam_domain_permissions: Dict[str, Dict[str, List[str]]] = Field(
|
|
109
94
|
..., # Required field, no default
|
|
110
95
|
description="IAM domain permissions granted by this subscription (domain -> IAM unit type -> list of unit references)"
|
|
111
96
|
)
|
|
@@ -125,7 +110,7 @@ class Subscription(BaseDataModel):
|
|
|
125
110
|
description="Reference to payment transaction"
|
|
126
111
|
)
|
|
127
112
|
|
|
128
|
-
#
|
|
113
|
+
# Credit management fields
|
|
129
114
|
subscription_based_insight_credits_per_update: int = Field(
|
|
130
115
|
default=0,
|
|
131
116
|
description="Number of insight credits to add on each update"
|
|
@@ -157,6 +142,23 @@ class Subscription(BaseDataModel):
|
|
|
157
142
|
description="Additional metadata for the subscription"
|
|
158
143
|
)
|
|
159
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
|
+
|
|
160
162
|
# Methods for subscription management
|
|
161
163
|
def is_active(self) -> bool:
|
|
162
164
|
"""Check if the subscription is currently active."""
|
|
@@ -176,4 +178,13 @@ class Subscription(BaseDataModel):
|
|
|
176
178
|
now = datetime.now(timezone.utc)
|
|
177
179
|
if now > self.cycle_end_date:
|
|
178
180
|
return 0
|
|
179
|
-
|
|
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
|
|
@@ -16,7 +16,7 @@ class UserProfile(BaseDataModel):
|
|
|
16
16
|
model_config = ConfigDict(frozen=False, extra="forbid") # Allow field modification
|
|
17
17
|
|
|
18
18
|
# Class constants
|
|
19
|
-
VERSION: ClassVar[float] = 5.0 # Incremented version for
|
|
19
|
+
VERSION: ClassVar[float] = 5.0 # Incremented version for primary_usertype addition
|
|
20
20
|
DOMAIN: ClassVar[str] = "_".join(list_as_lower_strings(Layer.PULSE_APP, Module.CORE.name, Subject.USER.name))
|
|
21
21
|
OBJ_REF: ClassVar[str] = "userprofile"
|
|
22
22
|
|
|
@@ -36,14 +36,14 @@ class UserProfile(BaseDataModel):
|
|
|
36
36
|
description="User UID from Firebase Auth"
|
|
37
37
|
)
|
|
38
38
|
|
|
39
|
-
# Added
|
|
40
|
-
|
|
39
|
+
# Added primary_usertype field for main role categorization
|
|
40
|
+
primary_usertype: str = Field(
|
|
41
41
|
...,
|
|
42
42
|
description="Primary user type (e.g., customer, internal, admin, superadmin)"
|
|
43
43
|
)
|
|
44
44
|
|
|
45
|
-
# Renamed user_types to
|
|
46
|
-
|
|
45
|
+
# Renamed user_types to secondary_usertypes
|
|
46
|
+
secondary_usertypes: List[str] = Field(
|
|
47
47
|
default_factory=list,
|
|
48
48
|
description="List of secondary user types"
|
|
49
49
|
)
|
|
@@ -47,7 +47,7 @@ class UserStatus(BaseDataModel):
|
|
|
47
47
|
model_config = ConfigDict(frozen=False, extra="forbid")
|
|
48
48
|
|
|
49
49
|
# Class constants
|
|
50
|
-
VERSION: ClassVar[float] = 5.
|
|
50
|
+
VERSION: ClassVar[float] = 5.1 # Incremented version for removing computed fields
|
|
51
51
|
DOMAIN: ClassVar[str] = "_".join(list_as_lower_strings(Layer.PULSE_APP, Module.CORE.name, Subject.USER.name))
|
|
52
52
|
OBJ_REF: ClassVar[str] = "userstatus"
|
|
53
53
|
|
|
@@ -68,14 +68,14 @@ class UserStatus(BaseDataModel):
|
|
|
68
68
|
description="User UID from Firebase Auth"
|
|
69
69
|
)
|
|
70
70
|
|
|
71
|
-
# Added
|
|
72
|
-
|
|
71
|
+
# Added primary_usertype field for main role categorization
|
|
72
|
+
primary_usertype: str = Field(
|
|
73
73
|
...,
|
|
74
74
|
description="Primary user type (e.g., customer, internal, admin, superadmin)"
|
|
75
75
|
)
|
|
76
76
|
|
|
77
|
-
# Renamed user_types to
|
|
78
|
-
|
|
77
|
+
# Renamed user_types to secondary_usertypes
|
|
78
|
+
secondary_usertypes: List[str] = Field(
|
|
79
79
|
default_factory=list,
|
|
80
80
|
description="List of secondary user types/roles"
|
|
81
81
|
)
|
|
@@ -321,7 +321,7 @@ class UserStatus(BaseDataModel):
|
|
|
321
321
|
"""
|
|
322
322
|
added_count = 0
|
|
323
323
|
|
|
324
|
-
for domain, permissions_by_type in subscription.
|
|
324
|
+
for domain, permissions_by_type in subscription.default_iam_domain_permissions.items():
|
|
325
325
|
for iam_unit_type_str, iam_unit_refs in permissions_by_type.items():
|
|
326
326
|
# Convert string to enum if needed for internal processing
|
|
327
327
|
try:
|
|
@@ -353,14 +353,14 @@ class UserStatus(BaseDataModel):
|
|
|
353
353
|
"""
|
|
354
354
|
return self.update_iam_unit_refs_from_subscription(subscription)
|
|
355
355
|
|
|
356
|
-
|
|
356
|
+
# Method instead of computed field
|
|
357
357
|
def is_subscription_active(self) -> bool:
|
|
358
358
|
"""Check if the user has an active subscription."""
|
|
359
359
|
if self.active_subscription:
|
|
360
360
|
return self.active_subscription.is_active()
|
|
361
361
|
return False
|
|
362
362
|
|
|
363
|
-
|
|
363
|
+
# Method instead of computed field
|
|
364
364
|
def subscription_expires_in_days(self) -> Optional[int]:
|
|
365
365
|
"""Get days until subscription expiration."""
|
|
366
366
|
if self.active_subscription and self.active_subscription.is_active():
|
|
@@ -464,21 +464,31 @@ class UserStatus(BaseDataModel):
|
|
|
464
464
|
if plan_version is None or validity_time_length is None or validity_time_unit is None:
|
|
465
465
|
return
|
|
466
466
|
|
|
467
|
-
#
|
|
467
|
+
# Calculate directly assigned fields
|
|
468
|
+
plan_id = f"{plan_name_str}_{plan_version}"
|
|
469
|
+
cycle_start_date = datetime.now(timezone.utc)
|
|
470
|
+
cycle_end_date = Subscription.calculate_cycle_end_date(
|
|
471
|
+
cycle_start_date,
|
|
472
|
+
validity_time_length,
|
|
473
|
+
validity_time_unit
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
# Create subscription object with direct fields instead of computed
|
|
468
477
|
subscription = Subscription(
|
|
469
478
|
plan_name=plan_name,
|
|
470
479
|
plan_version=plan_version,
|
|
471
|
-
|
|
480
|
+
plan_id=plan_id,
|
|
481
|
+
cycle_start_date=cycle_start_date,
|
|
482
|
+
cycle_end_date=cycle_end_date,
|
|
472
483
|
validity_time_length=validity_time_length,
|
|
473
484
|
validity_time_unit=validity_time_unit,
|
|
474
|
-
auto_renew=plan_data.get("plan_auto_renewal", False),
|
|
485
|
+
auto_renew=plan_data.get("plan_auto_renewal", False),
|
|
475
486
|
status=SubscriptionStatus.ACTIVE,
|
|
476
|
-
|
|
487
|
+
default_iam_domain_permissions=iam_domain_permissions,
|
|
477
488
|
fallback_plan_id=plan_data.get("fallback_plan_id_if_current_plan_expired"),
|
|
478
489
|
price_paid_usd=plan_data.get("plan_per_cycle_price_usd") or 0.0,
|
|
479
490
|
created_by=source,
|
|
480
491
|
updated_by=source,
|
|
481
|
-
# Direct attributes for credit-related fields
|
|
482
492
|
subscription_based_insight_credits_per_update=plan_data.get("subscription_based_insight_credits_per_update") or 0,
|
|
483
493
|
subscription_based_insight_credits_update_freq_h=plan_data.get("subscription_based_insight_credits_update_freq_h") or 24,
|
|
484
494
|
extra_insight_credits_per_cycle=plan_data.get("extra_insight_credits_per_cycle") or 0,
|
|
@@ -491,14 +501,14 @@ class UserStatus(BaseDataModel):
|
|
|
491
501
|
|
|
492
502
|
@staticmethod
|
|
493
503
|
def fetch_user_status_defaults(firestore_client,
|
|
494
|
-
|
|
504
|
+
primary_usertype: str,
|
|
495
505
|
collection: str = "papp_core_configs_user") -> Dict[str, Any]:
|
|
496
506
|
"""
|
|
497
507
|
Fetch user status defaults from Firestore.
|
|
498
508
|
|
|
499
509
|
Args:
|
|
500
510
|
firestore_client: Initialized Firestore client
|
|
501
|
-
|
|
511
|
+
primary_usertype: Primary type of user (customer, internal, admin, etc)
|
|
502
512
|
collection: Collection name for user status defaults
|
|
503
513
|
|
|
504
514
|
Returns:
|
|
@@ -521,7 +531,7 @@ class UserStatus(BaseDataModel):
|
|
|
521
531
|
|
|
522
532
|
# Look for defaults with format "{user_type}_defaults_{version}"
|
|
523
533
|
for key in data.keys():
|
|
524
|
-
if key.startswith(f"{
|
|
534
|
+
if key.startswith(f"{primary_usertype}_defaults_"):
|
|
525
535
|
try:
|
|
526
536
|
version = int(key.split("_")[-1])
|
|
527
537
|
if version > latest_version:
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import datetime
|
|
3
|
+
from typing import Any
|
|
4
|
+
from enum import Enum
|
|
5
|
+
from uuid import UUID
|
|
6
|
+
from decimal import Decimal
|
|
7
|
+
from pydantic import BaseModel
|
|
8
|
+
|
|
9
|
+
class CustomJSONEncoder(json.JSONEncoder):
|
|
10
|
+
"""Custom JSON encoder to handle special types like datetimes, UUIDs, and Pydantic models."""
|
|
11
|
+
|
|
12
|
+
def default(self, obj: Any) -> Any:
|
|
13
|
+
# Handle datetime objects
|
|
14
|
+
if isinstance(obj, datetime.datetime):
|
|
15
|
+
return obj.isoformat()
|
|
16
|
+
|
|
17
|
+
# Handle date objects
|
|
18
|
+
elif isinstance(obj, datetime.date):
|
|
19
|
+
return obj.isoformat()
|
|
20
|
+
|
|
21
|
+
# Handle time objects
|
|
22
|
+
elif isinstance(obj, datetime.time):
|
|
23
|
+
return obj.isoformat()
|
|
24
|
+
|
|
25
|
+
# Handle UUID objects
|
|
26
|
+
elif isinstance(obj, UUID):
|
|
27
|
+
return str(obj)
|
|
28
|
+
|
|
29
|
+
# Handle Decimal objects
|
|
30
|
+
elif isinstance(obj, Decimal):
|
|
31
|
+
return float(obj)
|
|
32
|
+
|
|
33
|
+
# Handle Enum objects
|
|
34
|
+
elif isinstance(obj, Enum):
|
|
35
|
+
return obj.value
|
|
36
|
+
|
|
37
|
+
# Handle Pydantic models - exclude computed fields
|
|
38
|
+
elif isinstance(obj, BaseModel):
|
|
39
|
+
return obj.model_dump(exclude_computed=True)
|
|
40
|
+
|
|
41
|
+
# Handle sets by converting to sorted lists for consistent output
|
|
42
|
+
elif isinstance(obj, set):
|
|
43
|
+
return sorted(list(obj))
|
|
44
|
+
|
|
45
|
+
# For everything else, use the default encoder
|
|
46
|
+
return super().default(obj)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ipulse_shared_core_ftredge
|
|
3
|
-
Version:
|
|
3
|
+
Version: 10.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
|
|
@@ -13,7 +13,7 @@ Requires-Dist: pydantic[email]~=2.5
|
|
|
13
13
|
Requires-Dist: python-dateutil~=2.8
|
|
14
14
|
Requires-Dist: fastapi~=0.115.8
|
|
15
15
|
Requires-Dist: pytest
|
|
16
|
-
Requires-Dist: ipulse_shared_base_ftredge>=
|
|
16
|
+
Requires-Dist: ipulse_shared_base_ftredge>=6.4.1
|
|
17
17
|
Dynamic: author
|
|
18
18
|
Dynamic: classifier
|
|
19
19
|
Dynamic: home-page
|
|
@@ -28,4 +28,5 @@ src/ipulse_shared_core_ftredge/services/base_service_exceptions.py
|
|
|
28
28
|
src/ipulse_shared_core_ftredge/services/fastapiservicemon.py
|
|
29
29
|
src/ipulse_shared_core_ftredge/services/servicemon.py
|
|
30
30
|
src/ipulse_shared_core_ftredge/utils/__init__.py
|
|
31
|
+
src/ipulse_shared_core_ftredge/utils/custom_json_encoder.py
|
|
31
32
|
src/ipulse_shared_core_ftredge/utils/json_encoder.py
|
|
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
|