diff --git a/src/khana/views.py b/src/khana/views.py index 4c1e062..9e482df 100644 --- a/src/khana/views.py +++ b/src/khana/views.py @@ -81,23 +81,8 @@ def __getEventInfo(request): .. todo:: il refuse mon 'filter' que ce soit avant ou après le 'next(5)'. Une idée ? next_event = Event.objects.filter(club__in=(request.session["clubid"], )).next(5) """ - - # rest = 0 - # counted = 0 - # event_list = [] - # today = pendulum.now().date() next_event_list = Event.objects.next(5) - # for event in next_event: - # counted = event.get_number_of_occurence_to_event(today) - # # print('pouf !') - - # unavailabilities = Unavailability.objects.filter(datebegin__lte=event.datebegin.date(), dateend__gte=today) - # for unavailable in unavailabilities: - # counted -= unavailable.get_total_occurence() - - # event_list.append((event, counted, int((counted/16)*100))) - return next_event_list @@ -130,18 +115,9 @@ def __getCourseInfo(request): else: rest = int((tmp.days + 1) / 7) - # # select tous les unavailables liés au cours - # unavailabilities = Unavailability.objects.filter(course=course) - # for unavailable in unavailabilities: - # tmp = unavailable.get_total_occurence() - # counted -= tmp - # rest -= tmp # si un unavailability.date < today, on soustrait quand même de rest ??? Si oui => BUG !!!! - courses_left += rest courses_done += counted - rest - # courses.append((course, counted, (counted - rest))) - return courses, courses_done, courses_left diff --git a/src/location/views.py b/src/location/views.py index b01a832..5f4ff13 100644 --- a/src/location/views.py +++ b/src/location/views.py @@ -170,10 +170,14 @@ class GymnastStatistics(): @login_required def club_statistics(request, clubid): - """ - Renvoie les statistiques d'un club pour une saison choisie. + """Construit les statistiques d'un club, pour une saison choisie - .. todo:: tenir compte de la saison. + Questions: + Tu dis que cela construit les stats d'un club __pour une saison__... + Mais je ne vois pas la saison dans les paramètres... + + Todo: + * Tenir compte de la saison. """ courses = Course.objects.filter(club__in=clubid).order_by( @@ -189,13 +193,12 @@ def club_statistics(request, clubid): courseList = [] for course in courses: - nbtrainer = course.trainers.count() + number_of_trainers = course.trainers.count() list_of_gymnasts = Gymnast.objects.filter(to_gym__in=course.to_subgroup.all()) gymnasts.extend(list_of_gymnasts) - nbgymnast = len(list_of_gymnasts) - # gymnasts = set(gymnasts.extend(Gymnast.objects.filter(to_gym__in=course.to_subgroup.all()))) + number_of_gymnasts = len(list_of_gymnasts) - nbhour = __diffTime(course.hour_end, course.hour_begin) # timedelta + nbhour = __diffTime(course.hour_end, course.hour_begin) totalHoursByWeek += nbhour.seconds counted = course.get_total_occurence() @@ -211,7 +214,7 @@ def club_statistics(request, clubid): totalTimeForCourse.seconds / 3600 ) totalHours += totalHourForCourse - totalHoursPaidForCourse = totalHourForCourse * nbtrainer + totalHoursPaidForCourse = totalHourForCourse * number_of_trainers totalHoursPaid += totalHoursPaidForCourse # tmp = int(nbhour.seconds/3600) @@ -221,8 +224,8 @@ def club_statistics(request, clubid): courseList.append( ( course, - nbtrainer, - nbgymnast, + number_of_trainers, + number_of_gymnasts, hour, counted, totalHourForCourse, diff --git a/src/planning/models.py b/src/planning/models.py index 393a8f4..68ff806 100644 --- a/src/planning/models.py +++ b/src/planning/models.py @@ -13,496 +13,500 @@ from people.models import Gymnast def get_week(a_date): - """ + """ - Remarks: - Je ne comprends pas trop cette fonction... - Tu pars d'une date, et tu récupères le lundi et le samedi de la semaine correspondant ? - """ - the_date = pendulum.parse(a_date) - day = the_date.weekday() - monday = the_date - timedelta(days=day) - sunday = the_date + timedelta(days=(6 - day)) - return monday, sunday + Remarks: + Je ne comprends pas trop cette fonction... + Tu pars d'une date, et tu récupères le lundi et le samedi de la semaine correspondant ? + """ + the_date = pendulum.parse(a_date) + day = the_date.weekday() + monday = the_date - timedelta(days=day) + sunday = the_date + timedelta(days=(6 - day)) + return monday, sunday def get_number_of_weeks_between(start, stop): - """ - Renvoie le nombre de semaines entre deux dates. - Par extension, cela permet de connaitre le nombre d'occurence d'un - évènement (entraînement, par exemple) hebdromadaire entre deux dates - et ainsi pouvoir plannifier. + """ + Renvoie le nombre de semaines entre deux dates. + Par extension, cela permet de connaitre le nombre d'occurence d'un + évènement (entraînement, par exemple) hebdromadaire entre deux dates + et ainsi pouvoir plannifier. - :param start: date de début de la période - :type start: datetime.date - :param stop: date de fin de la période - :type stop: datetime.date - :return: Le nombre de semaines entre les deux dates. + :param start: date de début de la période + :type start: datetime.date + :param stop: date de fin de la période + :type stop: datetime.date + :return: Le nombre de semaines entre les deux dates. - Remarks: - Proposition d'utiliser isocalendar() sur une date. - L'indice 1 de la valeur de retour donne la semaine correspondant. + Remarks: + Proposition d'utiliser isocalendar() sur une date. + L'indice 1 de la valeur de retour donne la semaine correspondant. - Eg. - >>> from datetime import date - >>> d = date(2020, 9, 27) - >>> d.isocalendar() - (2020, 39, 7) + Eg. + >>> from datetime import date + >>> d = date(2020, 9, 27) + >>> d.isocalendar() + (2020, 39, 7) - -> Est-ce qu'il ne suffirait pas de faire la différence ? - """ + -> Est-ce qu'il ne suffirait pas de faire la différence ? + """ - tmp = stop - start - number_of_days = abs(tmp.days) - number_of_week = int((number_of_days + 1) / 7) + tmp = stop - start + number_of_days = abs(tmp.days) + number_of_week = int((number_of_days + 1) / 7) - if ((number_of_days + 1) % 7) > 0: - number_of_week += 1 + if ((number_of_days + 1) % 7) > 0: + number_of_week += 1 - if tmp.days < 0: - number_of_week *= -1 + if tmp.days < 0: + number_of_week *= -1 - return number_of_week + return number_of_week class TemporizableQuerySet(models.QuerySet): - """ - Classe permettant de spécifier le `QuerySet` de la classe `Temporizable`. - """ + """ + Classe permettant de spécifier le `QuerySet` de la classe `Temporizable`. + """ - def next(self, limit): - """ - Renvoie la liste des prochains "temporizable" (par rapport à la date du jour). + def next(self, limit): + """ + Renvoie la liste des prochains "temporizable" (par rapport à la date du jour). - :param limit: nombre d'éléments désirés. - :type limit: int - :return: une liste de `limit` éléments temporizables. - """ - return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:limit] + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables. + """ + return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:limit] - def last(self, limit): - """ - Renvoie la liste des derniers "temporizable" (par rapport à la date du jour). + def last(self, limit): + """ + Renvoie la liste des derniers "temporizable" (par rapport à la date du jour). - :param limit: nombre d'éléments désirés. - :type limit: int - :return: une liste de `limit` éléments temporizables - """ - return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:limit] + :param limit: nombre d'éléments désirés. + :type limit: int + :return: une liste de `limit` éléments temporizables + """ + return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:limit] - # def get(self, date_string): - # """ - # """ - # try: - # selected_object = self.get(datebegin__lte=date_string, dateend__gte=date_string) - # except self.DoesNotExist: - # return None - # except self.MultipleObjectsReturned: - # return None + # def get(self, date_string): + # """ + # """ + # try: + # selected_object = self.get(datebegin__lte=date_string, dateend__gte=date_string) + # except self.DoesNotExist: + # return None + # except self.MultipleObjectsReturned: + # return None - # return selected_object + # return selected_object class Temporizable(models.Model): - """Classe abstraite définissant une période comprise entre deux dates. + """Classe abstraite définissant une période comprise entre deux dates. - """ + """ - class Meta: - abstract = True + class Meta: + abstract = True - datebegin = models.DateTimeField(verbose_name="Début") - dateend = models.DateTimeField(blank=True, verbose_name="Fin") + datebegin = models.DateTimeField(verbose_name="Début") + dateend = models.DateTimeField(blank=True, verbose_name="Fin") - objects = models.Manager.from_queryset(TemporizableQuerySet)() + objects = models.Manager.from_queryset(TemporizableQuerySet)() - def get_total_occurence(self): - """ - Renvoie le nombre de semaines entre les deux dates d'une instance de la - classe `Temporizable`. + def get_total_occurence(self): + """ + Renvoie le nombre de semaines entre les deux dates d'une instance de la + classe `Temporizable`. - :return: nombre de semaines. - """ - return get_number_of_weeks_between(self.datebegin.date(), self.dateend.date()) + :return: nombre de semaines. + """ + return get_number_of_weeks_between(self.datebegin.date(), self.dateend.date()) - def get_number_of_occurence_to_event(self, the_date): - """ - Renvoie le nombre semaines entre une date choisie et le début - (datebegin) d'une instance de la classe `Temporizable`. + def get_number_of_occurence_to_event(self, the_date): + """ + Renvoie le nombre semaines entre une date choisie et le début + (datebegin) d'une instance de la classe `Temporizable`. - :param the_date: date par rapport à laquelle le calcul sera fait. - :type the_date: datetime.date - :return: nombre de semaines. - """ - return get_number_of_weeks_between(the_date, self.datebegin.date()) + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :return: nombre de semaines. + """ + return get_number_of_weeks_between(the_date, self.datebegin.date()) - def get_number_of_occurence_inbetween(self, the_date, rest=True): - """ - Renvoie le nombre semaines entre une date choisie et une instance de la - classe `Temporizable`. Le calcul peut se faire soit entre la date - choisie et le date de fin d'une occurence de la classe, soit entre la - date de début d'une occurence de la classe et la date choisie. + def get_number_of_occurence_inbetween(self, the_date, rest=True): + """ + Renvoie le nombre semaines entre une date choisie et une instance de la + classe `Temporizable`. Le calcul peut se faire soit entre la date + choisie et le date de fin d'une occurence de la classe, soit entre la + date de début d'une occurence de la classe et la date choisie. - :param the_date: date par rapport à laquelle le calcul sera fait. - :type the_date: datetime.date - :param rest: paramètre définissant s'il faut calculer le reste des - occurences à venir (depuis `the_date` jusqu'à la date de fin) ou - les occurences déjà passées (depuis la date de début jusqu'à - `the_date`) - :type rest: booléen - :return: nombre de semaines. - """ - if rest: - return get_number_of_weeks_between(the_date, self.dateend.date()) - else: - return get_number_of_weeks_between(self.datebegin.date(), the_date) + :param the_date: date par rapport à laquelle le calcul sera fait. + :type the_date: datetime.date + :param rest: paramètre définissant s'il faut calculer le reste des + occurences à venir (depuis `the_date` jusqu'à la date de fin) ou + les occurences déjà passées (depuis la date de début jusqu'à + `the_date`) + :type rest: booléen + :return: nombre de semaines. + """ + if rest: + return get_number_of_weeks_between(the_date, self.dateend.date()) + else: + return get_number_of_weeks_between(self.datebegin.date(), the_date) class Season(Temporizable): - """ - Classe représentant une saison. Une saison est déinie par : - - un id, - - un label, - - une date de début et - - une date de fin. + """ + Classe représentant une saison. Une saison est déinie par : + - un id, + - un label, + - une date de début et + - une date de fin. - La date de début est très souvent le : 01/09/xxxx - La date de fin est très souvent le : 31/08/xxxy - exemple : 1/9/2015 - 31/8/2016 - """ + La date de début est très souvent le : 01/09/xxxx + La date de fin est très souvent le : 31/08/xxxy + exemple : 1/9/2015 - 31/8/2016 + """ - class Meta: - verbose_name = "Season" - verbose_name_plural = "Seasons" + class Meta: + verbose_name = "Season" + verbose_name_plural = "Seasons" - label = models.CharField(max_length=11, verbose_name="Label") - # active ou default = models.BooleanField(verbose_name='Défaut') + label = models.CharField(max_length=11, verbose_name="Label") + # active ou default = models.BooleanField(verbose_name='Défaut') - def __str__(self): - return "%s" % (self.label) + def __str__(self): + return "%s" % (self.label) - def week_number_from_begin(self, target_date): - return get_number_of_weeks_between(self.datebegin.date(), target_date) + def week_number_from_begin(self, target_date): + return get_number_of_weeks_between(self.datebegin.date(), target_date) class EventType(models.Model): - """ - Classe représentant les types d'évènements. - C'est un dictionnaire fini : - - compétiton qualificative, - - compétition finale, - - démonstration, - - … - """ + """ + Classe représentant les types d'évènements. + C'est un dictionnaire fini : + - compétiton qualificative, + - compétition finale, + - démonstration, + - … + """ - class Meta: - verbose_name = "Event Type" - verbose_name_plural = "Event Types" + class Meta: + verbose_name = "Event Type" + verbose_name_plural = "Event Types" - name = models.CharField(max_length=255, verbose_name="Nom") - acronym = models.CharField(max_length=5, verbose_name="Acronyme") + name = models.CharField(max_length=255, verbose_name="Nom") + acronym = models.CharField(max_length=5, verbose_name="Acronyme") - def __str__(self): - return "%s (%s)" % (self.name, self.acronym) + def __str__(self): + return "%s (%s)" % (self.name, self.acronym) class Event(Markdownizable, Temporizable): - """Classe représentant les évènements. + """Classe représentant les évènements. - Un évènement est caractérisé par : - * un nom, - * un lieu (place), - * un type (compétition, démonstration, …), - * des gymnastes (participation prévue). - Je ne me rapelle plus à quoi sert le club. - """ + Un évènement est caractérisé par : + * un nom, + * un lieu (place), + * un type (compétition, démonstration, …), + * des gymnastes (participation prévue). + Je ne me rapelle plus à quoi sert le club. + """ - class Meta: - verbose_name = "Event" - verbose_name_plural = "Event" + class Meta: + verbose_name = "Event" + verbose_name_plural = "Event" - place = models.ForeignKey( - "location.Place", verbose_name="Lieu", on_delete=models.CASCADE, default=None - ) - eventtype = models.ForeignKey( - EventType, verbose_name="Type", on_delete=models.CASCADE, default=None - ) - name = models.CharField(max_length=255, verbose_name="Nom") - # club = models.ManyToManyField('location.Club', related_name="concernate_by", blank=True) - gymnasts = models.ManyToManyField( - "people.Gymnast", - through="Event_Participation", - related_name="participate_to", - verbose_name="Participants", - ) + place = models.ForeignKey( + "location.Place", verbose_name="Lieu", on_delete=models.CASCADE, default=None + ) + eventtype = models.ForeignKey( + EventType, verbose_name="Type", on_delete=models.CASCADE, default=None + ) + name = models.CharField(max_length=255, verbose_name="Nom") + # club = models.ManyToManyField('location.Club', related_name="concernate_by", blank=True) + gymnasts = models.ManyToManyField( + "people.Gymnast", + through="Event_Participation", + related_name="participate_to", + verbose_name="Participants", + ) - def __str__(self): - return "%s (à %s)" % (self.name, self.place.city) + def __str__(self): + return "%s (à %s)" % (self.name, self.place.city) - def save(self, *args, **kwargs): - self.checkdates() - super().save(*args, **kwargs) + def save(self, *args, **kwargs): + self.checkdates() + super().save(*args, **kwargs) - def checkdates(self): - """ - Fonction assignant la date de fin d'un évènement à la date de début, si la date - de fin n'est pas définie, l'heure de fin est par défaut 18h00. - """ - if self.dateend is None and self.datebegin is not None: - self.dateend = datetime.combine(self.datebegin.date(), time(18, 0)) + def checkdates(self): + """ + Fonction assignant la date de fin d'un évènement à la date de début, si la date + de fin n'est pas définie, l'heure de fin est par défaut 18h00. + """ + if self.dateend is None and self.datebegin is not None: + self.dateend = datetime.combine(self.datebegin.date(), time(18, 0)) - @property - def number_of_week_from_today(self): - today = pendulum.now().date() - return get_number_of_weeks_between(today, self.datebegin.date()) + @property + def number_of_week_from_today(self): + today = pendulum.now().date() + return get_number_of_weeks_between(today, self.datebegin.date()) class Event_Participation(models.Model): - """ - """ + """ + """ - event = models.ForeignKey(Event, on_delete=models.CASCADE) - gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) - rank = models.PositiveSmallIntegerField(default=0) + event = models.ForeignKey(Event, on_delete=models.CASCADE) + gymnast = models.ForeignKey("people.Gymnast", on_delete=models.CASCADE) + rank = models.PositiveSmallIntegerField(default=0) class Course(Markdownizable, Temporizable): - """Classe représentant les cours. + """Classe représentant les cours. - Un cours est défini par : - * une heure de début et une heure de fin, - * une date de début et une date de fin (un cours est considéré comme donné hebdromadairement entre - ces deux dates) (hérite de la classe `Temporizable`) - * est associé à un ou plusieurs entraineurs, - * est associé à un club - * est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche). - """ + Un cours est défini par : + * une heure de début et une heure de fin, + * une date de début et une date de fin (un cours est considéré comme donné hebdromadairement entre + ces deux dates) (hérite de la classe `Temporizable`) + * est associé à un ou plusieurs entraineurs, + * est associé à un club + * est associé à un jour de la semaine (numéro du jour dans la semaine : 0 = lundi, 6 = dimanche). + """ - class Meta: - verbose_name = "Course" - verbose_name_plural = "Courses" + class Meta: + verbose_name = "Cours" + verbose_name_plural = "Cours" - DAY_CHOICE = ( - (1, "Lundi"), - (2, "Mardi"), - (3, "Mercredi"), - (4, "Jeudi"), - (5, "Vendredi"), - (6, "Samedi"), - (7, "Dimanche"), - ) + DAY_CHOICE = ( + (1, "Lundi"), + (2, "Mardi"), + (3, "Mercredi"), + (4, "Jeudi"), + (5, "Vendredi"), + (6, "Samedi"), + (7, "Dimanche"), + ) - club = models.ForeignKey( - "location.Club", verbose_name="Club", on_delete=models.CASCADE, default=None - ) - iso_day_number = models.PositiveSmallIntegerField( - choices=DAY_CHOICE, verbose_name="Jour" - ) - hour_begin = models.TimeField(verbose_name="Heure de début") - hour_end = models.TimeField(verbose_name="Heure de fin") - season = models.ForeignKey(Season, on_delete=models.SET_NULL, null=True) - trainers = models.ManyToManyField( - User, verbose_name="Coach(es)", related_name="trainee" - ) - gymnasts = models.ManyToManyField( - Gymnast, verbose_name="Gymnasts", related_name="courses" - ) + club = models.ForeignKey( + "location.Club", verbose_name="Club", on_delete=models.CASCADE, default=None + ) + iso_day_number = models.PositiveSmallIntegerField( + choices=DAY_CHOICE, verbose_name="Jour" + ) + hour_begin = models.TimeField(verbose_name="Heure de début") + hour_end = models.TimeField(verbose_name="Heure de fin") + season = models.ForeignKey(Season, on_delete=models.SET_NULL, null=True) + trainers = models.ManyToManyField( + User, verbose_name="Coach(es)", related_name="trainee" + ) + gymnasts = models.ManyToManyField( + Gymnast, verbose_name="Gymnasts", related_name="courses" + ) - def __str__(self): - return "%s (%s à %s)" % ( - self.get_iso_day_number_display(), - self.hour_begin.strftime("%H:%M"), - self.hour_end.strftime("%H:%M"), - ) + def __str__(self): + return "%s (%s à %s)" % ( + self.get_iso_day_number_display(), + self.hour_begin.strftime("%H:%M"), + self.hour_end.strftime("%H:%M"), + ) - @property - def duration(self): - """ - Renvoie la durée d'un cours en heures - """ - date_begin = pendulum.datetime( - 2000, 1, 1, self.hour_begin.hour, self.hour_begin.minute - ) - date_end = pendulum.datetime( - 2000, 1, 1, self.hour_end.hour, self.hour_end.minute - ) - return date_end.diff(date_begin).in_hours() + @property + def duration(self): + """Renvoie la durée d'un cours en heures + + Examples: + >>> course = Course(hour_begin=20, hour_end=22) + >>> course.duration + 2 + """ + date_begin = pendulum.datetime( + 2000, 1, 1, self.hour_begin.hour, self.hour_begin.minute + ) + date_end = pendulum.datetime( + 2000, 1, 1, self.hour_end.hour, self.hour_end.minute + ) + return date_end.diff(date_begin).in_hours() class Group(models.Model): - """Classe représentant les groupes (Loisir, D1, D2, A, B, …). + """Classe représentant les groupes (Loisir, D1, D2, A, B, …). - Un groupe appartient à un club. - """ + Un groupe appartient à un club. + """ - class Meta: - verbose_name = "Group" - verbose_name_plural = "Groups" + class Meta: + verbose_name = "Group" + verbose_name_plural = "Groups" - club = models.ForeignKey("location.Club", on_delete=models.CASCADE, default=None) - name = models.CharField(max_length=255) - acronym = models.CharField(max_length=50) - active = models.BooleanField(default=1) - season = models.CharField( - max_length=9, - default=str(timezone.now().year) + "-" + str(timezone.now().year + 1), - ) + club = models.ForeignKey("location.Club", on_delete=models.CASCADE, default=None) + name = models.CharField(max_length=255) + acronym = models.CharField(max_length=50) + active = models.BooleanField(default=1) + season = models.CharField( + max_length=9, + default=str(timezone.now().year) + "-" + str(timezone.now().year + 1), + ) - def __str__(self): - return "%s (%s)" % (self.name, self.acronym) + def __str__(self): + return "%s (%s)" % (self.name, self.acronym) class Subgroup(models.Model): - """Classe représentant les sous-groupes. + """Classe représentant les sous-groupes. - Un sous-groupe appartient à un groupe (lui-même lié à un club). + Un sous-groupe appartient à un groupe (lui-même lié à un club). - De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe, - nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison. - """ + De cette manière, quand un gymnaste est mis dans un sous-groupe, en remontant via le groupe, + nous pouvons connaître le(s) club(s) du gymnaste pour chaque saison. + """ - class Meta: - verbose_name = "Subgroup" - verbose_name_plural = "Subgroups" + class Meta: + verbose_name = "Subgroup" + verbose_name_plural = "Subgroups" - name = models.CharField(max_length=255) - acronym = models.CharField(max_length=50) - group = models.ForeignKey(Group, on_delete=models.CASCADE, default=None) - courses = models.ManyToManyField(Course, related_name="to_subgroup") - gymnasts = models.ManyToManyField( - "people.Gymnast", related_name="to_gym", blank=True - ) - active = models.BooleanField(default=1) + name = models.CharField(max_length=255) + acronym = models.CharField(max_length=50) + group = models.ForeignKey(Group, on_delete=models.CASCADE, default=None) + courses = models.ManyToManyField(Course, related_name="to_subgroup") + gymnasts = models.ManyToManyField( + "people.Gymnast", related_name="to_gym", blank=True + ) + active = models.BooleanField(default=1) - def __str__(self): - return "%s (%s)" % (self.name, self.group.name) + def __str__(self): + return "%s (%s)" % (self.name, self.group.name) class UnavailabilityManager(models.Manager): - """Classe représentant le manager de la classe `Unavailability`. - """ + """Classe représentant le manager de la classe `Unavailability`. + """ - def next(self, count): - return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:count] + def next(self, count): + return self.filter(datebegin__gte=timezone.now()).order_by("datebegin")[0:count] - def last(self, count): - return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:count] + def last(self, count): + return self.filter(dateend__lte=timezone.now()).order_by("-dateend")[0:count] class Unavailability(Markdownizable, Temporizable): - """Classe représentant les indisponibilités. - """ + """Classe représentant les indisponibilités. + """ - class Meta: - verbose_name = "Indisponibilité" - verbose_name_plural = "Indisponibilités" + class Meta: + verbose_name = "Indisponibilité" + verbose_name_plural = "Indisponibilités" - course = models.ManyToManyField(Course, related_name="unavailability") + course = models.ManyToManyField(Course, related_name="unavailability") - objects = UnavailabilityManager() + objects = UnavailabilityManager() - def __str__(self): - return "du %s au %s" % (self.datebegin, self.dateend) + def __str__(self): + return "du %s au %s" % (self.datebegin, self.dateend) - def save(self, *args, **kwargs): - self.checkdates() + def save(self, *args, **kwargs): + self.checkdates() - super().save(*args, **kwargs) + super().save(*args, **kwargs) - def checkdates(self): - if self.dateend is None and self.datebegin is not None: - self.dateend = self.datebegin + def checkdates(self): + if self.dateend is None and self.datebegin is not None: + self.dateend = self.datebegin class Training(models.Model): - """Classe représentant les entraînements. + """Classe représentant les entraînements. - Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents. + Un entraînement est une occurence d'un cours pendant lequel des gmnastes sont présents. - Un objet de cette classe lie donc un cours et un gymnaste à une date donnée. - """ + Un objet de cette classe lie donc un cours et un gymnaste à une date donnée. + """ - class Meta: - verbose_name = "Training" - verbose_name_plural = "Trainings" + class Meta: + verbose_name = "Training" + verbose_name_plural = "Trainings" - gymnast = models.ForeignKey( - "people.Gymnast", - verbose_name="Gymnast", - on_delete=models.CASCADE, - default=None, - related_name="trainings", - ) - course = models.ForeignKey( - Course, verbose_name="Course", on_delete=models.CASCADE, default=None - ) - trainingdate = models.DateField(verbose_name="Date") + gymnast = models.ForeignKey( + "people.Gymnast", + verbose_name="Gymnast", + on_delete=models.CASCADE, + default=None, + related_name="trainings", + ) + course = models.ForeignKey( + Course, verbose_name="Course", on_delete=models.CASCADE, default=None + ) + trainingdate = models.DateField(verbose_name="Date") - def __str__(self): - return "%s - %s, %s" % (self.trainingdate, self.course, self.gymnast) + def __str__(self): + return "%s - %s, %s" % (self.trainingdate, self.course, self.gymnast) - @staticmethod - def create(gymnast, course, trainingdate): - t = Training() - t.gymnast = gymnast - t.course = course - t.trainingdate = trainingdate - t.save() - return t + @staticmethod + def create(gymnast, course, trainingdate): + t = Training() + t.gymnast = gymnast + t.course = course + t.trainingdate = trainingdate + t.save() + return t class Round(Markdownizable): - """Classe représentant les passages des élèves lors d'un entrainement. + """Classe représentant les passages des élèves lors d'un entrainement. - Chaque record représente un passage. Il est donc lié à un record de la classe `Training`. - """ + Chaque record représente un passage. Il est donc lié à un record de la classe `Training`. + """ - class Meta: - verbose_name = "Round" - verbose_name_plural = "Rounds" + class Meta: + verbose_name = "Round" + verbose_name_plural = "Rounds" - EVALUATION_CHOICES = ( - (0, "- -"), - (1, "- +"), - (2, "+ -"), - (3, "+ +"), - ) + EVALUATION_CHOICES = ( + (0, "- -"), + (1, "- +"), + (2, "+ -"), + (3, "+ +"), + ) - training = models.ForeignKey( - Training, on_delete=models.CASCADE, default=None, related_name="rounds" - ) - educative = models.ForeignKey( - "objective.Educative", - on_delete=models.CASCADE, - default=None, - blank=True, - null=True, - ) - round_information = models.CharField(max_length=255, blank=True, null=True) - round_number = models.PositiveSmallIntegerField(blank=True, null=True) - gymnast_evaluation = models.PositiveSmallIntegerField( - choices=EVALUATION_CHOICES, blank=True, null=True - ) - coach_evaluation = models.PositiveSmallIntegerField(blank=True, null=True) - coachid = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL) - nb_of_realisation = models.PositiveSmallIntegerField(blank=True, null=True) - nb_of_success = models.PositiveSmallIntegerField(blank=True, null=True) - is_important = models.BooleanField(default=False) + training = models.ForeignKey( + Training, on_delete=models.CASCADE, default=None, related_name="rounds" + ) + educative = models.ForeignKey( + "objective.Educative", + on_delete=models.CASCADE, + default=None, + blank=True, + null=True, + ) + round_information = models.CharField(max_length=255, blank=True, null=True) + round_number = models.PositiveSmallIntegerField(blank=True, null=True) + gymnast_evaluation = models.PositiveSmallIntegerField( + choices=EVALUATION_CHOICES, blank=True, null=True + ) + coach_evaluation = models.PositiveSmallIntegerField(blank=True, null=True) + coachid = models.ForeignKey(User, blank=True, null=True, on_delete=models.SET_NULL) + nb_of_realisation = models.PositiveSmallIntegerField(blank=True, null=True) + nb_of_success = models.PositiveSmallIntegerField(blank=True, null=True) + is_important = models.BooleanField(default=False) - def __str__(self): - return "%s" % (self.round_number) + def __str__(self): + return "%s" % (self.round_number) class PlanningLine(Markdownizable): - """Classe représentant les passages prévisionnels (incubating idea). - """ + """Classe représentant les passages prévisionnels (incubating idea). + """ - class Meta: - verbose_name = "Planning Line" - verbose_name_plural = "Planning lines" - # ordering = ['gymnast', 'date', 'order'] + class Meta: + verbose_name = "Planning Line" + verbose_name_plural = "Planning lines" + # ordering = ['gymnast', 'date', 'order'] - gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE, default=None) - date = models.DateField(verbose_name="Date") - order = models.PositiveSmallIntegerField() - todo = models.CharField(max_length=255) + gymnast = models.ForeignKey(Gymnast, on_delete=models.CASCADE, default=None) + date = models.DateField(verbose_name="Date") + order = models.PositiveSmallIntegerField() + todo = models.CharField(max_length=255) diff --git a/src/planning/tests/tests_models.py b/src/planning/tests/tests_models.py index 3416ed2..4c99dee 100644 --- a/src/planning/tests/tests_models.py +++ b/src/planning/tests/tests_models.py @@ -1,7 +1,39 @@ +"""Tests liés au modèle de l'application planning""" -from datetime import datetime +from datetime import datetime, time from django.test import TestCase -from ..models import get_number_of_weeks_between, Season + +from location.models import Club, Place, Country + +from ..models import get_number_of_weeks_between, Season, Course + + +class TestCourse(TestCase): + def setUp(self): + pass + + def test_course_duration(self): + """Vérifie le calcul de durée d'un cours""" + course = Course.objects.create( + iso_day_number=2, + datebegin=datetime(2021, 1, 1), + dateend=datetime(2021, 9, 30), + hour_begin=time(hour=19, minute=30), + hour_end=time(hour=22, minute=45), + club=Club.objects.create( + name="RCW", + place=Place.objects.create( + name="Somewhere", + postal=1080, + country=Country.objects.create( + namefr="Belgique", + isonum=56 + ) + ) + ) + ) + + self.assertEqual(course.duration, 2) class TestUtils(TestCase):