# -*- encoding: utf-8 -*- from uuid import uuid4 import os from django.db import models from django.conf import settings from django.utils import timezone from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin from django.utils.translation import gettext_lazy as _ from django.core.files.storage import FileSystemStorage from django.utils.deconstruct import deconstructible class OverwriteStorage(FileSystemStorage): def get_available_name(self, name, max_length=None): if self.exists(name): os.remove(os.path.join(self.location, name)) return name @deconstructible class IconPathGenerator: def __call__(self, instance, filename: str) -> str: return os.path.join("users", str(instance.uuid), "icon.webp") class UserManager(BaseUserManager): def create_user(self, email: str, forename: str, surname: str, password: str=None, **extra_fields): if not email: raise ValueError("Please provide an email address") email = self.normalize_email(email) user = self.model(email=email, forename=forename, surname=surname, **extra_fields) user.set_password(password) user.save() return user def create_superuser(self, email: str, forename: str, surname: str, password: str, **extra_fields): extra_fields.setdefault("is_staff", True) extra_fields.setdefault("is_superuser", True) extra_fields.setdefault("is_active", True) if not extra_fields.get("is_staff"): raise ValueError("Superuser must have is_staff=True") if not extra_fields.get("is_superuser"): raise ValueError("Superuser must have is_superuser=True") return self.create_user(email, forename, surname, password, **extra_fields) class User(AbstractBaseUser, PermissionsMixin): uuid = models.UUIDField(primary_key=True, default=uuid4, editable=False) icon = models.ImageField( _("profile picture"), upload_to=IconPathGenerator(), default="../static/images/defaultuser.webp", # Path starts from media dir storage=OverwriteStorage() ) email = models.EmailField( _("email address"), unique=True, error_messages = { "unique": _("A user with this email address already exists.") } ) forename = models.CharField( _("first name"), max_length=150, help_text=_("This should be your real first name.") ) surname = models.CharField( _("last name"), max_length=150, help_text=_("This should be your real last name.") ) create_timestamp = models.DateTimeField( _("Creation Date"), editable=True, default=timezone.now, help_text=_("When the user was created.") ) edit_timestamp = models.DateTimeField( _("Last Edited"), editable=True, default=timezone.now, help_text=_("When the user was last edited.") ) is_active = models.BooleanField( _("active status"), default=True, help_text=_('Use as a "soft delete" rather than deleting the user.') ) is_staff = models.BooleanField( _("staff status"), default=False, help_text=_("Designates whether the user can log into this admin site.") ) is_superuser = models.BooleanField( _("superuser status"), default=False, help_text=_("Designates whether the user has unrestricted site control.") ) USERNAME_FIELD = "email" EMAIL_FIELD = "email" REQUIRED_FIELDS = ["forename", "surname"] objects = UserManager() class Meta: verbose_name = _('user') verbose_name_plural = _('users') def __str__(self): return self.formal_fullname def save(self, *args, **kwargs): self.edit_timestamp = timezone.now() super().save(*args, **kwargs) def clean(self): super().clean() self.email = self.__class__.objects.normalize_email(self.email) @property def fullname(self) -> str: return f"{self.forename} {self.surname}" @property def formal_fullname(self) -> str: return f"{self.surname}, {self.forename}"