-
Notifications
You must be signed in to change notification settings - Fork 1
/
profile.py
165 lines (136 loc) 路 4.73 KB
/
profile.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
from __future__ import annotations
import enum
from phonenumber_field.modelfields import PhoneNumberField
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import CharField, CheckConstraint, TextChoices
from django.utils.translation import gettext_lazy as _
from django_countries.fields import CountryField
from django_lifecycle import AFTER_SAVE, LifecycleModelMixin, hook
from apps.files.storage import NamespacedFilesStorage
from apps.utils.models import BaseTimestampedModel
from apps.utils.models.query import Q
user_profile_picture_storage = NamespacedFilesStorage("profile-picture")
class UserProfileState(TextChoices):
# CREATED = 'created', _('Created')
INCOMPLETE = "incomplete", _("Uncompleted")
COMPLETE = "complete", _("Completed")
class UserProfile(LifecycleModelMixin, BaseTimestampedModel):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.RESTRICT,
related_name="profile",
verbose_name=_("user"),
)
# ### FIELDS, which are conditionaly REQUIRED ###
# see AccountsConfiguration
nationality = CountryField(
verbose_name=_("nationality"),
blank=True,
null=True,
)
class Gender(TextChoices):
MALE = "male", _("male")
FEMALE = "female", _("female")
DECLINE_TO_STATE = "decline_to_state", _("decline to state")
OTHER = "other", _("other")
gender = CharField(
verbose_name=_("gender"),
blank=True,
null=True,
choices=Gender.choices,
max_length=16,
)
home_university = models.ForeignKey(
"universities.University",
on_delete=models.RESTRICT,
verbose_name=_("home university"),
help_text=_("home university for all users"),
related_name="home_university_user_profiles",
null=True,
blank=True,
db_index=True,
)
home_faculty = models.ForeignKey(
"universities.Faculty",
on_delete=models.RESTRICT,
verbose_name=_("home faculty"),
# TODO: help, not description
help_text=_("home faculty for members, empty for internationals"),
related_name="home_faculty_user_profiles",
null=True,
blank=True,
db_index=True,
)
guest_faculty = models.ForeignKey(
"universities.Faculty",
on_delete=models.RESTRICT,
verbose_name=_("guest faculty"),
help_text=_("guest faculty for international students, empty for members"),
related_name="guest_user_profiles",
blank=True,
null=True,
db_index=True,
)
picture = models.ImageField(
storage=user_profile_picture_storage,
upload_to=user_profile_picture_storage.upload_to,
verbose_name=_("profile picture"),
null=True,
blank=True,
)
# TODO: phone, profiles
phone_number = PhoneNumberField(
null=True, blank=True, verbose_name=("phone number")
)
@enum.unique
class Preferences(enum.Flag):
WEEKLY_UPDATES = enum.auto()
# TODO: push notifications & emails
# TODO: define formfield/widget to handle flagging
preferences = models.PositiveSmallIntegerField(
default=0, verbose_name=_("user preferences as flags")
)
State = UserProfileState
state = models.CharField(
verbose_name=_("state"),
max_length=16,
choices=State.choices,
default=State.INCOMPLETE,
)
class Meta:
verbose_name = _("user profile")
verbose_name_plural = _("user profiles")
constraints = (
CheckConstraint(
# home university XOR home faculty
check=Q(state=UserProfileState.INCOMPLETE.value)
| (Q(home_university=None) ^ Q(home_faculty=None)),
name="home_university_or_faculty",
),
)
def clean(self):
super().clean()
if not (bool(self.home_university) ^ bool(self.home_faculty)):
raise ValidationError(
{
"home_university": _(
# TODO: weird, basically it's exactly one of these
"At least one from home university/faculty has to be set."
)
}
)
@hook(AFTER_SAVE)
def on_save(self):
from apps.accounts.services import UserProfileStateSynchronizer
UserProfileStateSynchronizer.on_user_profile_update(profile=self)
def __str__(self):
return (
f"{self.user} {self.nationality} "
f"{self.home_university or self.home_faculty.university} "
)
__all__ = [
"UserProfile",
"user_profile_picture_storage",
]