Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ 428081e0

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

    
3
from datetime import datetime, date
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

    
13
from calebasse.ressources.models import Service, NamedAbstractModel
14
from calebasse.models import BaseModelMixin, WeekRankField
15
from calebasse.utils import weeks_since_epoch, weekday_ranks
16

    
17
from interval import Interval
18

    
19
from model_utils import Choices
20
from model_utils.managers import PassThroughManager
21

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

    
26
class People(BaseModelMixin, models.Model):
27
    GENDERS =  Choices(
28
            (1, 'homme'),
29
            (2, 'femme'),
30
    )
31

    
32
    last_name = models.CharField(max_length=128, verbose_name=u'Nom')
33
    first_name = models.CharField(max_length=128, verbose_name=u'Prénom(s)')
34
    display_name = models.CharField(max_length=256,
35
            verbose_name=u'Nom complet', editable=False)
36
    gender = models.IntegerField(verbose_name=u"Genre", choices=GENDERS,
37
            max_length=1, default=0, blank=True)
38

    
39
    def save(self, **kwargs):
40
        self.display_name = self.first_name + ' ' + self.last_name
41
        super(People, self).save(**kwargs)
42

    
43
    def __unicode__(self):
44
        return self.display_name
45

    
46
    class Meta:
47
        ordering = ['last_name', 'first_name']
48

    
49
class WorkerQuerySet(query.QuerySet):
50
    def for_service(self, service, type=None):
51
        if type:
52
            return self.filter(services__in=[service]).filter(type=type)
53
        else:
54
            return self.filter(services__in=[service])
55

    
56
class Worker(People):
57
    objects = PassThroughManager.for_queryset_class(WorkerQuerySet)()
58
    # TODO : use manytomany here ?
59
    type = models.ForeignKey('ressources.WorkerType',
60
            verbose_name=u'Type de personnel')
61
    services = models.ManyToManyField('ressources.Service')
62

    
63
    def is_away(self):
64
        if self.timetable_set.filter(weekday=date.today().weekday()).exists():
65
            return False
66
        return True
67

    
68
    @models.permalink
69
    def get_absolute_url(self):
70
        return ('worker_update', (), {
71
            'service': self.services.all()[0].name.lower(),
72
            'pk': self.pk })
73

    
74
    class Meta:
75
        verbose_name = u'Personnel'
76
        verbose_name_plural = u'Personnels'
77

    
78
reversion.register(Worker, follow=['people_ptr'])
79
reversion.register(User)
80

    
81
class UserWorker(BaseModelMixin, models.Model):
82
    user = models.OneToOneField('auth.User')
83
    worker = models.ForeignKey('Worker',
84
            verbose_name=u'Personnel')
85

    
86
    def __unicode__(self):
87
        return u'Lien entre la personne {0} et l\'utilisateur {1}'.format(
88
                self.worker, self.user)
89

    
90
reversion.register(UserWorker)
91

    
92
class SchoolTeacher(People):
93
    schools = models.ManyToManyField('ressources.School')
94
    role = models.ForeignKey('ressources.SchoolTeacherRole')
95

    
96
reversion.register(SchoolTeacher, follow=['user_ptr'])
97

    
98
class TimeTableQuerySet(query.QuerySet):
99
    def current(self, today=None):
100
        if today is None:
101
            today = date.today()
102
        return self.filter(start_date__lte=today, end_date__gte=today)
103

    
104
    def for_today(self, today=None):
105
        if today is None:
106
            today = date.today()
107
        qs = self.current(today)
108
        qs = self.filter(weekday=today.weekday())
109
        filters = []
110
        # week periods
111
        for week_period in range(1,5):
112
            filters.append(models.Q(week_period=week_period,
113
                week_offset=weeks_since_epoch(today) % week_period))
114
        # week parity
115
        parity = (today.isocalendar()[1]-1) % 2
116
        filters.append(models.Q(week_parity=parity))
117
        # week ranks
118
        filters.append(models.Q(week_rank__in=weekday_ranks(today)))
119
        qs = qs.filter(reduce(models.Q.__or__, filters))
120
        return qs
121

    
122

    
123
class TimeTable(BaseModelMixin, models.Model):
124
    objects = PassThroughManager.for_queryset_class(TimeTableQuerySet)()
125
    worker = models.ForeignKey(Worker,
126
            verbose_name=u'Intervenant')
127
    services = models.ManyToManyField('ressources.Service')
128
    WEEKDAYS = Choices(*enumerate(('lundi', 'mardi', 'mercredi', 'jeudi', 'vendredi',
129
        'samedi', 'dimanche')))
130

    
131
    weekday = models.PositiveIntegerField(
132
        verbose_name=u"Jour de la semaine",
133
        choices=WEEKDAYS)
134
    start_time = models.TimeField(
135
        verbose_name=u'Heure de début')
136
    end_time = models.TimeField(
137
        verbose_name=u'Heure de fin')
138
    start_date = models.DateField(
139
        verbose_name=u'Début',
140
        help_text=u'format: jj/mm/aaaa')
141
    end_date = models.DateField(
142
        verbose_name=u'Fin', blank=True, null=True,
143
        help_text=u'format: jj/mm/aaaa')
144

    
145
    PERIODS = (
146
            (1, u'Toutes les semaines'),
147
            (2, u'Une semaine sur deux'),
148
            (3, u'Une semaine sur trois'),
149
            (4, 'Une semaine sur quatre'),
150
            (4, 'Une semaine sur cinq')
151
    )
152
    OFFSET = range(0,4)
153
    week_offset = models.PositiveIntegerField(
154
            choices=zip(OFFSET, OFFSET),
155
            verbose_name=u"Décalage en semaines par rapport au 1/1/1970 pour le calcul de période",
156
            default=0)
157
    week_period = models.PositiveIntegerField(
158
            choices=PERIODS,
159
            verbose_name=u"Période en semaines",
160
            default=1,
161
            blank=True,
162
            null=True)
163

    
164
    PARITIES = (
165
            (0, 'Les semaines paires'),
166
            (1, 'Les semaines impaires')
167
    )
168
    week_parity = models.PositiveIntegerField(
169
            choices=PARITIES,
170
            verbose_name=u"Parité des semaines",
171
            default=None,
172
            blank=True, null=True)
173

    
174
    WEEK_RANKS = (
175
            (0, u'La première semaine du mois'),
176
            (1, u'La deuxième semaine du mois'),
177
            (2, u'La troisième semaine du mois'),
178
            (3, u'La quatrième semaine du mois'),
179
            (4, u'La dernière semaine du mois')
180
    )
181

    
182
    week_rank = WeekRankField(
183
            verbose_name=u"Rang de la semaine dans le mois",
184
            choices=WEEK_RANKS,
185
            default=None)
186

    
187
    def clean(self):
188
        if (self.week_period is None) + (self.week_parity is None) + \
189
                (self.week_rank is None) != 2:
190
            raise forms.ValidationError('only one periodicity criteria can be used')
191
        if self.week_period and self.start_date:
192
            self.week_offset = weeks_since_epoch(self.start_date) % self.week_period
193

    
194
    def __unicode__(self):
195
        s = u'%s pour au %s le %s de %s à %s' % \
196
                (self.worker, ', '.join(map(unicode, self.services.all())), self.weekday, self.start_time,
197
                        self.end_time)
198
        if self.end_time:
199
            s += u' à partir du %s' % self.start_date
200
        else:
201
            s += u' du %s au %s' % (self.start_data, self.end_date)
202
        return s
203

    
204
    class Meta:
205
        verbose_name = u'Emploi du temps'
206
        verbose_name_plural = u'Emplois du temps'
207

    
208
    def to_interval(self, date):
209
        return Interval(datetime.combine(date, self.start_time),
210
                datetime.combine(date, self.end_time))
211

    
212
class HolidayQuerySet(query.QuerySet):
213
    def for_worker(self, worker):
214
        filter_query = models.Q(worker=worker) \
215
              | models.Q(worker__isnull=True,
216
                           service=worker.services.all())
217
        return self.filter(filter_query)
218

    
219
    def for_service(self, service):
220
        return self.filter(worker__isnull=True) \
221
                   .filter(models.Q(service=service)
222
                          |models.Q(service__isnull=True)) \
223

    
224
    def for_service_workers(self, service):
225
        return self.filter(worker__services=service)
226

    
227
    def future(self):
228
        return self.filter(end_date__gte=date.today())
229

    
230
    def today(self):
231
        today = date.today()
232
        return self.filter(start_date__lte=today,
233
                end_date__gte=today)
234

    
235
    def for_period(self, start_date, end_date):
236
        return self.filter(start_date__lte=end_date, end_date__gte=start_date)
237

    
238
def time2french(time):
239
    if time.minute:
240
        return '{0}h{1}'.format(time.hour, time.minute)
241
    return '{0}h'.format(time.hour)
242

    
243
class Holiday(BaseModelMixin, models.Model):
244
    objects = PassThroughManager().for_queryset_class(HolidayQuerySet)()
245

    
246
    worker = models.ForeignKey(Worker, blank=True, null=True,
247
            verbose_name=u"Personnel")
248
    service = models.ForeignKey(Service, blank=True, null=True,
249
            verbose_name=u"Service")
250
    start_date = models.DateField(verbose_name=u"Date de début",
251
        help_text=u'format: jj/mm/aaaa')
252
    end_date = models.DateField(verbose_name=u"Date de fin",
253
        help_text=u'format: jj/mm/aaaa')
254
    start_time = models.TimeField(verbose_name=u"Horaire de début", blank=True,
255
            null=True)
256
    end_time = models.TimeField(verbose_name=u"Horaire de fin", blank=True,
257
            null=True)
258
    comment = models.TextField(verbose_name=u'Commentaire', blank=True)
259

    
260
    class Meta:
261
        verbose_name = u'Congé'
262
        verbose_name_plural = u'Congés'
263
        ordering = ('start_date', 'start_time')
264

    
265
    def is_current(self):
266
        return self.start_date <= date.today() <= self.end_date
267

    
268
    def __unicode__(self):
269
        ret = ''
270
        if self.start_date == self.end_date:
271
            ret = u'le {0}'.format(date_filter(self.start_date, 'j F Y'))
272
            if self.start_time and self.end_time:
273
                ret += u', de {0} à {1}'.format(time2french(self.start_time),
274
                time2french(self.end_time))
275
        else:
276
            ret = u'du {0} au {1}'.format(
277
                    date_filter(self.start_date, 'j F'),
278
                    date_filter(self.end_date, 'j F Y'))
279
        return ret
280

    
281
class ExternalDoctor(People):
282
    class Meta:
283
        verbose_name = u'Médecin extérieur'
284
        verbose_name_plural = u'Médecins extérieurs'
285

    
286
class ExternalIntervener(People):
287
    class Meta:
288
        verbose_name = u'Intervenant extérieur'
289
        verbose_name_plural = u'Intervenants extérieurs'
290

    
(4-4/7)