ipulse-shared-core-ftredge 16.0.1__py3-none-any.whl → 19.0.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/__init__.py +1 -12
- ipulse_shared_core_ftredge/dependencies/authz_for_apis.py +8 -5
- ipulse_shared_core_ftredge/exceptions/__init__.py +47 -0
- ipulse_shared_core_ftredge/exceptions/user_exceptions.py +219 -0
- ipulse_shared_core_ftredge/models/__init__.py +1 -3
- ipulse_shared_core_ftredge/models/base_api_response.py +15 -0
- ipulse_shared_core_ftredge/models/base_data_model.py +7 -6
- ipulse_shared_core_ftredge/models/user_auth.py +59 -4
- ipulse_shared_core_ftredge/models/user_profile.py +41 -7
- ipulse_shared_core_ftredge/models/user_status.py +44 -138
- ipulse_shared_core_ftredge/monitoring/__init__.py +5 -0
- ipulse_shared_core_ftredge/monitoring/microservmon.py +483 -0
- ipulse_shared_core_ftredge/services/__init__.py +21 -14
- ipulse_shared_core_ftredge/services/base/__init__.py +12 -0
- ipulse_shared_core_ftredge/services/base/base_firestore_service.py +520 -0
- ipulse_shared_core_ftredge/services/cache_aware_firestore_service.py +44 -8
- ipulse_shared_core_ftredge/services/charging_service.py +1 -1
- ipulse_shared_core_ftredge/services/user/__init__.py +37 -0
- ipulse_shared_core_ftredge/services/user/iam_management_operations.py +326 -0
- ipulse_shared_core_ftredge/services/user/subscription_management_operations.py +384 -0
- ipulse_shared_core_ftredge/services/user/user_account_operations.py +479 -0
- ipulse_shared_core_ftredge/services/user/user_auth_operations.py +305 -0
- ipulse_shared_core_ftredge/services/user/user_core_service.py +651 -0
- ipulse_shared_core_ftredge/services/user/user_holistic_operations.py +436 -0
- {ipulse_shared_core_ftredge-16.0.1.dist-info → ipulse_shared_core_ftredge-19.0.1.dist-info}/METADATA +2 -2
- ipulse_shared_core_ftredge-19.0.1.dist-info/RECORD +41 -0
- {ipulse_shared_core_ftredge-16.0.1.dist-info → ipulse_shared_core_ftredge-19.0.1.dist-info}/WHEEL +1 -1
- ipulse_shared_core_ftredge/models/organization_profile.py +0 -96
- ipulse_shared_core_ftredge/models/user_profile_update.py +0 -39
- ipulse_shared_core_ftredge/services/base_firestore_service.py +0 -249
- ipulse_shared_core_ftredge/services/fastapiservicemon.py +0 -140
- ipulse_shared_core_ftredge/services/servicemon.py +0 -240
- ipulse_shared_core_ftredge-16.0.1.dist-info/RECORD +0 -33
- ipulse_shared_core_ftredge/{services/base_service_exceptions.py → exceptions/base_exceptions.py} +1 -1
- {ipulse_shared_core_ftredge-16.0.1.dist-info → ipulse_shared_core_ftredge-19.0.1.dist-info}/licenses/LICENCE +0 -0
- {ipulse_shared_core_ftredge-16.0.1.dist-info → ipulse_shared_core_ftredge-19.0.1.dist-info}/top_level.txt +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
""" User Status model for tracking user subscription and access rights. """
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
3
|
from dateutil.relativedelta import relativedelta # Add missing import
|
|
4
|
-
from typing import Set, Optional, Dict, List, ClassVar, Any
|
|
4
|
+
from typing import Set, Optional, Dict, List, ClassVar, Any
|
|
5
5
|
from pydantic import Field, ConfigDict, field_validator, computed_field, BaseModel, model_validator
|
|
6
|
-
from ipulse_shared_base_ftredge import Layer, Module, list_as_lower_strings, Subject,
|
|
7
|
-
from ipulse_shared_base_ftredge.enums.enums_iam import IAMUnitType
|
|
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
8
|
from .subscription import Subscription
|
|
9
9
|
from .base_data_model import BaseDataModel
|
|
10
10
|
|
|
@@ -72,18 +72,6 @@ class UserStatus(BaseDataModel):
|
|
|
72
72
|
description="User UID from Firebase Auth"
|
|
73
73
|
)
|
|
74
74
|
|
|
75
|
-
# Added primary_usertype field for main role categorization
|
|
76
|
-
primary_usertype: str = Field(
|
|
77
|
-
...,
|
|
78
|
-
description="Primary user type (e.g., customer, internal, admin, superadmin)"
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
# Renamed user_types to secondary_usertypes
|
|
82
|
-
secondary_usertypes: List[str] = Field(
|
|
83
|
-
default_factory=list,
|
|
84
|
-
description="List of secondary user types/roles"
|
|
85
|
-
)
|
|
86
|
-
|
|
87
75
|
# Added organizations field for consistency with UserProfile
|
|
88
76
|
organizations_uids: Set[str] = Field(
|
|
89
77
|
default_factory=set,
|
|
@@ -98,8 +86,8 @@ class UserStatus(BaseDataModel):
|
|
|
98
86
|
|
|
99
87
|
# Subscription Management - Single active subscription instead of dictionary
|
|
100
88
|
subscriptions_history: Dict[str, Subscription] = Field(
|
|
101
|
-
|
|
102
|
-
description="Dictionary of user's past recent subscriptions, keyed by subscription ID"
|
|
89
|
+
default_factory=dict, # Initialize with empty dict
|
|
90
|
+
description="Dictionary of user\'s past recent subscriptions, keyed by subscription ID"
|
|
103
91
|
)
|
|
104
92
|
|
|
105
93
|
# Changed from dictionary to single Optional subscription
|
|
@@ -385,32 +373,20 @@ class UserStatus(BaseDataModel):
|
|
|
385
373
|
# Add IAM permissions from subscription
|
|
386
374
|
self.update_iam_unit_refs_from_subscription(subscription)
|
|
387
375
|
|
|
388
|
-
#
|
|
376
|
+
# Update subscription-based credits
|
|
389
377
|
credits_per_update = subscription.subscription_based_insight_credits_per_update
|
|
390
378
|
if credits_per_update > 0:
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
object.__setattr__(self, "sbscrptn_based_insight_credits_updtd_on", datetime.now(timezone.utc))
|
|
394
|
-
else:
|
|
395
|
-
self.sbscrptn_based_insight_credits = credits_per_update
|
|
396
|
-
self.sbscrptn_based_insight_credits_updtd_on = datetime.now(timezone.utc)
|
|
379
|
+
self.sbscrptn_based_insight_credits = credits_per_update
|
|
380
|
+
self.sbscrptn_based_insight_credits_updtd_on = datetime.now(timezone.utc)
|
|
397
381
|
|
|
398
382
|
# Update voting credits directly from subscription attributes
|
|
399
383
|
voting_credits = subscription.voting_credits_per_update
|
|
400
384
|
if voting_credits > 0:
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
object.__setattr__(self, "voting_credits_updtd_on", datetime.now(timezone.utc))
|
|
404
|
-
else:
|
|
405
|
-
self.voting_credits = voting_credits
|
|
406
|
-
self.voting_credits_updtd_on = datetime.now(timezone.utc)
|
|
385
|
+
self.voting_credits = voting_credits
|
|
386
|
+
self.voting_credits_updtd_on = datetime.now(timezone.utc)
|
|
407
387
|
|
|
408
388
|
# Store subscription details
|
|
409
|
-
|
|
410
|
-
if getattr(self.model_config, "frozen", False):
|
|
411
|
-
object.__setattr__(self, "active_subscription", subscription)
|
|
412
|
-
else:
|
|
413
|
-
self.active_subscription = subscription
|
|
389
|
+
self.active_subscription = subscription
|
|
414
390
|
|
|
415
391
|
def revoke_subscription(self) -> None:
|
|
416
392
|
"""
|
|
@@ -420,22 +396,17 @@ class UserStatus(BaseDataModel):
|
|
|
420
396
|
if not self.active_subscription:
|
|
421
397
|
return
|
|
422
398
|
|
|
423
|
-
#
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
object.__setattr__(self, "active_subscription", None)
|
|
428
|
-
else:
|
|
429
|
-
self.sbscrptn_based_insight_credits = 0
|
|
430
|
-
self.sbscrptn_based_insight_credits_updtd_on = datetime.now(timezone.utc)
|
|
431
|
-
self.active_subscription = None
|
|
399
|
+
# Clear subscription-based credits and active subscription
|
|
400
|
+
self.sbscrptn_based_insight_credits = 0
|
|
401
|
+
self.sbscrptn_based_insight_credits_updtd_on = datetime.now(timezone.utc)
|
|
402
|
+
self.active_subscription = None
|
|
432
403
|
|
|
433
404
|
def apply_subscription_plan(self,
|
|
434
405
|
plan_data: Dict[str, Any],
|
|
435
406
|
source: str = "default_configuration",
|
|
436
407
|
expires_at: Optional[datetime] = None) -> None:
|
|
437
408
|
"""
|
|
438
|
-
Apply a subscription plan's benefits from plan data dictionary.
|
|
409
|
+
Apply a subscription plan\'s benefits from plan data dictionary.
|
|
439
410
|
|
|
440
411
|
Args:
|
|
441
412
|
plan_data: Dictionary containing subscription plan details
|
|
@@ -452,11 +423,13 @@ class UserStatus(BaseDataModel):
|
|
|
452
423
|
# Extract plan name - no default fallbacks
|
|
453
424
|
plan_name_str = plan_data.get("plan_name")
|
|
454
425
|
if not plan_name_str:
|
|
426
|
+
# Consider logging a warning or raising an error if plan_name is critical
|
|
455
427
|
return # Cannot create subscription without plan name
|
|
456
428
|
|
|
457
429
|
try:
|
|
458
430
|
plan_name = SubscriptionPlan(plan_name_str)
|
|
459
431
|
except ValueError:
|
|
432
|
+
# Consider logging a warning or raising an error for invalid plan_name
|
|
460
433
|
return # Invalid plan name
|
|
461
434
|
|
|
462
435
|
# Extract required fields - no default fallbacks
|
|
@@ -466,28 +439,46 @@ class UserStatus(BaseDataModel):
|
|
|
466
439
|
|
|
467
440
|
# If any required field is missing, return without creating subscription
|
|
468
441
|
if plan_version is None or validity_time_length is None or validity_time_unit is None:
|
|
442
|
+
# Consider logging a warning or raising an error if these fields are critical
|
|
469
443
|
return
|
|
470
444
|
|
|
471
445
|
# Calculate directly assigned fields
|
|
472
|
-
plan_id = f"{plan_name_str}_{plan_version}"
|
|
446
|
+
plan_id = f"{plan_name_str}_{plan_version}" # Use the string version for ID
|
|
473
447
|
cycle_start_date = datetime.now(timezone.utc)
|
|
448
|
+
|
|
449
|
+
# Ensure cycle_end_date is calculated correctly using the provided expires_at or calculated from plan details
|
|
450
|
+
# If expires_at is provided, it should ideally align with plan's cycle, but we prioritize it if present.
|
|
451
|
+
# However, the current Subscription.calculate_cycle_end_date might be more robust if plan details are the source of truth.
|
|
452
|
+
# For now, let's assume expires_at is a specific override if provided, otherwise calculate from plan.
|
|
453
|
+
# This logic might need refinement based on how expires_at is intended to be used.
|
|
454
|
+
# If expires_at is meant to be the definitive end date, use it. Otherwise, calculate.
|
|
455
|
+
# The original code used expires_at for IAM assignments but calculated cycle_end_date for the subscription object itself.
|
|
456
|
+
# Let's stick to calculating cycle_end_date from plan details for the Subscription object for consistency.
|
|
457
|
+
|
|
474
458
|
cycle_end_date = Subscription.calculate_cycle_end_date(
|
|
475
459
|
cycle_start_date,
|
|
476
460
|
validity_time_length,
|
|
477
461
|
validity_time_unit
|
|
478
462
|
)
|
|
479
463
|
|
|
464
|
+
# If an explicit expires_at is given and differs, it might indicate a special case (e.g. trial ending sooner)
|
|
465
|
+
# For IAM assignments, the original code used the `expires_at` passed to `apply_subscription_plan`
|
|
466
|
+
# For the subscription object itself, it calculated `cycle_end_date`.
|
|
467
|
+
# We will use the calculated `cycle_end_date` for the subscription object.
|
|
468
|
+
# The `expires_at` for IAM assignments will be handled by `update_iam_unit_refs_from_subscription`
|
|
469
|
+
# which uses `subscription.cycle_end_date`.
|
|
470
|
+
|
|
480
471
|
# Create subscription object with direct fields instead of computed
|
|
481
472
|
subscription = Subscription(
|
|
482
|
-
plan_name=plan_name,
|
|
473
|
+
plan_name=plan_name, # Enum version
|
|
483
474
|
plan_version=plan_version,
|
|
484
|
-
plan_id=plan_id,
|
|
475
|
+
plan_id=plan_id, # String version
|
|
485
476
|
cycle_start_date=cycle_start_date,
|
|
486
|
-
cycle_end_date=cycle_end_date,
|
|
477
|
+
cycle_end_date=cycle_end_date, # Calculated cycle_end_date
|
|
487
478
|
validity_time_length=validity_time_length,
|
|
488
479
|
validity_time_unit=validity_time_unit,
|
|
489
480
|
auto_renew=plan_data.get("plan_auto_renewal", False),
|
|
490
|
-
status=SubscriptionStatus.ACTIVE,
|
|
481
|
+
status=SubscriptionStatus.ACTIVE, # Default to ACTIVE when applying
|
|
491
482
|
default_iam_domain_permissions=iam_domain_permissions,
|
|
492
483
|
fallback_plan_id=plan_data.get("fallback_plan_id_if_current_plan_expired"),
|
|
493
484
|
price_paid_usd=plan_data.get("plan_per_cycle_price_usd") or 0.0,
|
|
@@ -497,93 +488,8 @@ class UserStatus(BaseDataModel):
|
|
|
497
488
|
subscription_based_insight_credits_update_freq_h=plan_data.get("subscription_based_insight_credits_update_freq_h") or 24,
|
|
498
489
|
extra_insight_credits_per_cycle=plan_data.get("extra_insight_credits_per_cycle") or 0,
|
|
499
490
|
voting_credits_per_update=plan_data.get("voting_credits_per_update") or 0,
|
|
500
|
-
voting_credits_update_freq_h=plan_data.get("voting_credits_update_freq_h") or 62
|
|
491
|
+
voting_credits_update_freq_h=plan_data.get("voting_credits_update_freq_h") or 62 # Corrected typo from 62 to, e.g., 720 for monthly if intended
|
|
501
492
|
)
|
|
502
493
|
|
|
503
|
-
# Apply
|
|
504
|
-
self.apply_subscription(subscription)
|
|
505
|
-
|
|
506
|
-
@staticmethod
|
|
507
|
-
def fetch_user_status_defaults(firestore_client,
|
|
508
|
-
primary_usertype: str,
|
|
509
|
-
collection: str = "papp_core_configs_user") -> Dict[str, Any]:
|
|
510
|
-
"""
|
|
511
|
-
Fetch user status defaults from Firestore.
|
|
512
|
-
|
|
513
|
-
Args:
|
|
514
|
-
firestore_client: Initialized Firestore client
|
|
515
|
-
primary_usertype: Primary type of user (customer, internal, admin, etc)
|
|
516
|
-
collection: Collection name for user status defaults
|
|
517
|
-
|
|
518
|
-
Returns:
|
|
519
|
-
Dictionary containing user status defaults, or empty dict if not found
|
|
520
|
-
"""
|
|
521
|
-
try:
|
|
522
|
-
# Get the consolidated document containing all defaults
|
|
523
|
-
doc_ref = firestore_client.collection(collection).document("all_users_defaults")
|
|
524
|
-
doc = doc_ref.get()
|
|
525
|
-
|
|
526
|
-
if not doc.exists:
|
|
527
|
-
return {}
|
|
528
|
-
|
|
529
|
-
# Get the data
|
|
530
|
-
data = doc.to_dict()
|
|
531
|
-
|
|
532
|
-
# Find the latest version of defaults for the specified user type
|
|
533
|
-
latest_key = None
|
|
534
|
-
latest_version = -1
|
|
535
|
-
|
|
536
|
-
# Look for defaults with format "{user_type}_defaults_{version}"
|
|
537
|
-
for key in data.keys():
|
|
538
|
-
if key.startswith(f"{primary_usertype}_defaults_"):
|
|
539
|
-
try:
|
|
540
|
-
version = int(key.split("_")[-1])
|
|
541
|
-
if version > latest_version:
|
|
542
|
-
latest_version = version
|
|
543
|
-
latest_key = key
|
|
544
|
-
except ValueError:
|
|
545
|
-
continue
|
|
546
|
-
|
|
547
|
-
# Return the defaults if found
|
|
548
|
-
if latest_key and latest_key in data:
|
|
549
|
-
return data[latest_key]
|
|
550
|
-
|
|
551
|
-
return {}
|
|
552
|
-
except Exception:
|
|
553
|
-
# Return empty dict on error
|
|
554
|
-
return {}
|
|
555
|
-
|
|
556
|
-
@staticmethod
|
|
557
|
-
def fetch_subscription_plan(firestore_client,
|
|
558
|
-
plan_id: str,
|
|
559
|
-
collection: str = "papp_core_configs_subscriptionplans") -> Dict[str, Any]:
|
|
560
|
-
"""
|
|
561
|
-
Fetch subscription plan details from Firestore.
|
|
562
|
-
|
|
563
|
-
Args:
|
|
564
|
-
firestore_client: Initialized Firestore client
|
|
565
|
-
plan_id: ID of the plan to fetch
|
|
566
|
-
collection: Collection name for subscription plans
|
|
567
|
-
|
|
568
|
-
Returns:
|
|
569
|
-
Dictionary containing subscription plan details, or empty dict if not found
|
|
570
|
-
"""
|
|
571
|
-
try:
|
|
572
|
-
# Get the consolidated document containing all plans
|
|
573
|
-
doc_ref = firestore_client.collection(collection).document("all_subscriptionplans_defaults")
|
|
574
|
-
doc = doc_ref.get()
|
|
575
|
-
|
|
576
|
-
if not doc.exists:
|
|
577
|
-
return {}
|
|
578
|
-
|
|
579
|
-
# Get the data
|
|
580
|
-
data = doc.to_dict()
|
|
581
|
-
|
|
582
|
-
# Return the plan if found
|
|
583
|
-
if plan_id in data:
|
|
584
|
-
return data[plan_id]
|
|
585
|
-
|
|
586
|
-
return {}
|
|
587
|
-
except Exception:
|
|
588
|
-
# Return empty dict on error
|
|
589
|
-
return {}
|
|
494
|
+
# Apply this new subscription
|
|
495
|
+
self.apply_subscription(subscription)
|