Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ 797b7721

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

    
3
from datetime import datetime, date, time as datetime_time
4

    
5
from django.db import models
6
from django.db.models import query
7
from django.contrib.auth.models import User
8
from django.template.defaultfilters import date as date_filter
9
from django import forms
10

    
11
import reversion
12
from model_utils.managers import InheritanceManager
13

    
14
from calebasse.models import PhoneNumberField
15
from calebasse.ressources.models import Service, NamedAbstractModel
16
from calebasse.models import (BaseModelMixin, WeekRankField,
17
    PhoneNumberField, ZipCodeField)
18
from calebasse.utils import weeks_since_epoch, weekday_ranks
19

    
20
from interval import Interval
21

    
22
from model_utils import Choices
23
from model_utils.managers import PassThroughManager
24

    
25
class Role(NamedAbstractModel):
26
    users = models.ManyToManyField(User,
27
                verbose_name=u'Utilisateurs', blank=True)
28

    
29
class People(BaseModelMixin, models.Model):
30
    GENDERS =  Choices(
31
            (1, 'Masculin'),
32
            (2, 'Féminin'),
33
    )
34

    
35
    objects = InheritanceManager()
36
    last_name = models.CharField(max_length=128, verbose_name=u'Nom')
37
    first_name = models.CharField(max_length=128, verbose_name=u'Prénom(s)')
38
    display_name = models.CharField(max_length=256,
39
            verbose_name=u'Nom complet', editable=False)
40
    gender = models.IntegerField(verbose_name=u"Genre", choices=GENDERS,
41
            max_length=1, blank=True, null=True)
42
    email = models.EmailField(blank=True, null=True)
43
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
44

    
45
    def save(self, **kwargs):
46
        self.display_name = self.first_name + ' ' + self.last_name.upper()
47
        super(People, self).save(**kwargs)
48

    
49
    def __unicode__(self):
50
        return self.display_name
51

    
52
    def get_initials(self):
53
        initials = []
54
        if self.first_name:
55
            initials.append(self.first_name[0].upper())
56
        initials.append(self.last_name[0].upper())
57
        return ''.join(initials)
58

    
59
    class Meta:
60
        ordering = ['last_name', 'first_name']
61

    
62
class WorkerQuerySet(query.QuerySet):
63
    def for_service(self, service, type=None):
64
        if type:
65
            return self.filter(enabled=True, services__in=[service], type=type)
66
        else:
67
            return self.filter(enabled=True, services__in=[service])
68

    
69

    
70
class Worker(People):
71
    objects = PassThroughManager.for_queryset_class(WorkerQuerySet)()
72
    type = models.ForeignKey('ressources.WorkerType',
73
            verbose_name=u'Type de personnel')
74
    services = models.ManyToManyField('ressources.Service', blank=True, null=True)
75
    enabled = models.BooleanField(verbose_name=u'Actif',
76
                default=True)
77
    old_camsp_id = models.CharField(max_length=256,
78
            verbose_name=u'Ancien ID au CAMSP', blank=True, null=True)
79
    old_cmpp_id = models.CharField(max_length=256,
80
            verbose_name=u'Ancien ID au CMPP', blank=True, null=True)
81
    old_sessad_dys_id = models.CharField(max_length=256,
82
            verbose_name=u'Ancien ID au SESSAD TED', blank=True, null=True)
83
    old_sessad_ted_id = models.CharField(max_length=256,
84
            verbose_name=u'Ancien ID au SESSAD DYS', blank=True, null=True)
85

    
86
    def is_active(self):
87
        return self.enabled
88

    
89
    def is_away(self):
90
        if self.timetable_set.filter(weekday=date.today().weekday()).exists():
91
            return False
92
        return True
93

    
94
    @models.permalink
95
    def get_absolute_url(self):
96
        return ('worker_update', (), {
97
            'service': self.services.all()[0].name.lower(),
98
            'pk': self.pk })
99

    
100
    class Meta:
101
        verbose_name = u'Personnel'
102
        verbose_name_plural = u'Personnels'
103

    
104

    
105
reversion.register(Worker, follow=['people_ptr'])
106
reversion.register(User)
107

    
108
class ExternalWorker(People):
109
    description = models.TextField(blank=True, null=True, default=None)
110
    address = models.CharField(max_length=120,
111
            verbose_name=u"Adresse", blank=True, null=True, default=None)
112
    address_complement = models.CharField(max_length=120,
113
            blank=True,
114
            null=True,
115
            default=None,
116
            verbose_name=u"Complément d'adresse")
117
    zip_code = ZipCodeField(verbose_name=u"Code postal",
118
        blank=True, null=True, default=None)
119
    city = models.CharField(max_length=80, verbose_name=u"Ville",
120
        blank=True, null=True, default=None)
121
    phone_work = PhoneNumberField(verbose_name=u"Téléphone du travail",
122
        blank=True, null=True, default=None)
123
    fax = models.CharField(max_length=30,
124
            blank=True, null=True, default=None)
125
    type = models.ForeignKey('ressources.WorkerType',
126
            verbose_name=u'Spécialité')
127
    old_id = models.CharField(max_length=256,
128
            verbose_name=u'Ancien ID', blank=True, null=True)
129
    old_service = models.CharField(max_length=256,
130
            verbose_name=u'Ancien Service', blank=True, null=True)
131
    class Meta:
132
        verbose_name = u'Intervenant extérieur'
133
        verbose_name_plural = u'Intervenants extérieurs'
134

    
135
reversion.register(ExternalWorker, follow=['people_ptr'])
136

    
137
class ExternalTherapist(People):
138
    description = models.TextField(blank=True, null=True, default=None)
139
    address = models.CharField(max_length=120,
140
            verbose_name=u"Adresse", blank=True, null=True, default=None)
141
    address_complement = models.CharField(max_length=120,
142
            blank=True,
143
            null=True,
144
            default=None,
145
            verbose_name=u"Complément d'adresse")
146
    zip_code = ZipCodeField(verbose_name=u"Code postal",
147
        blank=True, null=True, default=None)
148
    city = models.CharField(max_length=80, verbose_name=u"Ville",
149
        blank=True, null=True, default=None)
150
    phone_work = PhoneNumberField(verbose_name=u"Téléphone du travail",
151
        blank=True, null=True, default=None)
152
    fax = models.CharField(max_length=30,
153
            blank=True, null=True, default=None)
154
    type = models.ForeignKey('ressources.WorkerType',
155
            verbose_name=u'Spécialité')
156
    old_id = models.CharField(max_length=256,
157
            verbose_name=u'Ancien ID', blank=True, null=True)
158
    old_service = models.CharField(max_length=256,
159
            verbose_name=u'Ancien Service', blank=True, null=True)
160
    old_id = models.CharField(max_length=256,
161
            verbose_name=u'Ancien ID', blank=True, null=True)
162
    old_service = models.CharField(max_length=256,
163
            verbose_name=u'Ancien Service', blank=True, null=True)
164
    class Meta:
165
        verbose_name = u'Médecin extérieur'
166
        verbose_name_plural = u'Médecins extérieurs'
167

    
168
reversion.register(ExternalTherapist, follow=['people_ptr'])
169

    
170
class UserWorker(BaseModelMixin, models.Model):
171
    user = models.OneToOneField('auth.User')
172
    worker = models.ForeignKey('Worker',
173
            verbose_name=u'Personnel')
174

    
175
    def __unicode__(self):
176
        return u'Lien entre la personne {0} et l\'utilisateur {1}'.format(
177
                self.worker, self.user)
178

    
179
reversion.register(UserWorker)
180

    
181
class SchoolTeacher(People):
182
    schools = models.ManyToManyField('ressources.School')
183
    role = models.ForeignKey('ressources.SchoolTeacherRole')
184

    
185
reversion.register(SchoolTeacher, follow=['user_ptr'])
186

    
187
class TimeTableQuerySet(query.QuerySet):
188
    def current(self, today=None):
189
        if today is None:
190
            today = date.today()
191
        return self.filter(start_date__lte=today, end_date__gte=today)
192

    
193
    def for_today(self, today=None):
194
        if today is None:
195
            today = date.today()
196
        qs = self.current(today)
197
        qs = self.filter(weekday=today.weekday())
198
        filters = []
199
        # week periods
200
        for week_period in range(1,5):
201
            filters.append(models.Q(week_period=week_period,
202
                week_offset=weeks_since_epoch(today) % week_period))
203
        # week parity
204
        parity = today.isocalendar()[1] % 2
205
        filters.append(models.Q(week_parity=parity))
206
        # week ranks
207
        filters.append(models.Q(week_rank__in=weekday_ranks(today)))
208
        qs = qs.filter(reduce(models.Q.__or__, filters))
209
        return qs
210

    
211
PERIODICITIES = (
212
        (1, u'Toutes les semaines'),
213
        (2, u'Une semaine sur deux'),
214
        (3, u'Une semaine sur trois'),
215
        (4, u'Une semaine sur quatre'),
216
        (5, u'Une semaine sur cinq'),
217
        (6, u'La première semaine du mois'),
218
        (7, u'La deuxième semaine du mois'),
219
        (8, u'La troisième semaine du mois'),
220
        (9, u'La quatrième semaine du mois'),
221
        (10, u'La dernière semaine du mois'),
222
        (11, u'Les semaines paires'),
223
        (12, u'Les semaines impaires')
224
)
225

    
226

    
227
class TimeTable(BaseModelMixin, models.Model):
228
    objects = PassThroughManager.for_queryset_class(TimeTableQuerySet)()
229
    worker = models.ForeignKey(Worker,
230
            verbose_name=u'Intervenant')
231
    services = models.ManyToManyField('ressources.Service')
232
    WEEKDAYS = Choices(*enumerate(('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
233
        'samedi', 'dimanche')))
234

    
235
    weekday = models.PositiveIntegerField(
236
        verbose_name=u"Jour de la semaine",
237
        choices=WEEKDAYS)
238
    start_time = models.TimeField(
239
        verbose_name=u'Heure de début')
240
    end_time = models.TimeField(
241
        verbose_name=u'Heure de fin')
242
    start_date = models.DateField(
243
        verbose_name=u'Début',
244
        help_text=u'format: jj/mm/aaaa')
245
    end_date = models.DateField(
246
        verbose_name=u'Fin', blank=True, null=True,
247
        help_text=u'format: jj/mm/aaaa')
248

    
249
    periodicity = models.PositiveIntegerField(
250
            choices=PERIODICITIES,
251
            verbose_name=u"Périodicité",
252
            default=1,
253
            blank=True,
254
            null=True)
255

    
256
    PERIODS = (
257
            (1, u'Toutes les semaines'),
258
            (2, u'Une semaine sur deux'),
259
            (3, u'Une semaine sur trois'),
260
            (4, u'Une semaine sur quatre'),
261
            (5, u'Une semaine sur cinq')
262
    )
263
    OFFSET = range(0,4)
264
    week_offset = models.PositiveIntegerField(
265
            choices=zip(OFFSET, OFFSET),
266
            verbose_name=u"Décalage en semaines par rapport au 1/1/1970 pour le calcul de période",
267
            default=0)
268
    week_period = models.PositiveIntegerField(
269
            choices=PERIODS,
270
            verbose_name=u"Période en semaines",
271
            blank=True, null=True)
272

    
273
    PARITIES = (
274
            (0, u'Les semaines paires'),
275
            (1, u'Les semaines impaires')
276
    )
277
    week_parity = models.PositiveIntegerField(
278
            choices=PARITIES,
279
            verbose_name=u"Parité des semaines",
280
            blank=True, null=True)
281

    
282
    WEEK_RANKS = (
283
            (0, u'La première semaine du mois'),
284
            (1, u'La deuxième semaine du mois'),
285
            (2, u'La troisième semaine du mois'),
286
            (3, u'La quatrième semaine du mois'),
287
            (4, u'La dernière semaine du mois')
288
    )
289

    
290
    week_rank = models.PositiveIntegerField(
291
            verbose_name=u"Rang de la semaine dans le mois",
292
            choices=WEEK_RANKS,
293
            blank=True, null=True)
294

    
295
    def clean(self):
296
        if (self.week_period is None) + (self.week_parity is None) + \
297
                (self.week_rank is None) != 2:
298
            raise forms.ValidationError('Only one periodicity criteria can be used')
299
        if self.week_period and self.start_date:
300
            self.week_offset = weeks_since_epoch(self.start_date) % self.week_period
301

    
302
    def __unicode__(self):
303
        s = u'%s pour au %s le %s de %s à %s' % \
304
                (self.worker, ', '.join(map(unicode, self.services.all())), self.weekday, self.start_time,
305
                        self.end_time)
306
        if self.end_time:
307
            s += u' à partir du %s' % self.start_date
308
        else:
309
            s += u' du %s au %s' % (self.start_data, self.end_date)
310
        return s
311

    
312
    class Meta:
313
        verbose_name = u'Emploi du temps'
314
        verbose_name_plural = u'Emplois du temps'
315

    
316
    def to_interval(self, date):
317
        return Interval(datetime.combine(date, self.start_time),
318
                datetime.combine(date, self.end_time))
319

    
320
class HolidayQuerySet(query.QuerySet):
321
    # To grab group holidays:
322
    # No worker AND
323
    # Either the holiday has no service, that means for all
324
    # Or the user must be in the service of the holiday
325
    def for_worker(self, worker):
326
        filter_query = models.Q(worker=worker) \
327
              | models.Q(worker__isnull=True,
328
                           service=None) \
329
              | models.Q(worker__isnull=True,
330
                           service__in=worker.services.all())
331
        return self.filter(filter_query)
332

    
333
    def for_worker_id(self, worker_id):
334
        worker = None
335
        try:
336
            worker = Worker.objects.get(pk=worker_id)
337
        except:
338
            return None
339
        filter_query = models.Q(worker=worker) \
340
              | models.Q(worker__isnull=True,
341
                           service=None) \
342
              | models.Q(worker__isnull=True,
343
                           service__in=worker.services.all())
344
        return self.filter(filter_query)
345

    
346
    def for_type(self, holiday_type):
347
        return self.filter(holiday_type=holiday_type)
348

    
349
    def for_service(self, service):
350
        return self.filter(worker__isnull=True) \
351
                   .filter(models.Q(service=service)
352
                          |models.Q(service__isnull=True))
353

    
354
    def for_service_workers(self, service):
355
        return self.filter(worker__services=service)
356

    
357
    def future(self):
358
        return self.filter(end_date__gte=date.today())
359

    
360
    def today(self):
361
        today = date.today()
362
        return self.filter(start_date__lte=today,
363
                end_date__gte=today)
364

    
365
    def for_period(self, start_date, end_date):
366
        return self.filter(start_date__lte=end_date, end_date__gte=start_date)
367

    
368
def time2french(time):
369
    if time.minute:
370
        return '{0}h{1}'.format(time.hour, time.minute)
371
    return '{0}h'.format(time.hour)
372

    
373
class Holiday(BaseModelMixin, models.Model):
374
    objects = PassThroughManager().for_queryset_class(HolidayQuerySet)()
375

    
376
    holiday_type = models.ForeignKey('ressources.HolidayType',
377
            verbose_name=u'Type de congé')
378
    worker = models.ForeignKey(Worker, blank=True, null=True,
379
            verbose_name=u"Personnel")
380
    service = models.ForeignKey(Service, blank=True, null=True,
381
            verbose_name=u"Service")
382
    start_date = models.DateField(verbose_name=u"Date de début",
383
        help_text=u'format: jj/mm/aaaa')
384
    end_date = models.DateField(verbose_name=u"Date de fin",
385
        help_text=u'format: jj/mm/aaaa')
386
    start_time = models.TimeField(verbose_name=u"Horaire de début", blank=True,
387
            null=True)
388
    end_time = models.TimeField(verbose_name=u"Horaire de fin", blank=True,
389
            null=True)
390
    comment = models.TextField(verbose_name=u'Commentaire', blank=True)
391

    
392
    class Meta:
393
        verbose_name = u'Congé'
394
        verbose_name_plural = u'Congés'
395
        ordering = ('start_date', 'start_time')
396

    
397
    def is_current(self):
398
        return self.start_date <= date.today() <= self.end_date
399

    
400
    def __unicode__(self):
401
        ret = ''
402
        if self.start_date == self.end_date:
403
            ret = u'le {0}'.format(date_filter(self.start_date, 'j F Y'))
404
            if self.start_time:
405
                ret += u', à partir de {0}'.format(time2french(self.start_time))
406
            if self.end_time:
407
                ret += u", jusqu'à {0}".format(time2french(self.end_time))
408
        else:
409
            ret = u'du {0}'.format(date_filter(self.start_date, 'j F Y'))
410
            if self.start_time:
411
                ret += u' (à partir de {0})'.format(time2french(self.start_time))
412
            ret += u' au {0}'.format(date_filter(self.end_date, 'j F Y'))
413
            if self.end_time:
414
                ret += u" (jusqu'à {0})".format(time2french(self.end_time))
415
        return ret
416

    
417
    def to_interval(self, date):
418
        if date == self.start_date:
419
            start_time = self.start_time or datetime_time(8, 0)
420
        else:
421
            start_time = datetime_time(8, 0)
422
        if date == self.end_date:
423
            end_time = self.end_time or datetime_time(20, 0)
424
        else:
425
            end_time = datetime_time(20, 0)
426
        return Interval(datetime.combine(self.start_date, start_time),
427
                datetime.combine(self.end_date, end_time))
(5-5/8)