Projet

Général

Profil

Télécharger (17,6 ko) Statistiques
| Branche: | Tag: | Révision:

calebasse / calebasse / personnes / models.py @ c6ef9ff5

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
from model_utils.managers import InheritanceManager
12

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

    
19
from interval import Interval
20

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

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

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

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

    
46
    def save(self, **kwargs):
47
        if self.first_name:
48
            self.display_name = self.first_name + ' ' + self.last_name.upper()
49
        else:
50
            self.display_name = self.last_name.upper()
51
        super(People, self).save(**kwargs)
52

    
53
    def __unicode__(self):
54
        return self.display_name
55

    
56
    def get_initials(self):
57
        initials = []
58
        if self.first_name:
59
            initials = [name[0].upper() for name in ' '.join(self.first_name.split('-')).split()]
60
        initials += [name[0].upper() for name in ' '.join(self.last_name.split('-')).split()]
61
        return ''.join(initials)
62

    
63
    class Meta:
64
        ordering = ['last_name', 'first_name']
65

    
66
class WorkerQuerySet(query.QuerySet):
67
    def for_service(self, service, type=None):
68
        if type:
69
            return self.filter(enabled=True, services__in=[service], type=type)
70
        else:
71
            return self.filter(enabled=True, services__in=[service])
72

    
73

    
74
class Worker(People):
75
    objects = PassThroughManager.for_queryset_class(WorkerQuerySet)()
76

    
77
    initials = models.CharField(max_length=5, verbose_name=u'Initiales', default='', blank=True)
78
    type = models.ForeignKey('ressources.WorkerType',
79
            verbose_name=u'Type de personnel')
80
    services = models.ManyToManyField('ressources.Service', blank=True, null=True)
81
    enabled = models.BooleanField(verbose_name=u'Actif',
82
                default=True)
83
    old_camsp_id = models.CharField(max_length=256,
84
            verbose_name=u'Ancien ID au CAMSP', blank=True, null=True)
85
    old_cmpp_id = models.CharField(max_length=256,
86
            verbose_name=u'Ancien ID au CMPP', blank=True, null=True)
87
    old_sessad_dys_id = models.CharField(max_length=256,
88
            verbose_name=u'Ancien ID au SESSAD TED', blank=True, null=True)
89
    old_sessad_ted_id = models.CharField(max_length=256,
90
            verbose_name=u'Ancien ID au SESSAD DYS', blank=True, null=True)
91

    
92
    def save(self, **kwargs):
93
        if not self.initials:
94
            self.initials = self.get_initials()
95
        super(Worker, self).save(**kwargs)
96

    
97
    def is_active(self):
98
        return self.enabled
99

    
100
    def is_away(self):
101
        if self.timetable_set.filter(weekday=date.today().weekday()).exists():
102
            return False
103
        return True
104

    
105
    @models.permalink
106
    def get_absolute_url(self):
107
        return ('worker_update', (), {
108
            'service': self.services.all()[0].name.lower(),
109
            'pk': self.pk })
110

    
111
    class Meta:
112
        verbose_name = u'Personnel'
113
        verbose_name_plural = u'Personnels'
114

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

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

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

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

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

    
186
class TimeTableQuerySet(query.QuerySet):
187
    def current(self, today=None):
188
        if today is None:
189
            today = date.today()
190
        return self.filter(models.Q(start_date__lte=today) | models.Q(start_date__isnull=True)) \
191
                    .filter(models.Q(end_date__gte=today) | models.Q(end_date__isnull=True))
192

    
193
    def for_today(self, today=None):
194
        if today is None:
195
            today = date.today()
196
        qs = self.current(today)
197
        qs = qs.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
                           services = None) \
329
              | models.Q(worker__isnull=True,
330
                           services__in = worker.services.all())
331
        return self.filter(filter_query).distinct()
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
                           services = None) \
342
              | models.Q(worker__isnull=True,
343
                           services__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(services = service)
352
                          |models.Q(services__isnull = True))
353

    
354
    def for_service_workers(self, service):
355
        return self.filter(models.Q(worker__services = [service])
356
                |models.Q(services__in = [service])
357
                |models.Q(worker__isnull=True, services__isnull = True))
358

    
359
    def future(self):
360
        return self.filter(end_date__gte=date.today())
361

    
362
    def today(self, today=None):
363
        today = today or date.today()
364
        return self.filter(start_date__lte=today,
365
                end_date__gte=today)
366

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

    
370
    def for_timed_period(self, date, start_time, end_time):
371
        filter_query = models.Q(start_date__lt=date, end_date__gt=date) \
372
            | models.Q(start_date=date, start_time__isnull=True, end_date__gt=date) \
373
            | models.Q(start_date=date, start_time__lt=end_time, end_date__gt=date) \
374
            | models.Q(start_date__lt=date, end_date=date, end_time__isnull=True) \
375
            | models.Q(start_date__lt=date, end_date=date, end_time__gt=start_time) \
376
            | models.Q(start_date=date, end_date=date, start_time__isnull=True, end_time__isnull=True) \
377
            | models.Q(start_date=date, end_date=date, start_time__isnull=True, end_time__gt=start_time) \
378
            | models.Q(start_date=date, end_date=date, start_time__lt=end_time, end_time__isnull=True) \
379
            | models.Q(start_date=date, end_date=date, start_time__lte=start_time, end_time__gt=start_time) \
380
            | models.Q(start_date=date, end_date=date, start_time__lt=end_time, end_time__gte=end_time)
381
        return self.filter(filter_query)
382

    
383
def time2french(time):
384
    if time.minute:
385
        return '{0}h{1}'.format(time.hour, time.minute)
386
    return '{0}h'.format(time.hour)
387

    
388
class Holiday(BaseModelMixin, models.Model):
389
    objects = PassThroughManager().for_queryset_class(HolidayQuerySet)()
390

    
391
    holiday_type = models.ForeignKey('ressources.HolidayType',
392
            verbose_name=u'Type de congé')
393
    worker = models.ForeignKey(Worker, blank=True, null=True,
394
            verbose_name=u"Personnel")
395
    services = models.ManyToManyField(Service, blank = False, null = False,
396
                                      verbose_name = u'Services')
397
    start_date = models.DateField(verbose_name=u"Date de début",
398
        help_text=u'format: jj/mm/aaaa')
399
    end_date = models.DateField(verbose_name=u"Date de fin",
400
        help_text=u'format: jj/mm/aaaa')
401
    start_time = models.TimeField(verbose_name=u"Horaire de début", blank=True,
402
            null=True)
403
    end_time = models.TimeField(verbose_name=u"Horaire de fin", blank=True,
404
            null=True)
405
    comment = models.TextField(verbose_name=u'Commentaire', blank=True)
406

    
407
    class Meta:
408
        verbose_name = u'Congé'
409
        verbose_name_plural = u'Congés'
410
        ordering = ('start_date', 'start_time')
411

    
412
    def is_current(self):
413
        return self.start_date <= date.today() <= self.end_date
414

    
415
    def for_all_services(self):
416
        return self.services.count() == Service.objects.count()
417

    
418
    def __unicode__(self):
419
        ret = ''
420
        if self.start_date == self.end_date:
421
            ret = u'le {0}'.format(date_filter(self.start_date, 'j F Y'))
422
            if self.start_time:
423
                ret += u', à partir de {0}'.format(time2french(self.start_time))
424
            if self.end_time:
425
                ret += u", jusqu'à {0}".format(time2french(self.end_time))
426
        else:
427
            ret = u'du {0}'.format(date_filter(self.start_date, 'j F Y'))
428
            if self.start_time:
429
                ret += u' (à partir de {0})'.format(time2french(self.start_time))
430
            ret += u' au {0}'.format(date_filter(self.end_date, 'j F Y'))
431
            if self.end_time:
432
                ret += u" (jusqu'à {0})".format(time2french(self.end_time))
433
        return ret
434

    
435
    def to_interval(self, date=None):
436
        if date == self.start_date:
437
            start_time = self.start_time or datetime_time(8, 0)
438
        else:
439
            start_time = datetime_time(8, 0)
440
        if date == self.end_date:
441
            end_time = self.end_time or datetime_time(20, 0)
442
        else:
443
            end_time = datetime_time(20, 0)
444
        return Interval(datetime.combine(self.start_date, start_time),
445
                datetime.combine(self.end_date, end_time))
(5-5/8)