Files @ 71805cc66e00
Branch filter:

Location: symposion_app/vendor/symposion/schedule/models.py - annotation

bsturmfels
Add additional account app templates

This works around use block "body" when we need block "content".
  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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
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
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
c4db94b7e5fd
c4db94b7e5fd
7c102aefa3a5
252697b842c0
143dbbce2891
3207621058b8
143dbbce2891
2cce659c28b4
a36ff64a8205
5b7087f47829
5b7087f47829
5f311ca7bd4e
5b7087f47829
252697b842c0
252697b842c0
143dbbce2891
143dbbce2891
2cce659c28b4
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
3207621058b8
36ab6d599ffc
a95825ede8d4
a95825ede8d4
36ab6d599ffc
2cce659c28b4
2cce659c28b4
2cce659c28b4
1b579610618f
1b579610618f
3207621058b8
3207621058b8
143dbbce2891
143dbbce2891
143dbbce2891
2cce659c28b4
2cce659c28b4
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
36ab6d599ffc
a95825ede8d4
2cce659c28b4
2cce659c28b4
2cce659c28b4
2cce659c28b4
36ab6d599ffc
77dc781e0d8b
77dc781e0d8b
1b579610618f
3207621058b8
3207621058b8
143dbbce2891
143dbbce2891
77dc781e0d8b
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
3207621058b8
36ab6d599ffc
6b17e021ca61
765e80765c3e
77dc781e0d8b
fab25ad0f5c0
fab25ad0f5c0
fab25ad0f5c0
3207621058b8
3207621058b8
3207621058b8
3207621058b8
77dc781e0d8b
765e80765c3e
765e80765c3e
252697b842c0
252697b842c0
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
765e80765c3e
77dc781e0d8b
77dc781e0d8b
77dc781e0d8b
77dc781e0d8b
77dc781e0d8b
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
36ab6d599ffc
a95825ede8d4
2cce659c28b4
143dbbce2891
3207621058b8
3207621058b8
3207621058b8
3207621058b8
143dbbce2891
143dbbce2891
2cce659c28b4
36ab6d599ffc
cdec6e225865
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
3207621058b8
4838adf7758c
4838adf7758c
4838adf7758c
4838adf7758c
4838adf7758c
11f697d13757
11f697d13757
11f697d13757
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
f98a3f2f9a46
f98a3f2f9a46
f98a3f2f9a46
36ab6d599ffc
f98a3f2f9a46
f98a3f2f9a46
f98a3f2f9a46
f98a3f2f9a46
2f6930ae11c6
2f6930ae11c6
2f6930ae11c6
2f6930ae11c6
36ab6d599ffc
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
7c102aefa3a5
36ab6d599ffc
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
0ebcc2f1247a
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
0ebcc2f1247a
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
c4db94b7e5fd
0ebcc2f1247a
f30dce6a61a8
f30dce6a61a8
f30dce6a61a8
36ab6d599ffc
c0402575475e
2cce659c28b4
ac71aa170b5b
a1cbcdddd5d1
c0402575475e
c0402575475e
c0402575475e
494b8ffb1779
2cce659c28b4
2cce659c28b4
2cce659c28b4
36ab6d599ffc
d98f9b82a85b
590b25d5f8ab
3207621058b8
3207621058b8
2c954c3a055b
2c954c3a055b
2c954c3a055b
2c954c3a055b
2c954c3a055b
2c954c3a055b
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
36ab6d599ffc
a95825ede8d4
a95825ede8d4
36ab6d599ffc
2c954c3a055b
2c954c3a055b
d98f9b82a85b
3207621058b8
3207621058b8
7324d0303162
7324d0303162
7324d0303162
2cce659c28b4
36ab6d599ffc
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
11f697d13757
11f697d13757
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
5f311ca7bd4e
3207621058b8
3b4a51e6d43e
3b4a51e6d43e
3b4a51e6d43e
3b4a51e6d43e
3207621058b8
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
36ab6d599ffc
11f697d13757
11f697d13757
11f697d13757
11f697d13757
402a02097fb6
402a02097fb6
402a02097fb6
36ab6d599ffc
5b7087f47829
5b7087f47829
db66749046bd
db66749046bd
db66749046bd
36ab6d599ffc
7324d0303162
7324d0303162
7324d0303162
582597b7b85d
582597b7b85d
36ab6d599ffc
a95825ede8d4
a95825ede8d4
36ab6d599ffc
d98f9b82a85b
d98f9b82a85b
3207621058b8
3207621058b8
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
34e250322c07
34e250322c07
34e250322c07
34e250322c07
34e250322c07
34e250322c07
8da6d1809805
8da6d1809805
8da6d1809805
8da6d1809805
8da6d1809805
8da6d1809805
8da6d1809805
8da6d1809805
a95825ede8d4
a41fb8bd3542
a41fb8bd3542
8da6d1809805
a41fb8bd3542
8da6d1809805
a41fb8bd3542
a41fb8bd3542
8da6d1809805
8da6d1809805
a41fb8bd3542
a95825ede8d4
a41fb8bd3542
3207621058b8
3207621058b8
3207621058b8
3207621058b8
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
3207621058b8
3207621058b8
a41fb8bd3542
a41fb8bd3542
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
252697b842c0
3207621058b8
3207621058b8
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
a41fb8bd3542
3207621058b8
3207621058b8
a41fb8bd3542
a95825ede8d4
a95825ede8d4
a95825ede8d4
import datetime

from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth import get_user_model
from django.db import models
from django.utils.translation import ugettext_lazy as _

from symposion.managers import DefaultSelectRelatedManager
from symposion.text_parser import parse
from symposion.proposals.models import ProposalBase
from symposion.conference.models import Section
from symposion.speakers.models import Speaker

User = get_user_model()


class Schedule(models.Model):
    objects = DefaultSelectRelatedManager()

    section = models.OneToOneField(
        Section,
        verbose_name=_("Section"),
        on_delete=models.CASCADE,
    )
    published = models.BooleanField(default=True, verbose_name=_("Published"))
    hidden = models.BooleanField(_("Hide schedule from overall conference view"), default=False)

    def __str__(self):
        return "%s Schedule" % self.section

    class SymposionMeta:
        select_related = ('section', )

    class Meta:
        ordering = ["section"]
        verbose_name = _('Schedule')
        verbose_name_plural = _('Schedules')


class Day(models.Model):
    objects = DefaultSelectRelatedManager()


    schedule = models.ForeignKey(
        Schedule,
        verbose_name=_("Schedule"),
        on_delete=models.CASCADE,
    )
    date = models.DateField(verbose_name=_("Date"))

    def __str__(self):
        return "%s: %s" % (self.schedule.section.name, self.date)

    class SymposionMeta:
        select_related = ('schedule__section', )

    class Meta:
        unique_together = [("schedule", "date")]
        ordering = ["date"]
        verbose_name = _("date")
        verbose_name_plural = _("dates")


class Room(models.Model):

    schedule = models.ForeignKey(
        Schedule,
        verbose_name=_("Schedule"),
        on_delete=models.CASCADE,
    )
    name = models.CharField(max_length=65, verbose_name=_("Name"))
    order = models.PositiveIntegerField(verbose_name=_("Order"))

    def __unicode__(self):
        return self.name

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _("Room")
        verbose_name_plural = _("Rooms")


class Track(models.Model):
    name = models.CharField(max_length=80, verbose_name=_("Track"))
    room = models.ForeignKey(Room, on_delete=models.CASCADE)
    day = models.ForeignKey(Day, on_delete=models.CASCADE)

    def __str__(self):
        return self.name

    class Meta:
        unique_together = [('room', 'day')]
        verbose_name = _("Track")
        verbose_name_plural = _("Tracks")


class SlotKind(models.Model):
    """
    A slot kind represents what kind a slot is. For example, a slot can be a
    break, lunch, or X-minute talk.
    """

    schedule = models.ForeignKey(
        Schedule,
        verbose_name=_("schedule"),
        on_delete=models.CASCADE,
    )
    label = models.CharField(max_length=50, verbose_name=_("Label"))

    def __str__(self):
        return ": ".join((self.schedule.section.name, self.label))

    class Meta:
        verbose_name = _("Slot kind")
        verbose_name_plural = _("Slot kinds")


class Slot(models.Model):
    objects = DefaultSelectRelatedManager()

    name = models.CharField(max_length=512, editable=False)
    day = models.ForeignKey(
        Day,
        verbose_name=_("Day"),
        on_delete=models.CASCADE,
    )
    kind = models.ForeignKey(
        SlotKind,
        verbose_name=_("Kind"),
        on_delete=models.CASCADE,
    )
    start = models.TimeField(verbose_name=_("Start"))
    end = models.TimeField(verbose_name=_("End"))
    exclusive = models.BooleanField(
        default=False,
        help_text=_("Set to true if this is the only event during this "
                    "timeslot"),
    )
    content_override = models.TextField(blank=True, verbose_name=_("Content override"))
    content_override_html = models.TextField(blank=True)

    def assign(self, content):
        """
        Assign the given content to this slot and if a previous slot content
        was given we need to unlink it to avoid integrity errors.
        """
        self.unassign()
        content.slot = self
        content.save()

    def unassign(self):
        """
        Unassign the associated content with this slot.
        """
        content = self.content
        if content and content.slot_id:
            content.slot = None
            content.save()

    @property
    def content(self):
        """
        Return the content this slot represents.
        @@@ hard-coded for presentation for now
        """
        try:
            return self.content_ptr
        except ObjectDoesNotExist:
            return None

    @property
    def start_datetime(self):
        return datetime.datetime(
            self.day.date.year,
            self.day.date.month,
            self.day.date.day,
            self.start.hour,
            self.start.minute)

    @property
    def end_datetime(self):
        return datetime.datetime(
            self.day.date.year,
            self.day.date.month,
            self.day.date.day,
            self.end.hour,
            self.end.minute)

    @property
    def length_in_minutes(self):
        return int(
            (self.end_datetime - self.start_datetime).total_seconds() / 60)

    @property
    def rooms(self):
        return Room.objects.filter(pk__in=self.slotroom_set.values("room"))

    def save(self, *args, **kwargs):
        roomlist = ' '.join(map(str, self.rooms))
        self.name = "%s %s (%s - %s) %s" % (self.day.date, self.kind.label, self.start, self.end, roomlist)
        self.content_override_html = parse(self.content_override)
        super(Slot, self).save(*args, **kwargs)

    def __str__(self):
        return "%s: %s" % (self.day.schedule.section.name, self.name)

    class SymposionMeta:
        select_related = ('kind__schedule__section', 'day')

    class Meta:
        ordering = ["day__schedule__section__name", "day", "start" ]
        verbose_name = _("slot")
        verbose_name_plural = _("slots")


class SlotRoom(models.Model):
    """
    Links a slot with a room.
    """

    slot = models.ForeignKey(
        Slot,
        verbose_name=_("Slot"),
        on_delete=models.CASCADE,
    )
    room = models.ForeignKey(
        Room,
        verbose_name=_("Room"),
        on_delete=models.CASCADE,
    )

    def __str__(self):
        return "%s %s" % (self.room, self.slot)

    class Meta:
        unique_together = [("slot", "room")]
        ordering = ["slot", "room__order"]
        verbose_name = _("Slot room")
        verbose_name_plural = _("Slot rooms")


class Presentation(models.Model):
    objects = DefaultSelectRelatedManager()

    slot = models.OneToOneField(
        Slot,
        null=True,
        blank=True,
        related_name="content_ptr",
        verbose_name=_("Slot"),
        on_delete=models.CASCADE,
    )
    title = models.CharField(max_length=100, verbose_name=_("Title"))
    abstract = models.TextField(verbose_name=_("Abstract"))
    abstract_html = models.TextField(blank=True)
    speaker = models.ForeignKey(
        Speaker,
        related_name="presentations",
        verbose_name=_("Speaker"),
        on_delete=models.CASCADE,
    )
    additional_speakers = models.ManyToManyField(Speaker, related_name="copresentations",
                                                 blank=True, verbose_name=_("Additional speakers"))
    unpublish = models.BooleanField(
        default=False,
        verbose_name=_("Do not publish"),
    )
    cancelled = models.BooleanField(default=False, verbose_name=_("Cancelled"))
    proposal_base = models.OneToOneField(
        ProposalBase,
        related_name="presentation",
        verbose_name=_("Proposal base"),
        on_delete=models.CASCADE,
    )
    section = models.ForeignKey(
        Section,
        related_name="presentations",
        verbose_name=_("Section"),
        on_delete=models.CASCADE,
    )

    def save(self, *args, **kwargs):
        self.abstract_html = parse(self.abstract)
        return super(Presentation, self).save(*args, **kwargs)

    @property
    def number(self):
        return self.proposal.number

    @property
    def proposal(self):
        if self.proposal_base_id is None:
            return None
        return ProposalBase.objects.get_subclass(pk=self.proposal_base_id)

    def speakers(self):
        yield self.speaker
        for speaker in self.additional_speakers.all():
            if speaker.user:
                yield speaker

    def __str__(self):
        return "#%s %s (%s)" % (self.number, self.title, self.speaker)

    class Meta:
        ordering = ["slot"]
        verbose_name = _("presentation")
        verbose_name_plural = _("presentations")


class Session(models.Model):

    day = models.ForeignKey(
        Day,
        related_name="sessions",
        verbose_name=_("Day"),
        on_delete=models.CASCADE,
    )
    slots = models.ManyToManyField(Slot, related_name="sessions", verbose_name=_("Slots"))

    def sorted_slots(self):
        return self.slots.order_by("start")

    def start(self):
        slots = self.sorted_slots()
        if slots:
            return list(slots)[0].start
        else:
            return None

    def end(self):
        slots = self.sorted_slots()
        if slots:
            return list(slots)[-1].end
        else:
            return None

    def chair(self):
        for role in self.sessionrole_set.all():
            if role.role == SessionRole.SESSION_ROLE_CHAIR:
                return role
        return None

    @property
    def rooms(self):
        return Room.objects.filter(slotroom__slot__in=self.slots.all()).distinct()

    @property
    def room_names(self):
        return ', '.join(room.name for room in self.rooms)

    def __str__(self):
        start = self.start()
        end = self.end()
        rooms = ', '.join(room.name for room in self.rooms)
        if start and end:
            return "%s: %s - %s in %s" % (
                self.day.date.strftime("%a"),
                start.strftime("%X"),
                end.strftime("%X"),
                rooms
            )
        return ""

    class Meta:
        verbose_name = _("Session")
        verbose_name_plural = _("Sessions")


class SessionRole(models.Model):

    SESSION_ROLE_CHAIR = 1
    SESSION_ROLE_RUNNER = 2

    SESSION_ROLE_TYPES = [
        (SESSION_ROLE_CHAIR, _("Session Chair")),
        (SESSION_ROLE_RUNNER, _("Session Runner")),
    ]

    session = models.ForeignKey(
        Session,
        verbose_name=_("Session"),
        on_delete=models.CASCADE,
    )
    user = models.ForeignKey(
        User,
        verbose_name=_("User"),
        on_delete=models.CASCADE,
    )
    role = models.IntegerField(choices=SESSION_ROLE_TYPES, verbose_name=_("Role"))
    status = models.NullBooleanField(verbose_name=_("Status"))

    submitted = models.DateTimeField(default=datetime.datetime.now)

    class Meta:
        unique_together = [("session", "user", "role")]
        verbose_name = _("Session role")
        verbose_name_plural = _("Session roles")

    def __str__(self):
        return "%s %s: %s" % (self.user, self.session,
                              self.SESSION_ROLE_TYPES[self.role - 1][1])