Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ 4e0fb78c

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
            db_index=True)
38
    first_name = models.CharField(max_length=128, verbose_name=u'Prénom(s)',
39
        blank=True, null=True)
40
    display_name = models.CharField(max_length=256,
41
            verbose_name=u'Nom complet', editable=False, db_index=True)
42
    gender = models.IntegerField(verbose_name=u"Genre", choices=GENDERS,
43
            max_length=1, blank=True, null=True)
44
    email = models.EmailField(blank=True, null=True)
45
    phone = PhoneNumberField(verbose_name=u"Téléphone", blank=True, null=True)
46

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

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

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

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

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

    
74

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

    
91
    def is_active(self):
92
        return self.enabled
93

    
94
    def is_away(self):
95
        if self.timetable_set.filter(weekday=date.today().weekday()).exists():
96
            return False
97
        return True
98

    
99
    @models.permalink
100
    def get_absolute_url(self):
101
        return ('worker_update', (), {
102
            'service': self.services.all()[0].name.lower(),
103
            'pk': self.pk })
104

    
105
    class Meta:
106
        verbose_name = u'Personnel'
107
        verbose_name_plural = u'Personnels'
108

    
109

    
110
reversion.register(Worker, follow=['people_ptr'])
111
reversion.register(User)
112

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

    
140
reversion.register(ExternalWorker, follow=['people_ptr'])
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
reversion.register(ExternalTherapist, follow=['people_ptr'])
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
reversion.register(UserWorker)
185

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

    
190
reversion.register(SchoolTeacher, follow=['user_ptr'])
191

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

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

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

    
232

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

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

    
255
    periodicity = models.PositiveIntegerField(
256
            choices=PERIODICITIES,
257
            verbose_name=u"Périodicité",
258
            default=1,
259
            blank=True,
260
            null=True)
261

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

    
279
    PARITIES = (
280
            (0, u'Les semaines paires'),
281
            (1, u'Les semaines impaires')
282
    )
283
    week_parity = models.PositiveIntegerField(
284
            choices=PARITIES,
285
            verbose_name=u"Parité des semaines",
286
            blank=True, null=True)
287

    
288
    WEEK_RANKS = (
289
            (0, u'La première semaine du mois'),
290
            (1, u'La deuxième semaine du mois'),
291
            (2, u'La troisième semaine du mois'),
292
            (3, u'La quatrième semaine du mois'),
293
            (4, u'La dernière semaine du mois')
294
    )
295

    
296
    week_rank = models.PositiveIntegerField(
297
            verbose_name=u"Rang de la semaine dans le mois",
298
            choices=WEEK_RANKS,
299
            blank=True, null=True)
300

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

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

    
318
    class Meta:
319
        verbose_name = u'Emploi du temps'
320
        verbose_name_plural = u'Emplois du temps'
321

    
322
    def to_interval(self, date):
323
        return Interval(datetime.combine(date, self.start_time),
324
                datetime.combine(date, self.end_time))
325

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

    
339
    def for_worker_id(self, worker_id):
340
        worker = None
341
        try:
342
            worker = Worker.objects.get(pk=worker_id)
343
        except:
344
            return None
345
        filter_query = models.Q(worker=worker) \
346
              | models.Q(worker__isnull=True,
347
                           service=None) \
348
              | models.Q(worker__isnull=True,
349
                           service__in=worker.services.all())
350
        return self.filter(filter_query)
351

    
352
    def for_type(self, holiday_type):
353
        return self.filter(holiday_type=holiday_type)
354

    
355
    def for_service(self, service):
356
        return self.filter(worker__isnull=True) \
357
                   .filter(models.Q(service=service)
358
                          |models.Q(service__isnull=True))
359

    
360
    def for_service_workers(self, service):
361
        return self.filter(models.Q(worker__services=service)
362
                |models.Q(service=service)
363
                |models.Q(worker__isnull=True, service__isnull=True))
364

    
365
    def future(self):
366
        return self.filter(end_date__gte=date.today())
367

    
368
    def today(self, today=None):
369
        today = today or date.today()
370
        return self.filter(start_date__lte=today,
371
                end_date__gte=today)
372

    
373
    def for_period(self, start_date, end_date):
374
        return self.filter(start_date__lte=end_date, end_date__gte=start_date)
375

    
376
def time2french(time):
377
    if time.minute:
378
        return '{0}h{1}'.format(time.hour, time.minute)
379
    return '{0}h'.format(time.hour)
380

    
381
class Holiday(BaseModelMixin, models.Model):
382
    objects = PassThroughManager().for_queryset_class(HolidayQuerySet)()
383

    
384
    holiday_type = models.ForeignKey('ressources.HolidayType',
385
            verbose_name=u'Type de congé')
386
    worker = models.ForeignKey(Worker, blank=True, null=True,
387
            verbose_name=u"Personnel")
388
    service = models.ForeignKey(Service, blank=True, null=True,
389
            verbose_name=u"Service")
390
    start_date = models.DateField(verbose_name=u"Date de début",
391
        help_text=u'format: jj/mm/aaaa')
392
    end_date = models.DateField(verbose_name=u"Date de fin",
393
        help_text=u'format: jj/mm/aaaa')
394
    start_time = models.TimeField(verbose_name=u"Horaire de début", blank=True,
395
            null=True)
396
    end_time = models.TimeField(verbose_name=u"Horaire de fin", blank=True,
397
            null=True)
398
    comment = models.TextField(verbose_name=u'Commentaire', blank=True)
399

    
400
    class Meta:
401
        verbose_name = u'Congé'
402
        verbose_name_plural = u'Congés'
403
        ordering = ('start_date', 'start_time')
404

    
405
    def is_current(self):
406
        return self.start_date <= date.today() <= self.end_date
407

    
408
    def __unicode__(self):
409
        ret = ''
410
        if self.start_date == self.end_date:
411
            ret = u'le {0}'.format(date_filter(self.start_date, 'j F Y'))
412
            if self.start_time:
413
                ret += u', à partir de {0}'.format(time2french(self.start_time))
414
            if self.end_time:
415
                ret += u", jusqu'à {0}".format(time2french(self.end_time))
416
        else:
417
            ret = u'du {0}'.format(date_filter(self.start_date, 'j F Y'))
418
            if self.start_time:
419
                ret += u' (à partir de {0})'.format(time2french(self.start_time))
420
            ret += u' au {0}'.format(date_filter(self.end_date, 'j F Y'))
421
            if self.end_time:
422
                ret += u" (jusqu'à {0})".format(time2french(self.end_time))
423
        return ret
424

    
425
    def to_interval(self, date=None):
426
        if date == self.start_date:
427
            start_time = self.start_time or datetime_time(8, 0)
428
        else:
429
            start_time = datetime_time(8, 0)
430
        if date == self.end_date:
431
            end_time = self.end_time or datetime_time(20, 0)
432
        else:
433
            end_time = datetime_time(20, 0)
434
        return Interval(datetime.combine(self.start_date, start_time),
435
                datetime.combine(self.end_date, end_time))
(5-5/8)