1
|
# -*- coding: utf-8 -*-
|
2
|
|
3
|
from datetime import datetime
|
4
|
|
5
|
from dateutil import rrule
|
6
|
|
7
|
from django.utils.translation import ugettext_lazy as _
|
8
|
from django.contrib.contenttypes.models import ContentType
|
9
|
from django.contrib.contenttypes import generic
|
10
|
from django.db import models
|
11
|
|
12
|
from calebasse.agenda import managers
|
13
|
from interval import Interval
|
14
|
|
15
|
__all__ = (
|
16
|
'Note',
|
17
|
'EventType',
|
18
|
'Event',
|
19
|
'Occurrence',
|
20
|
)
|
21
|
|
22
|
class Note(models.Model):
|
23
|
'''
|
24
|
A generic model for adding simple, arbitrary notes to other models such as
|
25
|
``Event`` or ``Occurrence``.
|
26
|
'''
|
27
|
|
28
|
class Meta:
|
29
|
verbose_name = u'Note'
|
30
|
verbose_name_plural = u'Notes'
|
31
|
|
32
|
def __unicode__(self):
|
33
|
return self.note
|
34
|
|
35
|
note = models.TextField(_('note'))
|
36
|
created = models.DateTimeField(_('created'), auto_now_add=True)
|
37
|
content_type = models.ForeignKey(ContentType)
|
38
|
|
39
|
|
40
|
class EventType(models.Model):
|
41
|
'''
|
42
|
Simple ``Event`` classifcation.
|
43
|
'''
|
44
|
class Meta:
|
45
|
verbose_name = u'Type d\'événement'
|
46
|
verbose_name_plural = u'Types d\'événement'
|
47
|
|
48
|
def __unicode__(self):
|
49
|
return self.label
|
50
|
|
51
|
label = models.CharField(_('label'), max_length=50)
|
52
|
|
53
|
|
54
|
class Event(models.Model):
|
55
|
'''
|
56
|
Container model for general metadata and associated ``Occurrence`` entries.
|
57
|
'''
|
58
|
objects = managers.EventManager()
|
59
|
|
60
|
title = models.CharField(_('Titre'), max_length=32)
|
61
|
description = models.TextField(_('Description'), max_length=100)
|
62
|
event_type = models.ForeignKey(EventType, verbose_name=u"Type d'événement")
|
63
|
notes = generic.GenericRelation(Note, verbose_name=_('notes'))
|
64
|
|
65
|
services = models.ManyToManyField('ressources.Service',
|
66
|
null=True, blank=True, default=None)
|
67
|
participants = models.ManyToManyField('personnes.People',
|
68
|
null=True, blank=True, default=None)
|
69
|
room = models.ForeignKey('ressources.Room', blank=True, null=True,
|
70
|
verbose_name=u'Salle')
|
71
|
|
72
|
class Meta:
|
73
|
verbose_name = u'Evénement'
|
74
|
verbose_name_plural = u'Evénements'
|
75
|
ordering = ('title', )
|
76
|
|
77
|
|
78
|
def __unicode__(self):
|
79
|
return self.title
|
80
|
|
81
|
def add_occurrences(self, start_time, end_time, room=None, **rrule_params):
|
82
|
'''
|
83
|
Add one or more occurences to the event using a comparable API to
|
84
|
``dateutil.rrule``.
|
85
|
|
86
|
If ``rrule_params`` does not contain a ``freq``, one will be defaulted
|
87
|
to ``rrule.DAILY``.
|
88
|
|
89
|
Because ``rrule.rrule`` returns an iterator that can essentially be
|
90
|
unbounded, we need to slightly alter the expected behavior here in order
|
91
|
to enforce a finite number of occurrence creation.
|
92
|
|
93
|
If both ``count`` and ``until`` entries are missing from ``rrule_params``,
|
94
|
only a single ``Occurrence`` instance will be created using the exact
|
95
|
``start_time`` and ``end_time`` values.
|
96
|
'''
|
97
|
rrule_params.setdefault('freq', rrule.DAILY)
|
98
|
|
99
|
if 'count' not in rrule_params and 'until' not in rrule_params:
|
100
|
self.occurrence_set.create(start_time=start_time, end_time=end_time)
|
101
|
else:
|
102
|
delta = end_time - start_time
|
103
|
for ev in rrule.rrule(dtstart=start_time, **rrule_params):
|
104
|
self.occurrence_set.create(start_time=ev, end_time=ev + delta)
|
105
|
|
106
|
def upcoming_occurrences(self):
|
107
|
'''
|
108
|
Return all occurrences that are set to start on or after the current
|
109
|
time.
|
110
|
'''
|
111
|
return self.occurrence_set.filter(start_time__gte=datetime.now())
|
112
|
|
113
|
def next_occurrence(self):
|
114
|
'''
|
115
|
Return the single occurrence set to start on or after the current time
|
116
|
if available, otherwise ``None``.
|
117
|
'''
|
118
|
upcoming = self.upcoming_occurrences()
|
119
|
return upcoming and upcoming[0] or None
|
120
|
|
121
|
def daily_occurrences(self, dt=None):
|
122
|
'''
|
123
|
Convenience method wrapping ``Occurrence.objects.daily_occurrences``.
|
124
|
'''
|
125
|
return Occurrence.objects.daily_occurrences(dt=dt, event=self)
|
126
|
|
127
|
|
128
|
class Occurrence(models.Model):
|
129
|
'''
|
130
|
Represents the start end time for a specific occurrence of a master ``Event``
|
131
|
object.
|
132
|
'''
|
133
|
start_time = models.DateTimeField()
|
134
|
end_time = models.DateTimeField()
|
135
|
event = models.ForeignKey('Event', verbose_name=_('event'), editable=False)
|
136
|
notes = models.ManyToManyField('Note', verbose_name=_('notes'),
|
137
|
null=True, blank=True, default=None)
|
138
|
|
139
|
objects = managers.OccurrenceManager()
|
140
|
|
141
|
class Meta:
|
142
|
verbose_name = u'Occurrence'
|
143
|
verbose_name_plural = u'Occurrences'
|
144
|
ordering = ('start_time', 'end_time')
|
145
|
|
146
|
def __unicode__(self):
|
147
|
return u'%s: %s' % (self.title, self.start_time.isoformat())
|
148
|
|
149
|
def __cmp__(self, other):
|
150
|
return cmp(self.start_time, other.start_time)
|
151
|
|
152
|
@property
|
153
|
def title(self):
|
154
|
return self.event.title
|
155
|
|
156
|
@property
|
157
|
def event_type(self):
|
158
|
return self.event.event_type
|
159
|
|
160
|
def to_interval(self):
|
161
|
return Interval(self.start_time, self.end_time)
|
162
|
|