Project

General

Profile

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

calebasse / calebasse / personnes / models.py @ ee47faa1

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.models import PhoneNumberField
14
from calebasse.ressources.models import Service, NamedAbstractModel
15
from calebasse.models import BaseModelMixin, WeekRankField
16
from calebasse.utils import weeks_since_epoch, weekday_ranks
17

    
18
from interval import Interval
19

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

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

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

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

    
42
    def save(self, **kwargs):
43
        self.display_name = self.first_name + ' ' + self.last_name.upper()
44
        super(People, self).save(**kwargs)
45

    
46
    def __unicode__(self):
47
        return self.display_name
48

    
49
    class Meta:
50
        ordering = ['last_name', 'first_name']
51

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

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

    
66
    def is_away(self):
67
        if self.timetable_set.filter(weekday=date.today().weekday()).exists():
68
            return False
69
        return True
70

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

    
77
    class Meta:
78
        verbose_name = u'Personnel'
79
        verbose_name_plural = u'Personnels'
80

    
81
reversion.register(Worker, follow=['people_ptr'])
82
reversion.register(User)
83

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

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

    
93
reversion.register(UserWorker)
94

    
95
class SchoolTeacher(People):
96
    schools = models.ManyToManyField('ressources.School')
97
    role = models.ForeignKey('ressources.SchoolTeacherRole')
98

    
99
reversion.register(SchoolTeacher, follow=['user_ptr'])
100

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

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

    
125

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

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

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

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

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

    
185
    week_rank = WeekRankField(
186
            verbose_name=u"Rang de la semaine dans le mois",
187
            choices=WEEK_RANKS,
188
            default=None)
189

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

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

    
207
    class Meta:
208
        verbose_name = u'Emploi du temps'
209
        verbose_name_plural = u'Emplois du temps'
210

    
211
    def to_interval(self, date):
212
        return Interval(datetime.combine(date, self.start_time),
213
                datetime.combine(date, self.end_time))
214

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

    
222
    def for_service(self, service):
223
        return self.filter(worker__isnull=True) \
224
                   .filter(models.Q(service=service)
225
                          |models.Q(service__isnull=True)) \
226

    
227
    def for_service_workers(self, service):
228
        return self.filter(worker__services=service)
229

    
230
    def future(self):
231
        return self.filter(end_date__gte=date.today())
232

    
233
    def today(self):
234
        today = date.today()
235
        return self.filter(start_date__lte=today,
236
                end_date__gte=today)
237

    
238
    def for_period(self, start_date, end_date):
239
        return self.filter(start_date__lte=end_date, end_date__gte=start_date)
240

    
241
def time2french(time):
242
    if time.minute:
243
        return '{0}h{1}'.format(time.hour, time.minute)
244
    return '{0}h'.format(time.hour)
245

    
246
class Holiday(BaseModelMixin, models.Model):
247
    objects = PassThroughManager().for_queryset_class(HolidayQuerySet)()
248

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

    
263
    class Meta:
264
        verbose_name = u'Congé'
265
        verbose_name_plural = u'Congés'
266
        ordering = ('start_date', 'start_time')
267

    
268
    def is_current(self):
269
        return self.start_date <= date.today() <= self.end_date
270

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

    
284
class ExternalDoctor(People):
285
    class Meta:
286
        verbose_name = u'Médecin extérieur'
287
        verbose_name_plural = u'Médecins extérieurs'
288

    
289
class ExternalIntervener(People):
290
    class Meta:
291
        verbose_name = u'Intervenant extérieur'
292
        verbose_name_plural = u'Intervenants extérieurs'
(4-4/7)