models.py 8.35 KB
Newer Older
Thom Wiggers's avatar
Thom Wiggers committed
1
2
3
4
5
from django.core import validators
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import Q
from django.utils import timezone
6
7
from django.utils.translation import ugettext_lazy as _, string_concat
from utils.translation import MultilingualField, ModelTranslateMeta
Thom Wiggers's avatar
Thom Wiggers committed
8
9


10
class Event(models.Model, metaclass=ModelTranslateMeta):
Thom Wiggers's avatar
Thom Wiggers committed
11
12
13
14
    """Represents events"""

    DEFAULT_NO_REGISTRATION_MESSAGE = _('No registration required')

15
16
17
18
19
    title = MultilingualField(
        models.CharField,
        _("title"),
        max_length=100
    )
Thom Wiggers's avatar
Thom Wiggers committed
20

21
22
23
24
    description = MultilingualField(
        models.TextField,
        _("description")
    )
Thom Wiggers's avatar
Thom Wiggers committed
25
26
27
28
29
30

    start = models.DateTimeField(_("start time"))

    end = models.DateTimeField(_("end time"))

    organiser = models.ForeignKey(
31
        'activemembers.Committee',
Thom Wiggers's avatar
Thom Wiggers committed
32
33
        models.SET_NULL,
        null=True,
34
        verbose_name=_("organiser")
Thom Wiggers's avatar
Thom Wiggers committed
35
36
37
38
39
40
41
42
43
44
45
    )

    registration_start = models.DateTimeField(
        _("registration start"),
        null=True,
        blank=True,
    )

    registration_end = models.DateTimeField(
        _("registration end"),
        null=True,
46
47
48
49
50
51
        blank=True
    )

    cancel_deadline = models.DateTimeField(
        _("cancel deadline"),
        null=True,
Thom Wiggers's avatar
Thom Wiggers committed
52
53
54
        blank=True
    )

55
56
57
58
59
    location = MultilingualField(
        models.CharField,
        _("location"),
        max_length=255,
    )
Thom Wiggers's avatar
Thom Wiggers committed
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

    map_location = models.CharField(
        _("location for minimap"),
        max_length=255,
        help_text=_('Location of Huygens: Heyendaalseweg 135, Nijmegen. '
                    'Not shown as text!!'),
    )

    price = models.DecimalField(
        _("price"),
        max_digits=5,
        decimal_places=2,
        default=0,
        validators=[validators.MinValueValidator(0)],
    )

    cost = models.DecimalField(
        _("cost"),
        max_digits=5,
        decimal_places=2,
        default=0,
        help_text=_("Actual cost of event."),
        validators=[validators.MinValueValidator(0)],
    )

    max_participants = models.PositiveSmallIntegerField(
        _('maximum number of participants'),
        blank=True,
        null=True,
    )

91
92
    no_registration_message = MultilingualField(
        models.CharField,
Thom Wiggers's avatar
Thom Wiggers committed
93
94
95
96
        _('message when there is no registration'),
        max_length=200,
        blank=True,
        null=True,
97
98
        help_text=(string_concat(_("Default: "),
                                 DEFAULT_NO_REGISTRATION_MESSAGE)),
Thom Wiggers's avatar
Thom Wiggers committed
99
100
101
102
    )

    published = models.BooleanField(_("published"), default=False)

103
104
105
    def registration_required(self):
        return bool(self.registration_start) or bool(self.registration_end)

Thom Wiggers's avatar
Thom Wiggers committed
106
107
    def clean(self):
        super().clean()
Thom Wiggers's avatar
Thom Wiggers committed
108
        errors = {}
Thom Wiggers's avatar
Thom Wiggers committed
109
        if self.end < self.start:
Thom Wiggers's avatar
Thom Wiggers committed
110
111
            errors.update({
                    'end': _("Can't have an event travel back in time")})
112
        if self.registration_required():
Thom Wiggers's avatar
Thom Wiggers committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
            if self.no_registration_message:
                errors.update(
                    {'no_registration_message': _(
                        "Doesn't make sense to have this if you require "
                        "registrations.")})
            if not self.registration_start:
                errors.update(
                    {'registration_start': _(
                        "If registration is required, you need a start of "
                        "registration")})
            if not self.registration_end:
                errors.update(
                    {'registration_end': _(
                        "If registration is required, you need an end of "
                        "registration")})
            if self.registration_start and self.registration_end and (
                    self.registration_start >= self.registration_end):
                message = _('Registration start should be before '
                            'registration end')
                errors.update({
                    'registration_start': message,
                    'registration_end': message})
        if errors:
            raise ValidationError(errors)
Thom Wiggers's avatar
Thom Wiggers committed
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
166
167
168
169
170
171
172
173
174
175
176
177
178
179

    def get_absolute_url(self):
        return ''

    def __str__(self):
        return '{}: {}'.format(
            self.title,
            timezone.localtime(self.start).strftime('%Y-%m-%d %H:%M'))

    class Meta:
        ordering = ('-start',)


class RegistrationInformationField(models.Model):
    """Field description to ask for when registering"""
    FIELD_TYPES = (('checkbox', _('checkbox')),
                   ('charfield', _('text field')),
                   ('intfield', _('integer field')))

    event = models.ForeignKey(Event, models.CASCADE)

    type = models.CharField(
        _('field type'),
        choices=FIELD_TYPES,
        max_length=10,
    )

    name = models.CharField(
        _('field name'),
        max_length=100,
    )

    description = models.TextField(
        _('description'),
        null=True,
        blank=True,
    )

    def get_value_for(self, registration):
        if self.type == 'charfield':
            value_set = self.textregistrationinformation_set
        elif self.type == 'checkbox':
            value_set = self.booleanregistrationinformation_set
180
        elif self.type == 'intfield':
Thom Wiggers's avatar
Thom Wiggers committed
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
            value_set = self.integerregistrationinformation_set
        try:
            return value_set.get(registration=registration).value
        except (TextRegistrationInformation.DoesNotExist,
                BooleanRegistrationInformation.DoesNotExist,
                IntegerRegistrationInformation.DoesNotExist):
            return None

    class Meta:
        order_with_respect_to = 'event'


class Registration(models.Model):
    """Event registrations"""

    event = models.ForeignKey(Event, models.CASCADE)

    member = models.ForeignKey(
        'members.Member', models.CASCADE,
        blank=True,
        null=True,
        limit_choices_to=(Q(user__membership__until__isnull=True) |
203
                          Q(user__membership__until__gt=timezone.now().date()))
Thom Wiggers's avatar
Thom Wiggers committed
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    )

    name = models.CharField(
        _('name'),
        max_length=50,
        help_text=_('Use this for non-members'),
        null=True,
        blank=True
    )

    date = models.DateTimeField(_('registration date'), auto_now_add=True)
    date_cancelled = models.DateTimeField(_('cancellation date'),
                                          null=True,
                                          blank=True)

    present = models.BooleanField(
220
        _('present'),
Thom Wiggers's avatar
Thom Wiggers committed
221
222
223
        default=False,
    )
    paid = models.BooleanField(
224
        _('paid'),
Thom Wiggers's avatar
Thom Wiggers committed
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
        default=False,
    )

    def registration_information(self):
        fields = self.event.registrationinformationfield_set.all()
        return [{'field': field, 'value': field.get_value_for(self)}
                for field in fields]

    def clean(self):
        if ((self.member is None and not self.name) or
                (self.member and self.name)):
            raise ValidationError({
                'member': _('Either specify a member or a name'),
                'name': _('Either specify a member or a name'),
            })

    def validate_unique(self, exclude=None):
        super().validate_unique(exclude)

    def __str__(self):
        if self.member:
            return '{}: {}'.format(self.member.get_full_name(), self.event)
        else:
            return '{}: {}'.format(self.name, self.event)

    class Meta:
        ordering = ('date',)
        unique_together = (('member', 'event', 'name', 'date_cancelled'),)


class AbstractRegistrationInformation(models.Model):
    """Abstract to contain common things for registration information"""

    registration = models.ForeignKey(Registration, models.CASCADE)
    field = models.ForeignKey(RegistrationInformationField, models.CASCADE)
    changed = models.DateTimeField(_('last changed'), auto_now=True)

    def __str__(self):
        return '{} - {}: {}'.format(self.registration, self.field, self.value)

    class Meta:
        abstract = True


class BooleanRegistrationInformation(AbstractRegistrationInformation):
    """Checkbox information filled in by members when registring"""

    value = models.BooleanField()


class TextRegistrationInformation(AbstractRegistrationInformation):
    """Checkbox information filled in by members when registring"""

    value = models.TextField()


class IntegerRegistrationInformation(AbstractRegistrationInformation):
    """Checkbox information filled in by members when registring"""

    value = models.IntegerField()