Project

General

Profile

Développement #64383

Interdire de réserver un évènement si une réservation existe sur la même période

Added by Valentin Deniaud over 2 years ago. Updated over 2 years ago.

Status:
Fermé
Priority:
Normal
Category:
-
Target version:
-
Start date:
20 April 2022
Due date:
% Done:

0%

Estimated time:
Patch proposed:
Yes
Planning:
No

Description

Soit l'évènement A de 13 à 14h sur l'agenda A', l'évènement B de 13h45 à 14h45 sur l'agenda B'. Une réservation pour l'évènement A doit rendre impossible la réservation de B.

Ce comportement ne peut pas avoir lieu par défaut car :
  • Il y a de l'existant.
  • On peut imaginer un évènement « semaine au ski » et « balade en traîneau » optionnel pendant cette semaine au ski, qu'il faut pouvoir réserver.

Notons par ailleurs que l'API /datetimes/ a déjà trop de paramètres, ça serait cool de ne pas en ajouter un de plus.

J'imagine que peut-être un nouveau paramètre au niveau de l'agenda « Rendre les évènement globalement exclusifs » pourrait convenir ?

En bonus on peut essayer d'inclure pour chaque évènement retourné lors d'un appel la liste des évènements exclus, ça permettrait de facilement griser les choix rendus impossibles par la sélection d'un évènement dans le widget de réservation en front.


Files


Related issues

Related to Intégrations graphiques Publik - Développement #65272: Publik famille: widget de réservation & temps ennemisFermé15 May 2022

Actions

Associated revisions

Revision b359c3f1 (diff)
Added by Valentin Deniaud over 2 years ago

api: forbid overlapping events booking (#64383)

Revision e629fcca (diff)
Added by Valentin Deniaud over 2 years ago

api: forbid overlapping recurring events booking (#64383)

History

#2

Updated by Valentin Deniaud over 2 years ago

  • Assignee changed from Valentin Deniaud to Stéphane Guiet
Je me demande si je ne vois pas les choses en trop grand, un plan moins global ça pourrait être :
  • Se limiter à l'API de réservation d'évènements multiples sur plusieurs agendas.
  • Garantir l'interdiction de la réservation d'évènements superposés uniquement sur les agendas concernés par l'appel.

L'avantage se serait le code plus simple et moins étendu, et le comportement peut être activé par défaut parce que pas d'existant et assez logique.

Ça colle au cas d'usage du ticket, une limitation apparaîtrait lors d'un cas comme :
  • Widget de réservation pour la cantine, deux créneaux pour manger de 12h à 13h ou de 13h à 14h.
  • Widget de réservation d'activités, il y a boxe à 13h.
  • Le plan ici ne permettra pas d'interdire de manger de 13h à 14h et de réserver la boxe en même temps, parce que widgets séparés. Mais c'est peut être justement ce qu'on veut ?
#3

Updated by Stéphane Guiet over 2 years ago

  • Assignee changed from Stéphane Guiet to Valentin Deniaud

Le plan "moins global", cad se limiter à l'API de réservations multiples me paraît être le bon.
Pourquoi attaquer l'ensemble de chrono quand (visiblement) seul famille et ces agendas + API de réservations multiples réclament cette feature ?

Le plan ici ne permettra pas d'interdire de manger de 13h à 14h et de réserver la boxe en même temps, parce que widgets séparés. Mais c'est peut être justement ce qu'on veut ?

Oui mais il y aura des cas ou il y aurait superposition et pourtant des temps non-ennemis.
Exemple : une activité est organisée, genre une sortie au ski et la journée est facturée avec ou pas un supplément pour la fourniture du matériel.
Je me voyais bien gérer ce cas avec deux temps (journée et matériel) dans le widget poussé vers les parents.

On peut trouver des manières de gérer ce genre de cas (par exemple en mettant des horaires fictifs au temps matériel), bref en paramétrant de manière "astucieuse".
Mais il y aura des cas (tu exposes celui d'activités organisées pendant un autre temps d'accueil, cas typique, les TAP-NAP qui existent encore dans certaines collectivités) ou ce contournement sera compliqué (surtout quand il faut comptabiliser du temps).

Si cela ne met pas une complexité folle dans le code, il faudrait pouvoir inclure ou exclure de cette règle de temps ennemis des agendas, des événements (y compris dans le même agenda).
A discuter, car partir du principe que nous pourrions ignorer ce cas en version 1 de Publik Famille pour le mettre en place quand le cas se présentera, est très entendable.

#4

Updated by Valentin Deniaud over 2 years ago

  • Support dans l'API /agendas/datetimes/
    • On récupère une nouvelle clé overlaps pour chaque évènement, c'est une liste contenant les slugs des évènements qui le chevauche, c'est à destination du widget.
  • Support dans les API /agenda/xxx/events/fillslots/ et /agendas/events/fillslots/
    • Refus de la réservation si deux évènements se chevauchent, c'est le comportement par défaut et dans l'attente d'un cas d'usage contraire ce n'est pas désactivable.

À noter aussi qu'un évènement qui n'a pas de durée n'est pas concerné par ces règles (ça aide peut-être pour le cas « matériel de ski » décrit par Stéphane).

#5

Updated by Lauréline Guérin over 2 years ago

Il faudrait aussi gérer la réservation récurrente non ?

#6

Updated by Stéphane Guiet over 2 years ago

Il faudrait aussi gérer la réservation récurrente non ?

Oui

#7

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

Il faudrait aussi gérer la réservation récurrente non ?

Fait dans 0002. Sacrément galère, je n'ai pas réussi à avoir ce que je voulais en une requête comme dans 0001, du coup je repasse en python derrière et c'est plutôt moche. (pour les archives j'ai même sollicité Pierre D ici https://pad.libre-entreprise.org/p/eo-sql-valentin mais je crois que la solution full SQL n'est pas représentable avec l'ORM)

#8

Updated by Lauréline Guérin over 2 years ago

Pas de paramètre optionnel passé aux endpoints pour activer ou non cette limitation ?

dans 0001, test_datetimes_multiple_agendas_overlapping_events,
('foo-bar@event', ['foo-bar@event-containing-all-events', 'foo-bar-2@event']),

foo-bar@event commence à midi et termine à 14H, foo-bar-2@event commence à 14H; il ne devrait pas y avoir d'overlaps, si ?

0002, dans test_recurring_events_api_list_overlapping_events, possible de rajouter des events qui partagent juste une borne ? (comme dans 0001, un event qui finit à 14H, un autre qui commence à la même heure)

(globalement tester le partage de borne dans tous les datetimes et fillslots)

(et beau boulot !)

#9

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

Pas de paramètre optionnel passé aux endpoints pour activer ou non cette limitation ?

Nope, vu le nombre de paramètres déjà présents je préfère attendre une demande explicite avant d'en introduire un nouveau.

dans 0001, test_datetimes_multiple_agendas_overlapping_events,
('foo-bar@event', ['foo-bar@event-containing-all-events', 'foo-bar-2@event']),

foo-bar@event commence à midi et termine à 14H, foo-bar-2@event commence à 14H; il ne devrait pas y avoir d'overlaps, si ?

foo-bar-2@event commence à 13h, tu confonds avec foo-bar-2@event-1 ! (ça appelait bien sûr renommage et j'ai donc renommé pour que les tests soient plus évidents)

0002, dans test_recurring_events_api_list_overlapping_events, possible de rajouter des events qui partagent juste une borne ? (comme dans 0001, un event qui finit à 14H, un autre qui commence à la même heure)

Ouep, j'ai fait ça partout.

#10

Updated by Lauréline Guérin over 2 years ago

  • Status changed from Solution proposée to Solution validée

Nope, vu le nombre de paramètres déjà présents je préfère attendre une demande explicite avant d'en introduire un nouveau.

Niveau perfs, ça va pas être trop impactant ?

foo-bar-2@event commence à 13h, tu confonds avec foo-bar-2@event-1 ! (ça appelait bien sûr renommage et j'ai donc renommé pour que les tests soient plus évidents)

Merci, c'est plus facile à lire avec ces nouveaux labels :)

#11

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

Niveau perfs, ça va pas être trop impactant ?

Effectivement la requête est vachement lourde, mais je pense qu'on est protégés par le fait qu'on ne va jamais réserver plus d'une dizaine d'évènements via ces API :

In [78]: for i in range(100):
    ...:     Event.objects.create(start_datetime=now() + datetime.timedelta(hours=i % 3), duration=120, p
    ...: laces=i + 1, agenda=a, slug='test-%s' % i)

In [79]: qs = Event.objects.filter(agenda=a)

In [80]: qs.count()
Out[80]: 100

In [81]: %timeit list(qs)
1.52 µs ± 74.4 ns per loop (mean ± std. dev. of 7 runs, 1000000 loops each)

In [82]: %timeit list(Event.annotate_queryset_with_overlaps(qs))
27.6 ms ± 2.61 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)

In [83]: %timeit list(qs.filter(places__lte=10))
2.03 ms ± 53.1 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

In [84]: %timeit list(Event.annotate_queryset_with_overlaps(qs.filter(places__lte=10)))
6.62 ms ± 829 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

#12

Updated by Lauréline Guérin over 2 years ago

on ne va jamais réserver plus d'une dizaine d'évènements via ces API

Heu, pour la famille, on réserve sur une année scolaire complète. Le widget de résa loade tous les créneaux ouverts entre aujourd'hui et la fin de l'année scolaire, pour la garderie du matin, la cantine, la garderie du soir, les mercredi. Ca fait bien plus d'une dizaine d'événements, surtout en début d'année.

Effectivement la requête est vachement lourde

Argument en faveur du param supplémentaire: On pourrait limiter l'impact avec un param pour activer cette limitation à la demande, car en dehors de la famille ça sera probablement peu utilisé.
(Mais ça n'empêche pas d'essayer de voir si on peut optimiser cette requête, PierreD aura peut-être des idées ?)

#13

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

Heu, pour la famille, on réserve sur une année scolaire complète. Le widget de résa loade tous les créneaux ouverts entre aujourd'hui et la fin de l'année scolaire, pour la garderie du matin, la cantine, la garderie du soir, les mercredi. Ca fait bien plus d'une dizaine d'événements, surtout en début d'année.

Tu parles de la réservation d'une semaine type ? Dans ce cas la requête compliquée n'est appliqué qu'aux évènements récurrents, pas aux créneaux effectifs.

#14

Updated by Lauréline Guérin over 2 years ago

Non, la réservation normale, endpoints events multi agenda (fillslots + datetimes)

#15

Updated by Valentin Deniaud over 2 years ago

Du coup je ne comprends pas « tous les créneaux ouverts entre aujourd'hui et la fin de l'année scolaire », je pensais que c'était un affichage filtré sur la semaine ? Le comportement est visible quelque part ?

#16

Updated by Lauréline Guérin over 2 years ago

On affiche à la semaine, mais tout est loadé; on navigue entre les semaines grâce à du JS

#18

Updated by Lauréline Guérin over 2 years ago

(et du coup je me dis qu'on pourrait - si c'est pas déjà fait - faire l'économie du calcul sur les événements hors délais (passés, hors délais de prévenance), mais quand même remontés par datetimes, en disabled)

#19

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

On affiche à la semaine, mais tout est loadé; on navigue entre les semaines grâce à du JS

OK je savais pas, revoici une version avec des paramètres.

Lauréline Guerin a écrit :

(et du coup je me dis qu'on pourrait - si c'est pas déjà fait - faire l'économie du calcul sur les événements hors délais (passés, hors délais de prévenance), mais quand même remontés par datetimes, en disabled)

C'est un peu de boulot pour arriver à ça, ces vérifs sont faites à posteriori en python et ça demanderait de les jouer d'abord en SQL, je pense qu'on pourra ouvrir un autre ticket.

#20

Updated by Lauréline Guérin over 2 years ago

        overlapping_events = Event.annotate_queryset_with_overlaps(events).filter(has_overlap=True)
        if check_overlaps and overlapping_events:
            raise APIError(
                N_('Some events occur at the same time: %s'),
                ', '.join(sorted(str(x) for x in overlapping_events)),
            )

L'annotation ne dépend pas de check_overlaps ?
#21

Updated by Valentin Deniaud over 2 years ago

Lauréline Guerin a écrit :

L'annotation ne dépend pas de check_overlaps ?

C'était juste une astuce pour éviter un niveau d'indentation en plus en exploitant la paresse du and, mais puisque ça n'apporte rien, revoici en plus clair.

#22

Updated by Lauréline Guérin over 2 years ago

  • Status changed from Solution proposée to Solution validée
#24

Updated by Lauréline Guérin over 2 years ago

#25

Updated by Valentin Deniaud over 2 years ago

  • Status changed from Solution validée to Résolu (à déployer)
commit e629fccaec0ee9776a0e0aec3f2cec944ad160fd
Author: Valentin Deniaud <vdeniaud@entrouvert.com>
Date:   Mon May 2 11:29:39 2022 +0200

    api: forbid overlapping recurring events booking (#64383)

commit b359c3f1fff4f91aee9dbc08f0ff1f2c0d56b68a
Author: Valentin Deniaud <vdeniaud@entrouvert.com>
Date:   Thu Apr 21 11:48:57 2022 +0200

    api: forbid overlapping events booking (#64383)
#26

Updated by Transition automatique over 2 years ago

  • Status changed from Résolu (à déployer) to Solution déployée
#27

Updated by Transition automatique over 2 years ago

Automatic expiration

Also available in: Atom PDF