Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ 84614733

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
        blank=True, null=True)
39
    display_name = models.CharField(max_length=256,
40
            verbose_name=u'Nom complet', editable=False)
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
    type = models.ForeignKey('ressources.WorkerType',
77
            verbose_name=u'Type de personnel')
78
    services = models.ManyToManyField('ressources.Service', blank=True, null=True)
79
    enabled = models.BooleanField(verbose_name=u'Actif',
80
                default=True)
81
    old_camsp_id = models.CharField(max_length=256,
82
            verbose_name=u'Ancien ID au CAMSP', blank=True, null=True)
83
    old_cmpp_id = models.CharField(max_length=256,
84
            verbose_name=u'Ancien ID au CMPP', blank=True, null=True)
85
    old_sessad_dys_id = models.CharField(max_length=256,
86
            verbose_name=u'Ancien ID au SESSAD TED', blank=True, null=True)
87
    old_sessad_ted_id = models.CharField(max_length=256,
88
            verbose_name=u'Ancien ID au SESSAD DYS', blank=True, null=True)
89

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

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

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

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

    
108

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

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

    
139
reversion.register(ExternalWorker, follow=['people_ptr'])
140

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

    
172
reversion.register(ExternalTherapist, follow=['people_ptr'])
173

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

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

    
183
reversion.register(UserWorker)
184

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

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

    
191
class TimeTableQuerySet(query.QuerySet):
192
    def current(self, today=None):
193
        if today is None:
194
            today = date.today()
195
        return self.filter(start_date__lte=today, end_date__gte=today)
196

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

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

    
230

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

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

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

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

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

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

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

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

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

    
316
    class Meta:
317
        verbose_name = u'Emploi du temps'
318
        verbose_name_plural = u'Emplois du temps'
319

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

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

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

    
350
    def for_type(self, holiday_type):
351
        return self.filter(holiday_type=holiday_type)
352

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

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

    
363
    def future(self):
364
        return self.filter(end_date__gte=date.today())
365

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

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

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

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

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

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

    
403
    def is_current(self):
404
        return self.start_date <= date.today() <= self.end_date
405

    
406
    def __unicode__(self):
407
        ret = ''
408
        if self.start_date == self.end_date:
409
            ret = u'le {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
            if self.end_time:
413
                ret += u", jusqu'à {0}".format(time2french(self.end_time))
414
        else:
415
            ret = u'du {0}'.format(date_filter(self.start_date, 'j F Y'))
416
            if self.start_time:
417
                ret += u' (à partir de {0})'.format(time2french(self.start_time))
418
            ret += u' au {0}'.format(date_filter(self.end_date, 'j F Y'))
419
            if self.end_time:
420
                ret += u" (jusqu'à {0})".format(time2french(self.end_time))
421
        return ret
422

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