Projet

Général

Profil

Télécharger (8,47 ko) Statistiques
| Branche: | Tag: | Révision:

calebasse / calebasse / agenda / managers.py @ 055d68a5

1

    
2
from datetime import datetime, timedelta, date, time
3
from interval import IntervalSet
4

    
5
from django.db.models import Q
6
from model_utils.managers import InheritanceManager, PassThroughManager, InheritanceQuerySet
7

    
8
from calebasse.agenda.conf import default
9
from calebasse.utils import weeks_since_epoch, weekday_ranks
10
from calebasse.utils import get_service_setting
11
from calebasse import agenda
12

    
13
__all__ = (
14
    'EventManager',
15
)
16

    
17

    
18
class EventQuerySet(InheritanceQuerySet):
19
    def for_today(self, today=None):
20
        today = today or date.today()
21
        excluded = self.filter(exceptions__exception_date=today).values_list('id', flat=True)
22
        weeks = weeks_since_epoch(today)
23
        filters = [Q(start_datetime__gte=datetime.combine(today, time()),
24
               start_datetime__lte=datetime.combine(today, time(23,59,59)),
25
               recurrence_periodicity__isnull=True,
26
               canceled=False) ]
27
        base_q = Q(start_datetime__lte=datetime.combine(today, time(23,59,59)),
28
                canceled=False,
29
                recurrence_week_day=today.weekday(),
30
                recurrence_periodicity__isnull=False) & \
31
                (Q(recurrence_end_date__gte=today) |
32
                    Q(recurrence_end_date__isnull=True)) & \
33
                ~ Q(id__in=excluded)
34
        # week periods
35
        for period in range(1, 6):
36
            filters.append(base_q & Q(recurrence_week_offset=weeks % period,
37
                recurrence_week_period=period))
38
        # week parity
39
        parity = today.isocalendar()[1] % 2
40
        filters.append(base_q & Q(recurrence_week_parity=parity))
41
        # week ranks
42
        filters.append(base_q & Q(recurrence_week_rank__in=weekday_ranks(today)))
43
        qs = self.filter(reduce(Q.__or__, filters))
44
        qs = qs.distinct()
45
        return qs
46

    
47
    def today_occurrences(self, today=None):
48
        today = today or date.today()
49
        self = self.for_today(today)
50
        occurences = ( e.today_occurrence(today) for e in self )
51
        return sorted(occurences, key=lambda e: e.start_datetime)
52

    
53
    def daily_disponibilities(self, date, events, participant, time_tables,
54
            holidays):
55
        '''Slice the day into quarters between 8:00 and 19:00, and returns the
56
           list of particpants with their status amon free, busy and away for each
57
           hour and quarters.
58

    
59
           date - the date of day we slice
60
           occurences - a dictionnary of iterable of events indexed by participants
61
           participants - an iterable of participants
62
           time_tables - a dictionnaty of timetable applying for this day indexed by participants
63
           holidays - a dictionnary of holidays applying for this day indexed by participants
64
        '''
65
        result = dict()
66
        quarter = 0
67

    
68
        events_intervals = IntervalSet((o.to_interval() for o in events if not o.is_event_absence()))
69

    
70
        timetables_intervals = IntervalSet((t.to_interval(date) for t in time_tables))
71
        holidays_intervals = IntervalSet((h.to_interval(date) for h in holidays))
72

    
73
        start_datetime = datetime(date.year, date.month, date.day, 8, 0)
74
        end_datetime = datetime(date.year, date.month, date.day, 8, 15)
75
        while (start_datetime.hour <= 19):
76

    
77
            if not result.has_key(start_datetime.hour):
78
                result[start_datetime.hour] = [[], [], [], []]
79
                quarter = 0
80
            interval = IntervalSet.between(start_datetime, end_datetime, False)
81
            mins = quarter * 15
82
            if get_service_setting('show_overlapping_appointments'):
83
                crossed_events = self.overlap_occurences(start_datetime, events)
84
            else:
85
                crossed_events = []
86
            if len(crossed_events) > 1:
87
                result[start_datetime.hour][quarter].append((mins, {'id': participant.id, 'dispo': 'overlap'}))
88
            elif interval.intersection(events_intervals):
89
                result[start_datetime.hour][quarter].append((mins, {'id': participant.id, 'dispo': 'busy'}))
90
            elif interval.intersection(holidays_intervals):
91
                result[start_datetime.hour][quarter].append((mins, {'id': participant.id, 'dispo': 'busy'}))
92
            elif not interval.intersection(timetables_intervals):
93
                result[start_datetime.hour][quarter].append((mins, {'id': participant.id, 'dispo': 'away'}))
94
            else:
95
                result[start_datetime.hour][quarter].append((mins, {'id': participant.id, 'dispo': 'free'}))
96
            quarter += 1
97
            start_datetime += timedelta(minutes=15)
98
            end_datetime += timedelta(minutes=15)
99
        return result
100

    
101
    def overlap_occurences(self, date_time=None, events=None):
102
        """
103
        returns the list of overlapping event occurences which do not begin and end
104
        at the same time and have the same act type
105
        """
106
        date_time = date_time or datetime.now()
107
        if events is None:
108
            events = self.today_occurrences(date_time.date())
109
        overlap = filter(lambda e: e.start_datetime <= date_time and e.end_datetime > date_time \
110
                         and not e.is_absent(), events)
111
        same_type_events = []
112
        different_overlap = []
113
        for event in overlap:
114
            if different_overlap:
115
                for e in different_overlap:
116
                    try:
117
                        if event.start_datetime == e.start_datetime and \
118
                           event.end_datetime == e.end_datetime and \
119
                           event.act_type == e.act_type:
120
                            continue
121
                        different_overlap.append(event)
122
                    except AttributeError:
123
                        continue
124
            else:
125
                different_overlap.append(event)
126
        return different_overlap
127

    
128

    
129
class EventManager(PassThroughManager.for_queryset_class(EventQuerySet),
130
        InheritanceManager):
131
    """ This class allows you to manage events, appointment, ...
132
    """
133

    
134
    def create_event(self, creator, title, event_type, participants=[], description='',
135
            services=[], start_datetime=None, end_datetime=None, ressource=None, note=None, periodicity=1, until=False, **kwargs):
136
        """
137
        Convenience function to create an ``Event``, optionally create an
138
        ``EventType``.
139

    
140
        Args:
141
            event_type: can be either an ``EventType`` object or the label
142
            is either created or retrieved.
143
            participants: List of CalebasseUser
144
            start_datetime: will default to the current hour if ``None``
145
            end_datetime: will default to ``start_datetime`` plus
146
            default.DEFAULT_EVENT_DURATION hour if ``None``
147
        Returns:
148
            Event object
149
        """
150
        if isinstance(event_type, str):
151
            event_type, created = agenda.models.EventType.objects.get_or_create(
152
                label=event_type
153
            )
154
        if not start_datetime:
155
            now = datetime.now()
156
            start_datetime = datetime.combine(now.date, time(now.hour))
157
        if not end_datetime:
158
            end_datetime = start_datetime + default.DEFAULT_EVENT_DURATION
159
        if until is False:
160
            until = start_datetime.date()
161
        event = self.create(creator=creator,
162
                title=title, start_datetime=start_datetime,
163
                end_datetime=end_datetime, event_type=event_type,
164
                ressource=ressource, recurrence_periodicity=periodicity,
165
                recurrence_end_date=until, **kwargs)
166
        event.services = services
167
        event.participants = participants
168
        return event
169

    
170
    def next_appointment(self, patient_record):
171
        qs = self.next_appointments(patient_record)
172
        if qs:
173
            return qs[0]
174
        else:
175
            return None
176

    
177
    def next_appointments(self, patient_record, today=None):
178
        from calebasse.actes.models import Act
179
        acts = Act.objects.next_acts(patient_record, today=today) \
180
                .filter(parent_event__isnull=False) \
181
                .select_related()
182
        return [ a.parent_event.today_occurrence(a.date) for a in acts ]
183

    
184
    def last_appointment(self, patient_record):
185
        qs = self.last_appointments(patient_record)
186
        if qs:
187
            return qs[0]
188
        else:
189
            return None
190

    
191
    def last_appointments(self, patient_record, today=None):
192
        from calebasse.actes.models import Act
193
        acts = Act.objects.last_acts(patient_record, today=today) \
194
                .filter(parent_event__isnull=False) \
195
                .select_related()
196
        return [ a.parent_event.today_occurrence(a.date) for a in acts ]
(6-6/10)