Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ 9090c9dd

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
from ..middleware.request import get_request
25

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

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

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

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

    
55
    def __unicode__(self):
56
        return self.display_name
57

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

    
65
    class Meta:
66
        ordering = ['last_name', 'first_name']
67

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

    
75

    
76
class Worker(People):
77
    objects = PassThroughManager.for_queryset_class(WorkerQuerySet)()
78

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

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

    
99
    def is_active(self):
100
        return self.enabled
101

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

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

    
113
    class Meta:
114
        verbose_name = u'Personnel'
115
        verbose_name_plural = u'Personnels'
116

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

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

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

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

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

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

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

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

    
228

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

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

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

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

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

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

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

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

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

    
314
    class Meta:
315
        verbose_name = u'Emploi du temps'
316
        verbose_name_plural = u'Emplois du temps'
317

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

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

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

    
348
    def for_type(self, holiday_type):
349
        return self.filter(holiday_type = holiday_type)
350

    
351
    def for_service(self, service):
352
        return self.filter(worker__isnull = True) \
353
                   .filter(models.Q(services = service)
354
                          |models.Q(services__isnull = True))
355

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

    
361
    def future(self):
362
        return self.filter(end_date__gte=date.today())
363

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

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

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

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

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

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

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

    
414
    def is_current(self):
415
        return self.start_date <= date.today() <= self.end_date
416

    
417
    def for_all_services(self):
418
        return self.services.count() == Service.objects.count()
419

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

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