calebasse/calebasse/agenda/models.py @ 7a2fc3bb
| 96f613c6 | Benjamin Dauvergne | # -*- coding: utf-8 -*-
|
|
| ba34dc24 | Jérôme Schneider | ||
| 76974b6f | Benjamin Dauvergne | from datetime import datetime, date, timedelta
|
|
from copy import copy
|
|||
| ba34dc24 | Jérôme Schneider | ||
from django.utils.translation import ugettext_lazy as _
|
|||
| 76974b6f | Benjamin Dauvergne | from django.contrib.auth.models import User
|
|
| ba34dc24 | Jérôme Schneider | from django.db import models
|
|
| 4ce756c8 | Benjamin Dauvergne | from django import forms
|
|
| ba34dc24 | Jérôme Schneider | ||
| 11984ab3 | Jérôme Schneider | from calebasse.agenda import managers
|
|
| 4ce756c8 | Benjamin Dauvergne | from calebasse.utils import weeks_since_epoch, weekday_ranks
|
|
| e6b125bd | Mikaël Ates | from calebasse.personnes.models import Holiday
|
|
| 7a0a30d7 | Benjamin Dauvergne | from interval import Interval
|
|
| ba34dc24 | Jérôme Schneider | ||
__all__ = (
|
|||
'EventType',
|
|||
'Event',
|
|||
| 76974b6f | Benjamin Dauvergne | 'EventWithAct',
|
|
| ba34dc24 | Jérôme Schneider | )
|
|
class EventType(models.Model):
|
|||
'''
|
|||
Simple ``Event`` classifcation.
|
|||
'''
|
|||
class Meta:
|
|||
| e39efb4a | Jérôme Schneider | verbose_name = u'Type d\'événement'
|
|
verbose_name_plural = u'Types d\'événement'
|
|||
| ba34dc24 | Jérôme Schneider | ||
def __unicode__(self):
|
|||
return self.label
|
|||
| e39efb4a | Jérôme Schneider | label = models.CharField(_('label'), max_length=50)
|
|
| 68e9f8c1 | Frédéric Péters | rank = models.IntegerField(_('Sorting Rank'), null=True, blank=True, default=0)
|
|
| e39efb4a | Jérôme Schneider | ||
| 84091f2c | Jérôme Schneider | class Event(models.Model):
|
|
| ba34dc24 | Jérôme Schneider | '''
|
|
| 76974b6f | Benjamin Dauvergne | Container model for general agenda events
|
|
| ba34dc24 | Jérôme Schneider | '''
|
|
| e39efb4a | Jérôme Schneider | objects = managers.EventManager()
|
|
| ba34dc24 | Jérôme Schneider | ||
| e90f4c73 | Jérôme Schneider | title = models.CharField(_('Title'), max_length=60, blank=True, default="")
|
|
| a334f1e6 | Mikaël Ates | description = models.TextField(_('Description'), max_length=100, blank=True, null=True)
|
|
| 69a9009e | Jérôme Schneider | event_type = models.ForeignKey(EventType, verbose_name=u"Type d'événement")
|
|
| 76974b6f | Benjamin Dauvergne | creator = models.ForeignKey(User, verbose_name=_(u'Créateur'), blank=True, null=True)
|
|
create_date = models.DateTimeField(_(u'Date de création'), auto_now_add=True)
|
|||
| ba34dc24 | Jérôme Schneider | ||
| 84091f2c | Jérôme Schneider | services = models.ManyToManyField('ressources.Service',
|
|
null=True, blank=True, default=None)
|
|||
| e39efb4a | Jérôme Schneider | participants = models.ManyToManyField('personnes.People',
|
|
null=True, blank=True, default=None)
|
|||
| dd986559 | Serghei MIHAI | ressource = models.ForeignKey('ressources.Ressource', blank=True, null=True,
|
|
verbose_name=u'Ressource')
|
|||
| e39efb4a | Jérôme Schneider | ||
| 5c93596e | Benjamin Dauvergne | start_datetime = models.DateTimeField(_('Début'), db_index=True)
|
|
| 76974b6f | Benjamin Dauvergne | end_datetime = models.DateTimeField(_('Fin'), blank=True, null=True)
|
|
| 74102956 | Benjamin Dauvergne | old_ev_id = models.CharField(max_length=8, blank=True, null=True)
|
|
old_rr_id = models.CharField(max_length=8, blank=True, null=True)
|
|||
| 44482a62 | Benjamin Dauvergne | # only used when there is no rr id
|
|
| 74102956 | Benjamin Dauvergne | old_rs_id = models.CharField(max_length=8, blank=True, null=True)
|
|
| a04356e2 | Benjamin Dauvergne | # exception to is mutually exclusive with recurrence_periodicity
|
|
# an exception cannot be periodic
|
|||
exception_to = models.ForeignKey('self', related_name='exceptions',
|
|||
blank=True, null=True,
|
|||
verbose_name=u'Exception à')
|
|||
exception_date = models.DateField(blank=True, null=True,
|
|||
| e9fb7b88 | Benjamin Dauvergne | verbose_name=u'Reporté du', db_index=True)
|
|
| a04356e2 | Benjamin Dauvergne | # canceled can only be used with exception to
|
|
| e9fb7b88 | Benjamin Dauvergne | canceled = models.BooleanField(_('Annulé'), db_index=True)
|
|
| 76974b6f | Benjamin Dauvergne | ||
PERIODS = (
|
|||
(1, u'Toutes les semaines'),
|
|||
(2, u'Une semaine sur deux'),
|
|||
(3, u'Une semaine sur trois'),
|
|||
(4, 'Une semaine sur quatre'),
|
|||
(5, 'Une semaine sur cinq')
|
|||
)
|
|||
| 3c5df84d | Jérôme Schneider | OFFSET = range(0, 4)
|
|
| 4ce756c8 | Benjamin Dauvergne | PERIODICITIES = (
|
|
(1, u'Toutes les semaines'),
|
|||
(2, u'Une semaine sur deux'),
|
|||
(3, u'Une semaine sur trois'),
|
|||
(4, u'Une semaine sur quatre'),
|
|||
(5, u'Une semaine sur cinq'),
|
|||
(6, u'La première semaine du mois'),
|
|||
(7, u'La deuxième semaine du mois'),
|
|||
(8, u'La troisième semaine du mois'),
|
|||
(9, u'La quatrième semaine du mois'),
|
|||
| 9de67cc5 | Benjamin Dauvergne | (13, u'La cinquième semaine du mois'),
|
|
| 4ce756c8 | Benjamin Dauvergne | (10, u'La dernière semaine du mois'),
|
|
(11, u'Les semaines paires'),
|
|||
(12, u'Les semaines impaires')
|
|||
)
|
|||
WEEK_RANKS = (
|
|||
(0, u'La première semaine du mois'),
|
|||
(1, u'La deuxième semaine du mois'),
|
|||
(2, u'La troisième semaine du mois'),
|
|||
(3, u'La quatrième semaine du mois'),
|
|||
| 9433e992 | Benjamin Dauvergne | (4, u'La cinquième semaine du mois'),
|
|
(-1, u'La dernière semaine du mois'),
|
|||
# (-2, u'L\'avant dernière semaine du mois'),
|
|||
# (-3, u'L\'antépénultième semaine du mois'),
|
|||
# (-4, u'L\'anté-antépénultième semaine du mois'),
|
|||
# (-5, u'L\'anté-anté-antépénultième semaine du mois')
|
|||
| 4ce756c8 | Benjamin Dauvergne | )
|
|
PARITIES = (
|
|||
(0, u'Les semaines paires'),
|
|||
(1, u'Les semaines impaires')
|
|||
)
|
|||
recurrence_periodicity = models.PositiveIntegerField(
|
|||
choices=PERIODICITIES,
|
|||
verbose_name=u"Périodicité",
|
|||
default=None,
|
|||
blank=True,
|
|||
| e9fb7b88 | Benjamin Dauvergne | null=True,
|
|
db_index=True)
|
|||
recurrence_week_day = models.PositiveIntegerField(default=0, db_index=True)
|
|||
| 76974b6f | Benjamin Dauvergne | recurrence_week_offset = models.PositiveIntegerField(
|
|
choices=zip(OFFSET, OFFSET),
|
|||
verbose_name=u"Décalage en semaines par rapport au 1/1/1970 pour le calcul de période",
|
|||
default=0,
|
|||
db_index=True)
|
|||
recurrence_week_period = models.PositiveIntegerField(
|
|||
choices=PERIODS,
|
|||
verbose_name=u"Période en semaines",
|
|||
default=None,
|
|||
blank=True,
|
|||
null=True,
|
|||
db_index=True)
|
|||
| 9433e992 | Benjamin Dauvergne | recurrence_week_rank = models.IntegerField(
|
|
| 4ce756c8 | Benjamin Dauvergne | verbose_name=u"Rang de la semaine dans le mois",
|
|
choices=WEEK_RANKS,
|
|||
| e9fb7b88 | Benjamin Dauvergne | blank=True, null=True, db_index=True)
|
|
| 4ce756c8 | Benjamin Dauvergne | recurrence_week_parity = models.PositiveIntegerField(
|
|
choices=PARITIES,
|
|||
verbose_name=u"Parité des semaines",
|
|||
blank=True,
|
|||
| e9fb7b88 | Benjamin Dauvergne | null=True,
|
|
db_index=True)
|
|||
| 76974b6f | Benjamin Dauvergne | recurrence_end_date = models.DateField(
|
|
verbose_name=_(u'Fin de la récurrence'),
|
|||
blank=True, null=True,
|
|||
db_index=True)
|
|||
| 4ce756c8 | Benjamin Dauvergne | PERIOD_LIST_TO_FIELDS = [(1, None, None),
|
|
(2, None, None),
|
|||
(3, None, None),
|
|||
(4, None, None),
|
|||
(5, None, None),
|
|||
(None, 0, None),
|
|||
(None, 1, None),
|
|||
(None, 2, None),
|
|||
(None, 3, None),
|
|||
| 9de67cc5 | Benjamin Dauvergne | (None, -1, None),
|
|
| 4ce756c8 | Benjamin Dauvergne | (None, None, 0),
|
|
| 9de67cc5 | Benjamin Dauvergne | (None, None, 1),
|
|
(None, 4, None),
|
|||
| 4ce756c8 | Benjamin Dauvergne | ]
|
|
| ba34dc24 | Jérôme Schneider | class Meta:
|
|
| e39efb4a | Jérôme Schneider | verbose_name = u'Evénement'
|
|
verbose_name_plural = u'Evénements'
|
|||
| 76974b6f | Benjamin Dauvergne | ordering = ('start_datetime', 'end_datetime', 'title')
|
|
| a04356e2 | Benjamin Dauvergne | unique_together = (('exception_to', 'exception_date'),)
|
|
| 76974b6f | Benjamin Dauvergne | ||
def __init__(self, *args, **kwargs):
|
|||
if kwargs.get('start_datetime') and not kwargs.has_key('recurrence_end_date'):
|
|||
kwargs['recurrence_end_date'] = kwargs.get('start_datetime').date()
|
|||
super(Event, self).__init__(*args, **kwargs)
|
|||
def clean(self):
|
|||
'''Initialize recurrence fields if they are not.'''
|
|||
| f16de572 | Benjamin Dauvergne | self.sanitize()
|
|
| 4ce756c8 | Benjamin Dauvergne | if self.recurrence_periodicity:
|
|
| f16de572 | Benjamin Dauvergne | if self.recurrence_end_date and self.start_datetime and self.recurrence_end_date < self.start_datetime.date():
|
|
| 4ce756c8 | Benjamin Dauvergne | raise forms.ValidationError(u'La date de fin de périodicité doit être postérieure à la date de début.')
|
|
if self.recurrence_week_parity is not None:
|
|||
if self.start_datetime:
|
|||
week = self.start_datetime.date().isocalendar()[1]
|
|||
start_week_parity = week % 2
|
|||
if start_week_parity != self.recurrence_week_parity:
|
|||
raise forms.ValidationError(u'Le date de départ de la périodicité est en semaine {week}.'.format(week=week))
|
|||
if self.recurrence_week_rank is not None and self.start_datetime:
|
|||
start_week_ranks = weekday_ranks(self.start_datetime.date())
|
|||
if self.recurrence_week_rank not in start_week_ranks:
|
|||
raise forms.ValidationError('La date de début de périodicité doit faire partie de la bonne semaine dans le mois.')
|
|||
| 76974b6f | Benjamin Dauvergne | ||
| f16de572 | Benjamin Dauvergne | def sanitize(self):
|
|
if self.recurrence_periodicity:
|
|||
l = self.PERIOD_LIST_TO_FIELDS[self.recurrence_periodicity-1]
|
|||
else:
|
|||
l = None, None, None
|
|||
self.recurrence_week_period = l[0]
|
|||
self.recurrence_week_rank = l[1]
|
|||
self.recurrence_week_parity = l[2]
|
|||
if self.start_datetime:
|
|||
if self.recurrence_periodicity:
|
|||
self.recurrence_week_day = self.start_datetime.weekday()
|
|||
if self.recurrence_week_period is not None:
|
|||
self.recurrence_week_offset = weeks_since_epoch(self.start_datetime) % self.recurrence_week_period
|
|||
| 76974b6f | Benjamin Dauvergne | def timedelta(self):
|
|
'''Distance between start and end of the event'''
|
|||
return self.end_datetime - self.start_datetime
|
|||
| a04356e2 | Benjamin Dauvergne | def match_date(self, date):
|
|
if self.is_recurring():
|
|||
# consider exceptions
|
|||
exception = self.get_exceptions_dict().get(date)
|
|||
if exception is not None:
|
|||
return exception if exception.match_date(date) else None
|
|||
if self.canceled:
|
|||
return None
|
|||
if date.weekday() != self.recurrence_week_day:
|
|||
return None
|
|||
if self.start_datetime.date() > date:
|
|||
return None
|
|||
if self.recurrence_end_date and self.recurrence_end_date < date:
|
|||
return None
|
|||
if self.recurrence_week_period is not None:
|
|||
if weeks_since_epoch(date) % self.recurrence_week_period != self.recurrence_week_offset:
|
|||
return None
|
|||
elif self.recurrence_week_parity is not None:
|
|||
if date.isocalendar()[1] % 2 != self.recurrence_week_parity:
|
|||
return None
|
|||
elif self.recurrence_week_rank is not None:
|
|||
if self.recurrence_week_rank not in weekday_ranks(date):
|
|||
return None
|
|||
else:
|
|||
raise NotImplemented
|
|||
return self
|
|||
else:
|
|||
| ced57915 | Benjamin Dauvergne | return self if date == self.start_datetime.date() else None
|
|
| a04356e2 | Benjamin Dauvergne | ||
| b8a9f824 | Benjamin Dauvergne | def today_occurrence(self, today=None, match=False, upgrade=True):
|
|
| 76974b6f | Benjamin Dauvergne | '''For a recurring event compute the today 'Event'.
|
|
The computed event is the fake one that you cannot save to the database.
|
|||
| ba34dc24 | Jérôme Schneider | '''
|
|
| 76974b6f | Benjamin Dauvergne | today = today or date.today()
|
|
| a04356e2 | Benjamin Dauvergne | if self.canceled:
|
|
| 76974b6f | Benjamin Dauvergne | return None
|
|
| a04356e2 | Benjamin Dauvergne | if match:
|
|
exception = self.get_exceptions_dict().get(today)
|
|||
| b16fae0e | Benjamin Dauvergne | if exception:
|
|
if exception.start_datetime.date() == today:
|
|||
return exception.today_occurrence(today)
|
|||
else:
|
|||
return None
|
|||
| 4ce756c8 | Benjamin Dauvergne | else:
|
|
| a04356e2 | Benjamin Dauvergne | exception_or_self = self.match_date(today)
|
|
if exception_or_self is None:
|
|||
return None
|
|||
if exception_or_self != self:
|
|||
| 5c2d0ea2 | Benjamin Dauvergne | return exception_or_self.today_occurrence(today)
|
|
| b8a9f824 | Benjamin Dauvergne | if self.event_type_id == 1 and type(self) != EventWithAct and upgrade:
|
|
self = self.eventwithact
|
|||
| 59136255 | Benjamin Dauvergne | if self.recurrence_periodicity is None:
|
|
return self
|
|||
| 76974b6f | Benjamin Dauvergne | start_datetime = datetime.combine(today, self.start_datetime.timetz())
|
|
end_datetime = start_datetime + self.timedelta()
|
|||
event = copy(self)
|
|||
| a04356e2 | Benjamin Dauvergne | event.exception_to = self
|
|
event.exception_date = today
|
|||
| 76974b6f | Benjamin Dauvergne | event.start_datetime = start_datetime
|
|
event.end_datetime = end_datetime
|
|||
| a04356e2 | Benjamin Dauvergne | event.recurrence_periodicity = None
|
|
event.recurrence_week_offset = 0
|
|||
| 76974b6f | Benjamin Dauvergne | event.recurrence_week_period = None
|
|
| a04356e2 | Benjamin Dauvergne | event.recurrence_week_parity = None
|
|
event.recurrence_week_rank = None
|
|||
event.recurrence_end_date = None
|
|||
| 76974b6f | Benjamin Dauvergne | event.parent = self
|
|
| 4ce756c8 | Benjamin Dauvergne | # the returned event is "virtual", it must not be saved
|
|
| a04356e2 | Benjamin Dauvergne | old_save = event.save
|
|
old_participants = list(self.participants.all())
|
|||
| 33522bec | Benjamin Dauvergne | old_services = list(self.services.all())
|
|
| a334f1e6 | Mikaël Ates | def save(*args, **kwargs):
|
|
| a04356e2 | Benjamin Dauvergne | event.id = None
|
|
| 86d19877 | Benjamin Dauvergne | event.event_ptr_id = None
|
|
| a04356e2 | Benjamin Dauvergne | old_save(*args, **kwargs)
|
|
| dbe774de | Benjamin Dauvergne | if hasattr(self, 'exceptions_dict'):
|
|
self.exceptions_dict[event.start_datetime.date()] = event
|
|||
| 33522bec | Benjamin Dauvergne | event.services = old_services
|
|
| a04356e2 | Benjamin Dauvergne | event.participants = old_participants
|
|
| 083e9b50 | Benjamin Dauvergne | event.save = old_save
|
|
| 76974b6f | Benjamin Dauvergne | event.save = save
|
|
return event
|
|||
def next_occurence(self, today=None):
|
|||
'''Returns the next occurence after today.'''
|
|||
today = today or date.today()
|
|||
for occurence in self.all_occurences():
|
|||
if occurence.start_datetime.date() > today:
|
|||
return occurence
|
|||
def is_recurring(self):
|
|||
'''Is this event multiple ?'''
|
|||
| 4ce756c8 | Benjamin Dauvergne | return self.recurrence_periodicity is not None
|
|
| 76974b6f | Benjamin Dauvergne | ||
| a04356e2 | Benjamin Dauvergne | def get_exceptions_dict(self):
|
|
if not hasattr(self, 'exceptions_dict'):
|
|||
self.exceptions_dict = dict()
|
|||
| 508c3726 | Benjamin Dauvergne | if self.exception_to_id is None:
|
|
| b8a9f824 | Benjamin Dauvergne | for exception in self.exceptions.all():
|
|
| 508c3726 | Benjamin Dauvergne | self.exceptions_dict[exception.exception_date] = exception
|
|
| a04356e2 | Benjamin Dauvergne | return self.exceptions_dict
|
|
| 76974b6f | Benjamin Dauvergne | def all_occurences(self, limit=90):
|
|
| 4ce756c8 | Benjamin Dauvergne | '''Returns all occurences of this event as virtual Event objects
|
|
| 76974b6f | Benjamin Dauvergne | ||
limit - compute occurrences until limit days in the future
|
|||
Default is to limit to 90 days.
|
|||
| ba34dc24 | Jérôme Schneider | '''
|
|
| 4ce756c8 | Benjamin Dauvergne | if self.recurrence_periodicity is not None:
|
|
| 76974b6f | Benjamin Dauvergne | day = self.start_datetime.date()
|
|
max_end_date = max(date.today(), self.start_datetime.date()) + timedelta(days=limit)
|
|||
end_date = min(self.recurrence_end_date or max_end_date, max_end_date)
|
|||
| a04356e2 | Benjamin Dauvergne | occurrences = []
|
|
| 4ce756c8 | Benjamin Dauvergne | if self.recurrence_week_period is not None:
|
|
delta = timedelta(days=self.recurrence_week_period*7)
|
|||
while day <= end_date:
|
|||
| 790fd669 | Benjamin Dauvergne | occurrence = self.today_occurrence(day, True)
|
|
| a04356e2 | Benjamin Dauvergne | if occurrence is not None:
|
|
occurrences.append(occurrence)
|
|||
| 4ce756c8 | Benjamin Dauvergne | day += delta
|
|
elif self.recurrence_week_parity is not None:
|
|||
delta = timedelta(days=7)
|
|||
while day <= end_date:
|
|||
if day.isocalendar()[1] % 2 == self.recurrence_week_parity:
|
|||
| 790fd669 | Benjamin Dauvergne | occurrence = self.today_occurrence(day, True)
|
|
| a04356e2 | Benjamin Dauvergne | if occurrence is not None:
|
|
occurrences.append(occurrence)
|
|||
| 4ce756c8 | Benjamin Dauvergne | day += delta
|
|
elif self.recurrence_week_rank is not None:
|
|||
delta = timedelta(days=7)
|
|||
while day <= end_date:
|
|||
if self.recurrence_week_rank in weekday_ranks(day):
|
|||
| 790fd669 | Benjamin Dauvergne | occurrence = self.today_occurrence(day, True)
|
|
| a04356e2 | Benjamin Dauvergne | if occurrence is not None:
|
|
occurrences.append(occurrence)
|
|||
| 4ce756c8 | Benjamin Dauvergne | day += delta
|
|
| a04356e2 | Benjamin Dauvergne | for exception in self.exceptions.all():
|
|
| fcc58fae | Jérôme Schneider | if not exception.canceled:
|
|
if exception.exception_date != exception.start_datetime.date() or exception.exception_date > end_date:
|
|||
occurrences.append(exception.eventwithact if exception.event_type_id == 1 else exception)
|
|||
| a04356e2 | Benjamin Dauvergne | return sorted(occurrences, key=lambda o: o.start_datetime)
|
|
| ba34dc24 | Jérôme Schneider | else:
|
|
| a04356e2 | Benjamin Dauvergne | return [self]
|
|
| ba34dc24 | Jérôme Schneider | ||
| 4ce756c8 | Benjamin Dauvergne | def save(self, *args, **kwargs):
|
|
| 0acdd88a | Benjamin Dauvergne | assert self.recurrence_periodicity is None or self.exception_to is None
|
|
| 12a8ef2b | Benjamin Dauvergne | assert self.exception_to is None or self.exception_to.recurrence_periodicity is not None
|
|
assert self.start_datetime is not None
|
|||
| f16de572 | Benjamin Dauvergne | self.sanitize() # init periodicity fields
|
|
| 4ce756c8 | Benjamin Dauvergne | super(Event, self).save(*args, **kwargs)
|
|
| 5f3ce603 | Benjamin Dauvergne | self.acts_cleaning()
|
|
| 4ce756c8 | Benjamin Dauvergne | ||
| fcd786f5 | Benjamin Dauvergne | def delete(self, *args, **kwargs):
|
|
| a04356e2 | Benjamin Dauvergne | self.canceled = True
|
|
| 5f3ce603 | Benjamin Dauvergne | # save will clean acts
|
|
self.save(*args, **kwargs)
|
|||
def acts_cleaning(self):
|
|||
# list of occurences may have changed
|
|||
from ..actes.models import Act
|
|||
if self.exception_to:
|
|||
# maybe a new exception, so look for parent acts with same date
|
|||
# as exception date
|
|||
acts = Act.objects.filter(models.Q(parent_event=self)
|
|||
|models.Q(parent_event=self.exception_to,
|
|||
date=self.exception_date))
|
|||
else:
|
|||
acts = Act.objects.filter(parent_event=self)
|
|||
acts = acts.prefetch_related('actvalidationstate_set')
|
|||
if acts:
|
|||
eventwithact = self.eventwithact
|
|||
for act in acts:
|
|||
| 1d10b2c6 | Benjamin Dauvergne | if act.is_billed:
|
|
pass
|
|||
occurrence = eventwithact.today_occurrence(act.date)
|
|||
if occurrence:
|
|||
occurrence.update_act(act)
|
|||
else:
|
|||
act.delete()
|
|||
| fcd786f5 | Benjamin Dauvergne | ||
| 76974b6f | Benjamin Dauvergne | def to_interval(self):
|
|
return Interval(self.start_datetime, self.end_datetime)
|
|||
| ba34dc24 | Jérôme Schneider | ||
| 76974b6f | Benjamin Dauvergne | def is_event_absence(self):
|
|
return False
|
|||
| ba34dc24 | Jérôme Schneider | ||
| bba85766 | Serghei MIHAI | def get_inactive_participants(self):
|
|
return self.participants.filter(worker__enabled=False)
|
|||
| e6b125bd | Mikaël Ates | def get_missing_participants(self):
|
|
missing_participants = []
|
|||
for participant in self.participants.all():
|
|||
holidays = None
|
|||
worker = participant.worker
|
|||
holidays = Holiday.objects.for_worker(worker) \
|
|||
.for_timed_period(self.start_datetime.date(), self.start_datetime.time(), self.end_datetime.time())
|
|||
if holidays:
|
|||
missing_participants.append(participant)
|
|||
return missing_participants
|
|||
| 3e9b47e9 | Benjamin Dauvergne | RECURRENCE_DESCRIPTION = [
|
|
u'Tous les %s', #(1, None, None),
|
|||
u'Un %s sur deux', #(2, None, None),
|
|||
u'Un %s sur trois', #(3, None, None),
|
|||
u'Un %s sur quatre', #(4, None, None),
|
|||
u'Un %s sur cinq', #(5, None, None),
|
|||
u'Le premier %s du mois', #(None, 0, None),
|
|||
u'Le deuxième %s du mois', #(None, 1, None),
|
|||
u'Le troisième %s du mois', #(None, 2, None),
|
|||
u'Le quatrième %s du mois', #(None, 3, None),
|
|||
u'Le dernier %s du mois', #(None, 4, None),
|
|||
u'Les %s les semaines paires', #(None, None, 0),
|
|||
u'Les %s les semaines impaires', #(None, None, 1)
|
|||
]
|
|||
WEEKDAY_DESRIPTION = [
|
|||
u'lundi',
|
|||
u'mardi',
|
|||
u'mercredi',
|
|||
u'jeudi',
|
|||
u'vendredi',
|
|||
u'samedi',
|
|||
u'dimanche'
|
|||
]
|
|||
def recurrence_description(self):
|
|||
'''Self description of this recurring event'''
|
|||
if not self.recurrence_periodicity:
|
|||
return None
|
|||
parts = []
|
|||
| 9ccdb2fe | Benjamin Dauvergne | parts.append(self.RECURRENCE_DESCRIPTION[self.recurrence_periodicity-1] \
|
|
| 3e9b47e9 | Benjamin Dauvergne | % self.WEEKDAY_DESRIPTION[self.recurrence_week_day])
|
|
if self.recurrence_end_date:
|
|||
parts.append(u'du')
|
|||
else:
|
|||
parts.append(u'à partir du')
|
|||
parts.append(self.start_datetime.strftime('%d/%m/%Y'))
|
|||
if self.recurrence_end_date:
|
|||
parts.append(u'au')
|
|||
parts.append(self.recurrence_end_date.strftime('%d/%m/%Y'))
|
|||
return u' '.join(parts)
|
|||
| 7a2fc3bb | Serghei MIHAI | def is_absent(self):
|
|
try:
|
|||
return self.eventwithact.is_absent()
|
|||
except self.DoesNotExist:
|
|||
return False
|
|||
| ba34dc24 | Jérôme Schneider | def __unicode__(self):
|
|
| 76974b6f | Benjamin Dauvergne | return self.title
|
|
| ba34dc24 | Jérôme Schneider | ||
| 6190a8ce | Benjamin Dauvergne | def __repr__(self):
|
|
return '<Event: on {start_datetime} with {participants}'.format(
|
|||
start_datetime=self.start_datetime,
|
|||
participants=self.participants.all() if self.id else '<un-saved>')
|
|||
| ba34dc24 | Jérôme Schneider | ||
| 76974b6f | Benjamin Dauvergne | class EventWithActManager(managers.EventManager):
|
|
def create_patient_appointment(self, creator, title, patient,
|
|||
doctors=[], act_type=None, service=None, start_datetime=None, end_datetime=None,
|
|||
| dd986559 | Serghei MIHAI | ressource=None, periodicity=1, until=False):
|
|
| 76974b6f | Benjamin Dauvergne | appointment = self.create_event(creator=creator,
|
|
title=title,
|
|||
event_type=EventType(id=1),
|
|||
participants=doctors,
|
|||
services=[service],
|
|||
start_datetime=start_datetime,
|
|||
end_datetime=end_datetime,
|
|||
| dd986559 | Serghei MIHAI | ressource=ressource,
|
|
| 76974b6f | Benjamin Dauvergne | periodicity=periodicity,
|
|
until=until,
|
|||
act_type=act_type,
|
|||
patient=patient)
|
|||
return appointment
|
|||
class EventWithAct(Event):
|
|||
'''An event corresponding to an act.'''
|
|||
objects = EventWithActManager()
|
|||
act_type = models.ForeignKey('ressources.ActType',
|
|||
verbose_name=u'Type d\'acte')
|
|||
patient = models.ForeignKey('dossiers.PatientRecord')
|
|||
| ddcbee95 | Benjamin Dauvergne | convocation_sent = models.BooleanField(blank=True,
|
|
| e9fb7b88 | Benjamin Dauvergne | verbose_name=u'Convoqué', db_index=True)
|
|
| ba34dc24 | Jérôme Schneider | ||
| 5c2d0ea2 | Benjamin Dauvergne | ||
| ba34dc24 | Jérôme Schneider | @property
|
|
| 76974b6f | Benjamin Dauvergne | def act(self):
|
|
| 6b0ae820 | Benjamin Dauvergne | for act in self.act_set.all():
|
|
if act.date == self.start_datetime.date():
|
|||
return act
|
|||
| 433b67bb | Benjamin Dauvergne | return self.build_act()
|
|
| 76974b6f | Benjamin Dauvergne | ||
| 433b67bb | Benjamin Dauvergne | def get_state(self):
|
|
act = self.act
|
|||
if act.id:
|
|||
return act.get_state()
|
|||
return None
|
|||
| 4b5bbcb1 | Mikaël Ates | def is_absent(self):
|
|
act = self.act
|
|||
if act.id:
|
|||
return act.is_absent()
|
|||
return False
|
|||
| 433b67bb | Benjamin Dauvergne | def build_act(self):
|
|
| 76974b6f | Benjamin Dauvergne | from ..actes.models import Act, ActValidationState
|
|
| 433b67bb | Benjamin Dauvergne | act = Act()
|
|
self.init_act(act)
|
|||
old_save = act.save
|
|||
def save(*args, **kwargs):
|
|||
| 8fa0047b | Benjamin Dauvergne | act.save = old_save
|
|
| 433b67bb | Benjamin Dauvergne | old_save(*args, **kwargs)
|
|
| 5534cf1d | Jérôme Schneider | act.comment = self.description
|
|
| d3cfbd19 | Serghei MIHAI | act.doctors = (participant.worker for participant in self.participants.all())
|
|
| 981abc28 | Jérôme Schneider | last_validation_state = ActValidationState.objects.create(
|
|
act=act, state_name='NON_VALIDE',
|
|||
author=self.creator, previous_state=None)
|
|||
act.last_validation_state = last_validation_state
|
|||
| 32551648 | Jérôme Schneider | old_save(*args, **kwargs)
|
|
| 433b67bb | Benjamin Dauvergne | act.save = save
|
|
| 76974b6f | Benjamin Dauvergne | return act
|
|
def update_act(self, act):
|
|||
'''Update an act to match details of the meeting'''
|
|||
| 433b67bb | Benjamin Dauvergne | self.init_act(act)
|
|
act.save()
|
|||
def init_act(self, act):
|
|||
| 76974b6f | Benjamin Dauvergne | delta = self.timedelta()
|
|
duration = delta.seconds // 60
|
|||
act._duration = duration
|
|||
act.act_type = self.act_type
|
|||
act.patient = self.patient
|
|||
| 046a69ee | Benjamin Dauvergne | act.parent_event = self
|
|
| 76974b6f | Benjamin Dauvergne | act.date = self.start_datetime.date()
|
|
| dfea3c5e | Benjamin Dauvergne | act.time = self.start_datetime.time()
|
|
| 76974b6f | Benjamin Dauvergne | ||
def save(self, *args, **kwargs):
|
|||
'''Force event_type to be patient meeting.'''
|
|||
self.event_type = EventType(id=1)
|
|||
super(EventWithAct, self).save(*args, **kwargs)
|
|||
| ba0e06ef | Benjamin Dauvergne | ||
| 5f4c5dd2 | Frédéric Péters | def is_event_absence(self):
|
|
| 76974b6f | Benjamin Dauvergne | return self.act.is_absent()
|
|
def __unicode__(self):
|
|||
kwargs = {
|
|||
'patient': self.patient,
|
|||
'start_datetime': self.start_datetime,
|
|||
'act_type': self.act_type
|
|||
}
|
|||
kwargs['doctors'] = ', '.join(map(unicode, self.participants.all())) if self.id else ''
|
|||
return u'Rdv le {start_datetime} de {patient} avec {doctors} pour ' \
|
|||
'{act_type} ({act_type.id})'.format(**kwargs)
|
|||
| 6eff18f3 | Benjamin Dauvergne | ||
from django.db.models.signals import m2m_changed
|
|||
from django.dispatch import receiver
|
|||
@receiver(m2m_changed, sender=Event.participants.through)
|
|||
def participants_changed(sender, instance, action, **kwargs):
|
|||
if action.startswith('post'):
|
|||
workers = [ p.worker for p in instance.participants.prefetch_related('worker') ]
|
|||
for act in instance.act_set.all():
|
|||
act.doctors = workers
|