Revision 76974b6f
Added by Benjamin Dauvergne almost 12 years ago
calebasse/agenda/managers.py | ||
---|---|---|
1 | 1 |
|
2 |
from datetime import datetime, timedelta, date |
|
2 |
from datetime import datetime, timedelta, date, time
|
|
3 | 3 |
from interval import IntervalSet |
4 | 4 |
|
5 |
from django.db import models
|
|
6 |
from model_utils.managers import InheritanceManager |
|
5 |
from django.db.models import Q
|
|
6 |
from model_utils.managers import InheritanceManager, PassThroughManager, InheritanceQuerySet
|
|
7 | 7 |
|
8 | 8 |
from calebasse.agenda.conf import default |
9 |
from calebasse.utils import weeks_since_epoch |
|
9 | 10 |
from calebasse import agenda |
10 | 11 |
|
11 | 12 |
__all__ = ( |
12 | 13 |
'EventManager', |
13 |
'OccurrenceManager', |
|
14 | 14 |
) |
15 | 15 |
|
16 |
class EventManager(InheritanceManager): |
|
17 |
""" This class allows you to manage events, appointment, ... |
|
18 |
""" |
|
19 |
|
|
20 |
def _set_event(self, event, participants=[], description='', services=[], |
|
21 |
start_datetime=None, end_datetime=None, note=None, room=None, **rrule_params): |
|
22 |
""" Private method to configure an Event or an EventAct |
|
23 |
""" |
|
24 |
event.description = description |
|
25 |
event.participants = participants |
|
26 |
event.services = services |
|
27 |
event.room = room |
|
28 |
if note is not None: |
|
29 |
event.notes.create(note=note) |
|
30 |
start_datetime = start_datetime or datetime.now().replace( |
|
31 |
minute=0, second=0, microsecond=0 |
|
32 |
) |
|
33 |
occurence_duration = default.DEFAULT_OCCURRENCE_DURATION |
|
34 |
end_datetime = end_datetime or start_datetime + occurence_duration |
|
35 |
event.add_occurrences(start_datetime, end_datetime, **rrule_params) |
|
36 |
event.save() |
|
37 |
|
|
38 |
return event |
|
39 | 16 |
|
40 |
|
|
41 |
def create_event(self, title, event_type, participants=[], description='', |
|
42 |
services=[], start_datetime=None, end_datetime=None, room=None, note=None, |
|
43 |
**rrule_params): |
|
44 |
""" |
|
45 |
Convenience function to create an ``Event``, optionally create an |
|
46 |
``EventType``, and associated ``Occurrence``s. ``Occurrence`` creation |
|
47 |
rules match those for ``Event.add_occurrences``. |
|
48 |
|
|
49 |
Args: |
|
50 |
event_type: can be either an ``EventType`` object or the label |
|
51 |
is either created or retrieved. |
|
52 |
participants: List of CalebasseUser |
|
53 |
start_datetime: will default to the current hour if ``None`` |
|
54 |
end_datetime: will default to ``start_datetime`` plus |
|
55 |
default.DEFAULT_OCCURRENCE_DURATION hour if ``None`` |
|
56 |
freq, count, rrule_params: |
|
57 |
follow the ``dateutils`` API (see http://labix.org/python-dateutil) |
|
58 |
Returns: |
|
59 |
Event object |
|
60 |
""" |
|
61 |
|
|
62 |
if isinstance(event_type, str): |
|
63 |
event_type, created = agenda.models.EventType.objects.get_or_create( |
|
64 |
label=event_type |
|
65 |
) |
|
66 |
event = self.create(title=title, event_type=event_type) |
|
67 |
|
|
68 |
return self._set_event(event, participants, services = services, |
|
69 |
start_datetime = start_datetime, end_datetime = end_datetime, |
|
70 |
room=room, **rrule_params) |
|
71 |
|
|
72 |
def create_holiday(self, start_date, end_date, peoples=[], services=[], motive=''): |
|
73 |
event_type, created = agenda.models.EventType.objects.get_or_create( |
|
74 |
label=u"Vacances" |
|
75 |
) |
|
76 |
event = self.create(title="Conge", event_type=event_type) |
|
77 |
start_datetime = datetime(start_date.year, start_date.month, start_date.day) |
|
78 |
end_datetime = datetime(end_date.year, end_date.month, end_date.day, 23, 59) |
|
79 |
return self._set_event(event, peoples, motive, services, start_datetime, end_datetime) |
|
80 |
|
|
81 |
class OccurrenceManager(models.Manager): |
|
82 |
|
|
83 |
def daily_occurrences(self, date=None, participants=None, services=None, |
|
84 |
event_type=None): |
|
85 |
''' |
|
86 |
Returns a queryset of for instances that have any overlap with a |
|
87 |
particular day. |
|
88 |
|
|
89 |
Args: |
|
90 |
date: may be either a datetime.datetime, datetime.date object, or |
|
91 |
``None``. If ``None``, default to the current day. |
|
92 |
participants: a list of CalebasseUser |
|
93 |
services: a list of services |
|
94 |
event_type: a single, or a list of, event types |
|
95 |
''' |
|
96 |
date = date or datetime.now() |
|
97 |
start = datetime(date.year, date.month, date.day) |
|
98 |
end = start.replace(hour=23, minute=59, second=59) |
|
99 |
qs = self.select_related('event').filter( |
|
100 |
models.Q( |
|
101 |
start_time__gte=start, |
|
102 |
start_time__lte=end, |
|
103 |
) | |
|
104 |
models.Q( |
|
105 |
end_time__gte=start, |
|
106 |
end_time__lte=end, |
|
107 |
) | |
|
108 |
models.Q( |
|
109 |
start_time__lt=start, |
|
110 |
end_time__gt=end, |
|
111 |
) |
|
112 |
) |
|
113 |
|
|
114 |
if participants: |
|
115 |
qs = qs.filter(event__participants__in=participants) |
|
116 |
if services: |
|
117 |
qs = qs.filter(event__services__in=services) |
|
118 |
if event_type: |
|
119 |
if type(event_type) is list: |
|
120 |
qs = qs.filter(event__event_type__in=event_type) |
|
121 |
else: |
|
122 |
qs = qs.filter(event__event_type=event_type) |
|
17 |
class EventQuerySet(InheritanceQuerySet): |
|
18 |
def for_today(self, today=None): |
|
19 |
today = today or date.today() |
|
20 |
weeks = weeks_since_epoch(today) |
|
21 |
filters = [Q(start_datetime__gte=datetime.combine(today, time()), |
|
22 |
start_datetime__lte=datetime.combine(today, time(23,59,59))) ] |
|
23 |
base_q = Q(start_datetime__lte=datetime.combine(today, time(23,59,59))) & \ |
|
24 |
(Q(recurrence_end_date__gte=today) | |
|
25 |
Q(recurrence_end_date__isnull=True)) |
|
26 |
for period in range(1, 6): |
|
27 |
filters.append(base_q & Q(recurrence_week_offset=weeks % period, |
|
28 |
recurrence_week_period=period, recurrence_week_day=today.weekday())) |
|
29 |
qs = self.filter(reduce(Q.__or__, filters)) |
|
123 | 30 |
return qs |
124 | 31 |
|
125 |
def daily_disponibility(self, date, occurrences, participants, time_tables, holidays): |
|
32 |
def today_occurences(self, today=None): |
|
33 |
today = today or date.today() |
|
34 |
self = self.for_today(today) |
|
35 |
occurences = ( e.today_occurence(today) for e in self ) |
|
36 |
return sorted(occurences, key=lambda e: e.start_datetime) |
|
37 |
|
|
38 |
def daily_disponibilities(self, date, events, participants, time_tables, |
|
39 |
holidays): |
|
40 |
'''Slice the day into quarters between 8:00 and 19:00, and returns the |
|
41 |
list of particpants with their status amon free, busy and away for each |
|
42 |
hour and quarters. |
|
43 |
|
|
44 |
date - the date of day we slice |
|
45 |
occurences - a dictionnary of iterable of events indexed by participants |
|
46 |
participants - an iterable of participants |
|
47 |
time_tables - a dictionnaty of timetable applying for this day indexed by participants |
|
48 |
holidays - a dictionnary of holidays applying for this day indexed by participants |
|
49 |
''' |
|
126 | 50 |
result = dict() |
127 | 51 |
quarter = 0 |
128 |
occurrences_set = {}
|
|
52 |
events_set = {}
|
|
129 | 53 |
timetables_set = {} |
130 | 54 |
holidays_set = {} |
131 | 55 |
for participant in participants: |
132 |
occurrences_set[participant.id] = IntervalSet((o.to_interval() for o in occurrences[participant.id] if not o.is_event_absence()))
|
|
56 |
events_set[participant.id] = IntervalSet((o.to_interval() for o in events[participant.id] if not o.is_event_absence()))
|
|
133 | 57 |
timetables_set[participant.id] = IntervalSet((t.to_interval(date) for t in time_tables[participant.id])) |
134 | 58 |
holidays_set[participant.id] = IntervalSet((h.to_interval(date) for h in holidays[participant.id])) |
135 | 59 |
start_datetime = datetime(date.year, date.month, date.day, 8, 0) |
... | ... | |
137 | 61 |
while (start_datetime.hour <= 19): |
138 | 62 |
for participant in participants: |
139 | 63 |
if not result.has_key(start_datetime.hour): |
140 |
result[start_datetime.hour] = [0, 1, 2, 3] |
|
141 |
result[start_datetime.hour][0] = [] |
|
142 |
result[start_datetime.hour][1] = [] |
|
143 |
result[start_datetime.hour][2] = [] |
|
144 |
result[start_datetime.hour][3] = [] |
|
64 |
result[start_datetime.hour] = [[], [], [], []] |
|
145 | 65 |
quarter = 0 |
146 |
|
|
147 | 66 |
interval = IntervalSet.between(start_datetime, end_datetime, False) |
148 |
if interval.intersection(occurrences_set[participant.id]):
|
|
67 |
if interval.intersection(events_set[participant.id]):
|
|
149 | 68 |
result[start_datetime.hour][quarter].append({'id': participant.id, 'dispo': 'busy'}) |
150 | 69 |
elif interval.intersection(holidays_set[participant.id]): |
151 | 70 |
result[start_datetime.hour][quarter].append({'id': participant.id, 'dispo': 'busy'}) |
... | ... | |
158 | 77 |
end_datetime += timedelta(minutes=15) |
159 | 78 |
return result |
160 | 79 |
|
161 |
def next_appoinment(self, patient_record): |
|
162 |
qs = self.next_appoinments(patient_record) |
|
80 |
|
|
81 |
class EventManager(PassThroughManager.for_queryset_class(EventQuerySet), |
|
82 |
InheritanceManager): |
|
83 |
""" This class allows you to manage events, appointment, ... |
|
84 |
""" |
|
85 |
|
|
86 |
def create_event(self, creator, title, event_type, participants=[], description='', |
|
87 |
services=[], start_datetime=None, end_datetime=None, room=None, note=None, periodicity=1, until=False, **kwargs): |
|
88 |
""" |
|
89 |
Convenience function to create an ``Event``, optionally create an |
|
90 |
``EventType``. |
|
91 |
|
|
92 |
Args: |
|
93 |
event_type: can be either an ``EventType`` object or the label |
|
94 |
is either created or retrieved. |
|
95 |
participants: List of CalebasseUser |
|
96 |
start_datetime: will default to the current hour if ``None`` |
|
97 |
end_datetime: will default to ``start_datetime`` plus |
|
98 |
default.DEFAULT_EVENT_DURATION hour if ``None`` |
|
99 |
Returns: |
|
100 |
Event object |
|
101 |
""" |
|
102 |
if isinstance(event_type, str): |
|
103 |
event_type, created = agenda.models.EventType.objects.get_or_create( |
|
104 |
label=event_type |
|
105 |
) |
|
106 |
if not start_datetime: |
|
107 |
now = datetime.now() |
|
108 |
start_datetime = datetime.combine(now.date, time(now.hour)) |
|
109 |
if not end_datetime: |
|
110 |
end_datetime = start_datetime + default.DEFAULT_EVENT_DURATION |
|
111 |
if until is False: |
|
112 |
until = start_datetime.date() |
|
113 |
event = self.create(creator=creator, |
|
114 |
title=title, start_datetime=start_datetime, |
|
115 |
end_datetime=end_datetime, event_type=event_type, |
|
116 |
room=room, recurrence_week_period=periodicity, |
|
117 |
recurrence_end_date=until, |
|
118 |
recurrence_week_day=start_datetime.weekday(), **kwargs) |
|
119 |
event.services = services |
|
120 |
event.participants = participants |
|
121 |
return event |
|
122 |
|
|
123 |
def next_appointment(self, patient_record): |
|
124 |
qs = self.next_appointments(patient_record) |
|
163 | 125 |
if qs: |
164 | 126 |
return qs[0] |
165 | 127 |
else: |
166 | 128 |
return None |
167 | 129 |
|
168 |
def next_appoinments(self, patient_record): |
|
169 |
today = date.today() |
|
170 |
return self.filter(start_time__gt=datetime(today.year, today.month, today.day)).\ |
|
171 |
filter(event__event_type__id=1).\ |
|
172 |
filter(event__eventact__patient=patient_record).\ |
|
173 |
prefetch_related('event__eventact').\ |
|
174 |
order_by('start_time') |
|
130 |
def next_appointments(self, patient_record, today=None): |
|
131 |
from calebasse.actes.models import Act |
|
132 |
acts = Act.objects.next_acts(patient_record, today=today) \ |
|
133 |
.filter(parent_event__isnull=False) \ |
|
134 |
.select_related() |
|
135 |
return [ a.parent_event.today_occurence(a.date) for a in acts ] |
|
175 | 136 |
|
176 |
def last_appoinment(self, patient_record): |
|
177 |
qs = self.last_appoinments(patient_record) |
|
137 |
def last_appointment(self, patient_record):
|
|
138 |
qs = self.last_appointments(patient_record)
|
|
178 | 139 |
if qs: |
179 | 140 |
return qs[0] |
180 | 141 |
else: |
181 | 142 |
return None |
182 | 143 |
|
183 |
def last_appoinments(self, patient_record): |
|
184 |
return self.filter(start_time__lt=datetime.now()).\ |
|
185 |
filter(event__event_type__id=1).\ |
|
186 |
filter(event__eventact__patient=patient_record).\ |
|
187 |
prefetch_related('event__eventact').\ |
|
188 |
order_by('-start_time') |
|
144 |
def last_appointments(self, patient_record, today=None): |
|
145 |
from calebasse.actes.models import Act |
|
146 |
acts = Act.objects.last_acts(patient_record, today=today) \ |
|
147 |
.filter(parent_event__isnull=False) \ |
|
148 |
.select_related() |
|
149 |
return [ a.parent_event.today_occurence(a.date) for a in acts ] |
Also available in: Unified diff
agenda/actes/dossiers: move Occurence fields into Event, add recurring events support