from uuid import uuid4 from django.contrib.auth.models import AbstractUser from django.contrib.auth.models import Group as BaseGroup from django.core.cache import cache from django.db.models import ( BooleanField, CharField, EmailField, ImageField, JSONField, UUIDField, ) from django.utils.translation import gettext_lazy as _ from rest_framework_simplejwt.token_blacklist.models import ( BlacklistedToken as BaseBlacklistedToken, ) from rest_framework_simplejwt.token_blacklist.models import ( OutstandingToken as BaseOutstandingToken, ) from core.abstract import NiceModel from evibes.settings import LANGUAGE_CODE, LANGUAGES from vibes_auth.managers import UserManager from vibes_auth.validators import validate_phone_number class User(AbstractUser, NiceModel): # type: ignore [django-manager-missing] __doc__ = _( "Represents a User entity with customized fields and methods for extended functionality. " # type: ignore "This class extends the AbstractUser model and integrates additional features like " "custom email login, validation methods, subscription status, verification, and " "attributes storage. It also provides utilities for managing recently viewed items and " "token-based activation for verifying accounts. The User model is designed to handle " "specific use cases for enhanced user management." ) def get_uuid_as_path(self, *args): return "users/" + str(self.uuid) + "/" + args[0] email = EmailField(_("email"), unique=True, help_text=_("user email address")) phone_number = CharField( _("phone_number"), max_length=20, unique=True, blank=True, null=True, help_text=_("user phone number"), validators=[ validate_phone_number, ], ) username: None = None # type: ignore [assignment] first_name = CharField(_("first_name"), max_length=150, blank=True, null=True) # type: ignore [assignment] last_name = CharField(_("last_name"), max_length=150, blank=True, null=True) # type: ignore [assignment] avatar = ImageField( null=True, verbose_name=_("avatar"), upload_to=get_uuid_as_path, blank=True, help_text=_("user profile image"), ) is_verified = BooleanField( default=False, verbose_name=_("is verified"), help_text=_("user verification status"), ) is_active = BooleanField( _("is_active"), default=False, help_text=_("unselect this instead of deleting accounts"), ) is_subscribed = BooleanField( verbose_name=_("is_subscribed"), help_text=_("user's newsletter subscription status"), default=False ) activation_token = UUIDField(default=uuid4, verbose_name=_("activation token")) language = CharField(choices=LANGUAGES, default=LANGUAGE_CODE, null=False, blank=False, max_length=7) attributes = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True) USERNAME_FIELD = "email" REQUIRED_FIELDS = [] # noinspection PyClassVar objects = UserManager() # type: ignore [misc, assignment] def add_to_recently_viewed(self, product_uuid): recently_viewed = self.recently_viewed if product_uuid not in recently_viewed: if not len(recently_viewed) >= 48: recently_viewed.append(product_uuid) cache.set(f"user_{self.uuid}_rv", recently_viewed) else: recently_viewed.pop(0) recently_viewed.append(product_uuid) cache.set(f"user_{self.uuid}_rv", recently_viewed) @property def recently_viewed(self): return cache.get(f"user_{self.uuid}_rv", []) def check_token(self, token): return str(token) == str(self.activation_token) def __str__(self): return self.email class Meta: swappable = "AUTH_USER_MODEL" verbose_name = _("user") verbose_name_plural = _("users") class Group(BaseGroup): """ Proxy model representing a Group entity. This class acts as a proxy for the `BaseGroup` model, providing additional configuration for display and usage purposes. It does not introduce new fields or behavior but alters representation and metadata for the Group model. Attributes: Meta.proxy (bool): Indicates this is a proxy model. Meta.verbose_name (str): Human-readable singular name for the Group model. Meta.verbose_name_plural (str): Human-readable plural name for the Group model. """ class Meta: proxy = True verbose_name = _("group") verbose_name_plural = _("groups") class OutstandingToken(BaseOutstandingToken): """ Represents a proxy model for outstanding tokens. This class is a proxy for the `BaseOutstandingToken` model, used to manage and customize behaviors or configurations related to outstanding tokens. It does not add additional fields or logic to the base model but allows for overloading or extending its default functionality as required. """ class Meta: proxy = True verbose_name = _("outstanding token") verbose_name_plural = _("outstanding tokens") class BlacklistedToken(BaseBlacklistedToken): """ Represents a blacklisted token model in the system. This class acts as a Django model proxy and serves the purpose of managing blacklisted tokens. It inherits from a base class provided for this purpose, allowing for the extension of functionality or customization of behavior without altering the original base class's structure. It also defines the Meta options for the model, affecting its database and administrative representation. """ class Meta: proxy = True verbose_name = _("blacklisted token") verbose_name_plural = _("blacklisted tokens")