Projet

Général

Profil

Development #64383

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

Ajouté par Valentin Deniaud il y a environ 2 ans. Mis à jour il y a presque 2 ans.

Statut:
Fermé
Priorité:
Normal
Assigné à:
Catégorie:
-
Version cible:
-
Début:
20 avril 2022
Echéance:
% réalisé:

0%

Temps estimé:
Patch proposed:
Oui
Planning:
Non

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.


Fichiers


Demandes liées

Lié à Intégrations graphiques Publik - Development #65272: Publik famille: widget de réservation & temps ennemisFermé15 mai 2022

Actions

Révisions associées

Révision b359c3f1 (diff)
Ajouté par Valentin Deniaud il y a presque 2 ans

api: forbid overlapping events booking (#64383)

Révision e629fcca (diff)
Ajouté par Valentin Deniaud il y a presque 2 ans

api: forbid overlapping recurring events booking (#64383)

Historique

#2

Mis à jour par Valentin Deniaud il y a environ 2 ans

  • Assigné à changé de Valentin Deniaud à 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

Mis à jour par Stéphane Guiet il y a environ 2 ans

  • Assigné à changé de Stéphane Guiet à 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

Mis à jour par Valentin Deniaud il y a presque 2 ans

  • 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

Mis à jour par Lauréline Guérin il y a presque 2 ans

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

#6

Mis à jour par Stéphane Guiet il y a presque 2 ans

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

Oui

#7

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

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

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

  • Statut changé de Solution proposée à 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

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

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

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

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

#15

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

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

#18

Mis à jour par Lauréline Guérin il y a presque 2 ans

(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

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

        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

Mis à jour par Valentin Deniaud il y a presque 2 ans

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

Mis à jour par Lauréline Guérin il y a presque 2 ans

  • Statut changé de Solution proposée à Solution validée
#24

Mis à jour par Lauréline Guérin il y a presque 2 ans

  • Lié à Development #65272: Publik famille: widget de réservation & temps ennemis ajouté
#25

Mis à jour par Valentin Deniaud il y a presque 2 ans

  • Statut changé de Solution validée à 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

Mis à jour par Transition automatique il y a presque 2 ans

  • Statut changé de Résolu (à déployer) à Solution déployée
#27

Mis à jour par Transition automatique il y a presque 2 ans

Automatic expiration

Formats disponibles : Atom PDF