Fixes: 1) Corrected `admin.py` imports for consistency and model alignment; Extra: Refactored `choices.py` for reusable enums; restructured `models.py` for clarity and maintainability.
190 lines
6.8 KiB
Python
190 lines
6.8 KiB
Python
from uuid import uuid4
|
|
|
|
from django.conf import settings
|
|
from django.contrib.auth.models import AbstractUser
|
|
from django.contrib.auth.models import Group as BaseGroup
|
|
from django.core.cache import cache
|
|
from django.core.exceptions import ValidationError
|
|
from django.db.models import (
|
|
BooleanField,
|
|
CharField,
|
|
EmailField,
|
|
ImageField,
|
|
JSONField,
|
|
UUIDField,
|
|
ForeignKey,
|
|
TextField,
|
|
DateTimeField,
|
|
Index,
|
|
CASCADE,
|
|
SET_NULL,
|
|
)
|
|
from django.utils import timezone
|
|
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 engine.core.abstract import NiceModel
|
|
from engine.vibes_auth.choices import SenderType, ThreadStatus
|
|
from engine.vibes_auth.managers import UserManager
|
|
from engine.vibes_auth.validators import validate_phone_number
|
|
from engine.payments.models import Balance
|
|
|
|
|
|
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=settings.LANGUAGES, default=settings.LANGUAGE_CODE, null=False, blank=False, max_length=7
|
|
)
|
|
attributes = JSONField(verbose_name=_("attributes"), default=dict, blank=True, null=True)
|
|
|
|
payments_balance: "Balance"
|
|
|
|
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 ChatThread(NiceModel):
|
|
user = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="chat_threads")
|
|
email = EmailField(blank=True, default="", help_text=_("For anonymous threads"))
|
|
assigned_to = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="assigned_chat_threads")
|
|
status = CharField(max_length=16, choices=ThreadStatus.choices, default=ThreadStatus.OPEN)
|
|
last_message_at = DateTimeField(default=timezone.now)
|
|
attributes = JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
indexes = [
|
|
Index(fields=["status", "modified"], name="chatthread_status_mod_idx"),
|
|
Index(fields=["assigned_to", "status"], name="chatthread_assigned_status_idx"),
|
|
Index(fields=["user"], name="chatthread_user_idx"),
|
|
Index(fields=["email"], name="chatthread_email_idx"),
|
|
]
|
|
ordering = ("-modified",)
|
|
verbose_name = _("Chat thread")
|
|
verbose_name_plural = _("Chat threads")
|
|
|
|
def clean(self) -> None:
|
|
super().clean()
|
|
if not self.user and not self.email:
|
|
raise ValidationError({"email": _("provide user or email for anonymous thread.")})
|
|
if self.assigned_to and not self.assigned_to.is_staff:
|
|
raise ValidationError({"assigned_to": _("assignee must be a staff user.")})
|
|
|
|
|
|
class ChatMessage(NiceModel):
|
|
thread = ForeignKey(ChatThread, on_delete=CASCADE, related_name="messages")
|
|
sender_type = CharField(max_length=16, choices=SenderType.choices)
|
|
sender_user = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="chat_messages")
|
|
text = TextField()
|
|
sent_at = DateTimeField(default=timezone.now)
|
|
attributes = JSONField(default=dict, blank=True)
|
|
|
|
class Meta:
|
|
indexes = [
|
|
Index(fields=["thread", "sent_at"], name="chatmessage_thread_sent_idx"),
|
|
]
|
|
ordering = ("sent_at", "uuid")
|
|
verbose_name = _("Chat message")
|
|
verbose_name_plural = _("Chat messages")
|
|
|
|
|
|
class Group(BaseGroup):
|
|
class Meta:
|
|
proxy = True
|
|
verbose_name = _("group")
|
|
verbose_name_plural = _("groups")
|
|
|
|
|
|
class OutstandingToken(BaseOutstandingToken):
|
|
class Meta:
|
|
proxy = True
|
|
verbose_name = _("outstanding token")
|
|
verbose_name_plural = _("outstanding tokens")
|
|
|
|
|
|
class BlacklistedToken(BaseBlacklistedToken):
|
|
class Meta:
|
|
proxy = True
|
|
verbose_name = _("blacklisted token")
|
|
verbose_name_plural = _("blacklisted tokens")
|