models.py 5.4 KB
Newer Older
1
2
3
4
5
#   models.py - models for accounts app
#
#   This file is part of debexpo
#   https://salsa.debian.org/mentors.debian.net-team/debexpo
#
6
#   Copyright © 2019 Baptiste Beauplat <lyknode@cilg.org>
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#
#   Permission is hereby granted, free of charge, to any person
#   obtaining a copy of this software and associated documentation
#   files (the "Software"), to deal in the Software without
#   restriction, including without limitation the rights to use,
#   copy, modify, merge, publish, distribute, sublicense, and/or sell
#   copies of the Software, and to permit persons to whom the
#   Software is furnished to do so, subject to the following
#   conditions:
#
#   The above copyright notice and this permission notice shall be
#   included in all copies or substantial portions of the Software.
#
#   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
#   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
#   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
#   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
#   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
#   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
#   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
#   OTHER DEALINGS IN THE SOFTWARE.

29
from email.utils import getaddresses
30
31
from enum import Enum

32
from django.contrib.auth.models import AbstractUser, BaseUserManager
33
from django.db import models
34
from django.utils.translation import gettext_lazy as _
35
36


37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
class UserStatus(int, Enum):
    def __new__(cls, value, label):
        obj = int.__new__(cls, value)
        obj._value_ = value
        obj.label = label
        obj.tuple = (value, label)
        return obj

    contributor = (1, _('Contributor'))
    maintainer = (2, _('Debian Maintainer (DM)'))
    developer = (3, _('Debian Developer (DD)'))

    @classmethod
    def as_tuple(cls):
        return (cls.contributor.tuple,
                cls.maintainer.tuple,
                cls.developer.tuple,)
54
55


56
57
58
59
60
61
62
63
class UserManager(BaseUserManager):
    """Define a model manager for User model with no username field."""

    use_in_migrations = True

    def _create_user(self, email, name, password, **extra_fields):
        """Create and save a User with the given email and password."""
        if not email:
64
            raise ValueError('The given email must not be null')
65
        if not name:
66
            raise ValueError('The given name must not be null')
67
68
69
70
71

        email = self.normalize_email(email)
        user = self.model(email=email, name=name, **extra_fields)

        user.set_password(password)
72
        user.full_clean()
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
        user.save(using=self._db)

        return user

    def create_user(self, email, name, password=None, **extra_fields):
        """Create and save a regular User with the given email and password."""
        extra_fields.setdefault('is_staff', False)
        extra_fields.setdefault('is_superuser', False)

        return self._create_user(email, name, password, **extra_fields)

    def create_superuser(self, name, email, password, **extra_fields):
        """Create and save a SuperUser with the given email and password."""
        extra_fields.setdefault('is_staff', True)
        extra_fields.setdefault('is_superuser', True)

        if extra_fields.get('is_staff') is not True:
            raise ValueError('Superuser must have is_staff=True.')
        if extra_fields.get('is_superuser') is not True:
            raise ValueError('Superuser must have is_superuser=True.')

        return self._create_user(email, name, password, **extra_fields)

96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
    def lookup_user_from_address(self, address):
        """
        Lookup a user using an arbitrary formatted email.

        This method is used by Changes to lookup the user of an unsigned upload,
        when allowed. The address is extracted from the .chagnes Changed-By
        field or, if missing, from the Maintainer field.

        The address format (Vincent Time <vtime@example.org>) is decoded using
        email.utils.getaddresses.
        """
        if not address:
            return

        decoded_address = getaddresses([address])
        email = decoded_address[0][1]

        try:
            return self.get(email=email)
        except User.DoesNotExist:
            return

118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139

class User(AbstractUser):
    """User model."""

    username = None
    first_name = None
    last_name = None
    email = models.EmailField(_('email address'), unique=True)
    name = models.CharField(_('full name'), max_length=150)

    USERNAME_FIELD = 'email'
    REQUIRED_FIELDS = []

    objects = UserManager()

    def get_full_name(self):
        return self.name

    def get_short_name(self):
        return self.name


140
141
142
class Countries(models.Model):
    name = models.CharField(max_length=100, unique=True)

Baptiste Beauplat's avatar
Baptiste Beauplat committed
143
144
145
    def __str__(self):
        return self.name

146
147
148
149
150
151
152
153
154

class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE)
    country = models.ForeignKey(Countries, on_delete=models.SET_NULL,
                                verbose_name=_('Country'), blank=True,
                                null=True)
    ircnick = models.CharField(max_length=100, blank=True,
                               verbose_name=_('IRC Nickname'))
    jabber = models.EmailField(blank=True, verbose_name=_('Jabber address'))
155
156
157
158
159
    status = models.PositiveSmallIntegerField(
        choices=UserStatus.as_tuple(),
        default=UserStatus.contributor.value,
        verbose_name=_('Status')
    )