Project

General

Profile

Download (9.97 KB) Statistics
| Branch: | Tag: | Revision:

calebasse / calebasse / agenda / models.py @ 76974b6f

1
# -*- coding: utf-8 -*-
2

    
3
from datetime import datetime, date, timedelta
4
from copy import copy
5

    
6
from django.utils.translation import ugettext_lazy as _
7
from django.contrib.auth.models import User
8
from django.db import models
9

    
10
from ..middleware.request import get_request
11
from calebasse.agenda import managers
12
from calebasse.utils import weeks_since_epoch
13
from interval import Interval
14

    
15
__all__ = (
16
    'EventType',
17
    'Event',
18
    'EventWithAct',
19
)
20

    
21
class EventType(models.Model):
22
    '''
23
    Simple ``Event`` classifcation.
24
    '''
25
    class Meta:
26
        verbose_name = u'Type d\'événement'
27
        verbose_name_plural = u'Types d\'événement'
28

    
29
    def __unicode__(self):
30
        return self.label
31

    
32
    label = models.CharField(_('label'), max_length=50)
33
    rank = models.IntegerField(_('Sorting Rank'), null=True, blank=True, default=0)
34

    
35

    
36
class Event(models.Model):
37
    '''
38
    Container model for general agenda events
39
    '''
40
    objects = managers.EventManager()
41

    
42
    title = models.CharField(_('Title'), max_length=32, blank=True)
43
    description = models.TextField(_('Description'), max_length=100)
44
    event_type = models.ForeignKey(EventType, verbose_name=u"Type d'événement")
45
    creator = models.ForeignKey(User, verbose_name=_(u'Créateur'), blank=True, null=True)
46
    create_date = models.DateTimeField(_(u'Date de création'), auto_now_add=True)
47

    
48
    services = models.ManyToManyField('ressources.Service',
49
            null=True, blank=True, default=None)
50
    participants = models.ManyToManyField('personnes.People',
51
            null=True, blank=True, default=None)
52
    room = models.ForeignKey('ressources.Room', blank=True, null=True,
53
            verbose_name=u'Salle')
54

    
55
    start_datetime = models.DateTimeField(_('Début'), blank=True, null=True, db_index=True)
56
    end_datetime = models.DateTimeField(_('Fin'), blank=True, null=True)
57

    
58
    PERIODS = (
59
            (1, u'Toutes les semaines'),
60
            (2, u'Une semaine sur deux'),
61
            (3, u'Une semaine sur trois'),
62
            (4, 'Une semaine sur quatre'),
63
            (5, 'Une semaine sur cinq')
64
    )
65
    OFFSET = range(0,4)
66
    recurrence_week_day = models.PositiveIntegerField(default=0)
67
    recurrence_week_offset = models.PositiveIntegerField(
68
            choices=zip(OFFSET, OFFSET),
69
            verbose_name=u"Décalage en semaines par rapport au 1/1/1970 pour le calcul de période",
70
            default=0,
71
            db_index=True)
72
    recurrence_week_period = models.PositiveIntegerField(
73
            choices=PERIODS,
74
            verbose_name=u"Période en semaines",
75
            default=None,
76
            blank=True,
77
            null=True,
78
            db_index=True)
79
    recurrence_end_date = models.DateField(
80
            verbose_name=_(u'Fin de la récurrence'),
81
            blank=True, null=True,
82
            db_index=True)
83

    
84
    class Meta:
85
        verbose_name = u'Evénement'
86
        verbose_name_plural = u'Evénements'
87
        ordering = ('start_datetime', 'end_datetime', 'title')
88

    
89
    def __init__(self, *args, **kwargs):
90
        if kwargs.get('start_datetime') and not kwargs.has_key('recurrence_end_date'):
91
            kwargs['recurrence_end_date'] = kwargs.get('start_datetime').date()
92
        super(Event, self).__init__(*args, **kwargs)
93

    
94
    def clean(self):
95
        '''Initialize recurrence fields if they are not.'''
96
        if self.start_datetime:
97
            if self.recurrence_week_period:
98
                if self.recurrence_end_date and self.recurrence_end_date < self.start_datetime.date():
99
                    self.recurrence_end_date = self.start_datetime.date()
100
                self.recurrence_week_day = self.start_datetime.weekday()
101
                self.recurrence_week_offset = weeks_since_epoch(self.start_datetime) % self.recurrence_week_period
102

    
103
    def timedelta(self):
104
        '''Distance between start and end of the event'''
105
        return self.end_datetime - self.start_datetime
106

    
107
    def today_occurence(self, today=None):
108
        '''For a recurring event compute the today 'Event'.
109

    
110
           The computed event is the fake one that you cannot save to the database.
111
        '''
112
        today = today or date.today()
113
        if not self.is_recurring():
114
            if today == self.start_datetime.date():
115
                return self
116
            else:
117
                return None
118
        if today.weekday() != self.recurrence_week_day:
119
            return None
120
        if weeks_since_epoch(today) % self.recurrence_week_period != self.recurrence_week_offset:
121
            return None
122
        if not (self.start_datetime.date() <= today <= self.recurrence_end_date):
123
            return None
124
        start_datetime = datetime.combine(today, self.start_datetime.timetz())
125
        end_datetime = start_datetime + self.timedelta()
126
        event = copy(self)
127
        event.start_datetime = start_datetime
128
        event.end_datetime = end_datetime
129
        event.recurrence_end_date = None
130
        event.recurrence_week_offset = None
131
        event.recurrence_week_period = None
132
        event.parent = self
133
        def save(*args, **kwarks): 
134
            raise RuntimeError()
135
        event.save = save
136
        return event
137

    
138
    def next_occurence(self, today=None):
139
        '''Returns the next occurence after today.'''
140
        today = today or date.today()
141
        for occurence in self.all_occurences():
142
            if occurence.start_datetime.date() > today:
143
                return occurence
144

    
145
    def is_recurring(self):
146
        '''Is this event multiple ?'''
147
        return self.recurrence_week_period is not None
148

    
149
    def all_occurences(self, limit=90):
150
        '''Returns all occurences of this event as a list or pair (start_datetime,
151
           end_datetime).
152

    
153
           limit - compute occurrences until limit days in the future
154

    
155
           Default is to limit to 90 days.
156
        '''
157
        if self.recurrence_week_period:
158
            day = self.start_datetime.date()
159
            max_end_date = max(date.today(), self.start_datetime.date()) + timedelta(days=limit)
160
            end_date = min(self.recurrence_end_date or max_end_date, max_end_date)
161
            delta = timedelta(days=self.recurrence_week_period*7)
162
            while day <= end_date:
163
                yield self.today_occurence(day)
164
                day += delta
165
        else:
166
            yield self
167

    
168
    def to_interval(self):
169
        return Interval(self.start_datetime, self.end_datetime)
170

    
171
    def is_event_absence(self):
172
        return False
173

    
174
    def __unicode__(self):
175
        return self.title
176

    
177

    
178
class EventWithActManager(managers.EventManager):
179
    def create_patient_appointment(self, creator, title, patient,
180
            doctors=[], act_type=None, service=None, start_datetime=None, end_datetime=None,
181
            room=None, periodicity=1, until=False):
182
        appointment = self.create_event(creator=creator,
183
                title=title,
184
                event_type=EventType(id=1),
185
                participants=doctors,
186
                services=[service],
187
                start_datetime=start_datetime,
188
                end_datetime=end_datetime,
189
                room=room,
190
                periodicity=periodicity,
191
                until=until,
192
                act_type=act_type,
193
                patient=patient)
194
        return appointment
195

    
196

    
197
class EventWithAct(Event):
198
    '''An event corresponding to an act.'''
199
    objects = EventWithActManager()
200
    act_type = models.ForeignKey('ressources.ActType',
201
        verbose_name=u'Type d\'acte')
202
    patient = models.ForeignKey('dossiers.PatientRecord')
203

    
204
    @property
205
    def act(self):
206
        return self.get_or_create_act()
207

    
208
    def get_or_create_act(self, today=None):
209
        from ..actes.models import Act, ActValidationState
210
        today = today or self.start_datetime.date()
211
        act, created = Act.objects.get_or_create(patient=self.patient,
212
                parent_event=getattr(self, 'parent', self),
213
                date=today,
214
                act_type=self.act_type)
215
        self.update_act(act)
216
        if created:
217
            ActValidationState.objects.create(act=act, state_name='NON_VALIDE',
218
                author=self.creator, previous_state=None)
219
        return act
220

    
221
    def update_act(self, act):
222
        '''Update an act to match details of the meeting'''
223
        delta = self.timedelta()
224
        duration = delta.seconds // 60
225
        act._duration = duration
226
        act.doctors = self.participants.select_subclasses()
227
        act.act_type = self.act_type
228
        act.patient = self.patient
229
        act.date = self.start_datetime.date()
230
        act.save()
231

    
232
    def save(self, *args, **kwargs):
233
        '''Force event_type to be patient meeting.'''
234
        self.clean()
235
        self.event_type = EventType(id=1)
236
        super(EventWithAct, self).save(*args, **kwargs)
237
        # list of occurences may have changed
238
        from ..actes.models import Act
239
        occurences = list(self.all_occurences())
240
        acts = Act.objects.filter(parent_event=self)
241
        occurences_by_date = dict((o.start_datetime.date(), o) for o in occurences)
242
        acts_by_date = dict()
243
        for a in acts:
244
            # sanity check
245
            assert a.date not in acts_by_date
246
            acts_by_date[a.date] = a
247
        for a in acts:
248
            o = occurences_by_date.get(a.date)
249
            if o:
250
                o.update_act(a)
251
            else:
252
                if len(a.actvalidationstate_set.all()) > 1:
253
                    a.parent_event = None
254
                    a.save()
255
                else:
256
                    a.delete()
257
        participants = self.participants.select_subclasses()
258
        for o in occurences:
259
            if o.start_datetime.date() in acts_by_date:
260
                continue
261
            o.get_or_create_act()
262

    
263
    def is_event_absence(self):
264
        return self.act.is_absent()
265

    
266
    def __unicode__(self):
267
        kwargs = {
268
                'patient': self.patient,
269
                'start_datetime': self.start_datetime,
270
                'act_type': self.act_type
271
        }
272
        kwargs['doctors'] = ', '.join(map(unicode, self.participants.all())) if self.id else ''
273
        return u'Rdv le {start_datetime} de {patient} avec {doctors} pour ' \
274
            '{act_type} ({act_type.id})'.format(**kwargs)
(7-7/10)