Préblocage d'une réservation (#80489) #135

Merged
bdauvergne merged 4 commits from wip/80489-preblocage-d-une-reservation into main 2023-11-16 10:53:43 +01:00
Owner

Le préblocage a lieu via le passage d'un paramètre "lock_code" contenant un chaîne quelconque non vide, les espaces en fin et début sont retirés pour éviter les bugs idiots de template.

Ce paramètre est disponible sur les vues datetimes et fillslot pour les rendez-vous et les évènements.

Lors d'une réservation (fillslot) avec lock_code est créé un modèle Lease plus des modèles Event/Booking habituels. Le Lease est en relation 1-1 avec le Booking créé. Le Lease dispose d'une date d'expiration (contrôlé via le setting CHRONO_LOCK_DEFAULT_DURATION mais par ailleurs contrôlable via un autre paramètre lock_duration durant le fill_slot) et d'un champ lock_code qui reprendre la valeur passée.

lock_duration est par ailleurs limité par les settings CHRONO_LOCK_MIN_DURATION/CHRONO_LOCK_MAX_DURATION.

chrono/settings.py:CHRONO_LOCK_MIN_DURATION = 5 * 60
chrono/settings.py:CHRONO_LOCK_DEFAULT_DURATION = 10 * 60
chrono/settings.py:CHRONO_LOCK_MAX_DURATION = 20 * 60

Régulièrement et sur différentes vues (datetimes et fillslot) sont appelés les fonctions clean_meeting_events_with_expired_lease()/clean_bookings_with_expired_lease qui suppriment les Booking (et Event dans le cas des rendez-vous) dont l'objet Lease a expiré, ça évite d'avoir une tâche de fond pour faire cela, c'est fait au fil de l'eau et notamment lors des appels fillslot/datetimes, on a donc une vue toujours à jour des créneaux/évènements disponibles.

Dans w.c.s:
L'appel avec lock_code doit être en sortie de page où le choix du rendez-vous est fait en utilisant les condition de sortie de page. Cet appel doit être en définissant un appel de web-service. Ex.:

Définition du web-service :
  Slug: rdv_fillslot_preblocage
  Méthode: POST
  URL: {{ form_var_rendez_vous_fillslot_url }}
  Paramètre POST: 
    lock_code={{ session_hash_id }}

Condition de sortie de page:
  webservice.rdv_fill_slot_preblocage.err == 0

Si cet appel fillslot a réussi alors le rendez-vous/inscription est pré-bloqué, et on peut passer à la page suivante.cet appel peut-être sur toutes les sorties des pages suivantes il est idempotent (si le lease existe avec le même lock_code il, le rendez-vous/l'inscription est supprimée et recréé à chaque fois, voir dans les blocs transaction.atomic() où se passent la réservation effective). À chaque appel fillslot, l'expiration du lease est prolongée, si l'appel est bien fait en sortie de la page finale ça évite que le fillslot définitif dans le workflow n'échoue.

Pour valider définitivement le rendez-vous/l'inscription, on ajoute à l'appel fillslot (celui qui est typiquement fait en début de workflow après réception de la demande) le lock_code et un autre paramètre "confirm_after_lock" à la valeur true; la présence de ce paramètre permettra le fillslot comme avant mais ne recréera pas le Lease, on se retrouve avec un rendez-vous/une inscription classique.

Appel webservice de réservation définitive:

  Méthode: POST
  URL: {{ form_var_rendez_vous_fillslot_url }}
  Paramètre POST: 
    lock_code={{ session_hash_id }}
    confirm_after_lock=true

Autre commentaire:

Des bidouilles sont faites pour les endpoints datetimes:

  • pour les évènemetns au niveau d'accesseur de Event et de api/views:get_events_from_slots pour avoir une vue des places disponibles des évènements excluant ou pas les inscriptions pré-bloquées
  • pour les rendez-vous dans Agenda.get_all_slots() (exclusion de certains évènements en fonction de la présence du paramètre lock_code, sur les agendas concernés, sur les ressources et sur les évènements agrégés en fonction du user_external_id)

Coté w.c.s on peut utiliser les sources de données datetimes automatiques mais elle ne passent pas de paramètre lock_code et donc l'expérience n'est pas optimale, par exemple si on revient sur la page de sélection du créneau le slot en cours de préblocage ne sera pas disponible. Pour pallier à cela il faut créer une source de donnée manuelle qui passera le paramètre lock_code={{ session_hash_id }}.

Le préblocage a lieu via le passage d'un paramètre "lock_code" contenant un chaîne quelconque non vide, les espaces en fin et début sont retirés pour éviter les bugs idiots de template. Ce paramètre est disponible sur les vues datetimes et fillslot pour les rendez-vous et les évènements. Lors d'une réservation (fillslot) avec lock_code est créé un modèle Lease plus des modèles Event/Booking habituels. Le Lease est en relation 1-1 avec le Booking créé. Le Lease dispose d'une date d'expiration (contrôlé via le setting CHRONO_LOCK_DEFAULT_DURATION mais par ailleurs contrôlable via un autre paramètre lock_duration durant le fill_slot) et d'un champ lock_code qui reprendre la valeur passée. lock_duration est par ailleurs limité par les settings CHRONO_LOCK_MIN_DURATION/CHRONO_LOCK_MAX_DURATION. ``` chrono/settings.py:CHRONO_LOCK_MIN_DURATION = 5 * 60 chrono/settings.py:CHRONO_LOCK_DEFAULT_DURATION = 10 * 60 chrono/settings.py:CHRONO_LOCK_MAX_DURATION = 20 * 60 ``` Régulièrement et sur différentes vues (datetimes et fillslot) sont appelés les fonctions clean_meeting_events_with_expired_lease()/clean_bookings_with_expired_lease qui suppriment les Booking (et Event dans le cas des rendez-vous) dont l'objet Lease a expiré, ça évite d'avoir une tâche de fond pour faire cela, c'est fait au fil de l'eau et notamment lors des appels fillslot/datetimes, on a donc une vue toujours à jour des créneaux/évènements disponibles. Dans w.c.s: L'appel avec lock_code doit être en sortie de page où le choix du rendez-vous est fait en utilisant les condition de sortie de page. Cet appel doit être en définissant un appel de web-service. Ex.: ``` Définition du web-service : Slug: rdv_fillslot_preblocage Méthode: POST URL: {{ form_var_rendez_vous_fillslot_url }} Paramètre POST: lock_code={{ session_hash_id }} Condition de sortie de page: webservice.rdv_fill_slot_preblocage.err == 0 ``` Si cet appel fillslot a réussi alors le rendez-vous/inscription est pré-bloqué, et on peut passer à la page suivante.cet appel peut-être sur toutes les sorties des pages suivantes il est idempotent (si le lease existe avec le même lock_code il, le rendez-vous/l'inscription est supprimée et recréé à chaque fois, voir dans les blocs transaction.atomic() où se passent la réservation effective). À chaque appel fillslot, l'expiration du lease est prolongée, si l'appel est bien fait en sortie de la page finale ça évite que le fillslot définitif dans le workflow n'échoue. Pour valider définitivement le rendez-vous/l'inscription, on ajoute à l'appel fillslot (celui qui est typiquement fait en début de workflow après réception de la demande) le lock_code et un autre paramètre "confirm_after_lock" à la valeur true; la présence de ce paramètre permettra le fillslot comme avant mais ne recréera pas le Lease, on se retrouve avec un rendez-vous/une inscription classique. ``` Appel webservice de réservation définitive: Méthode: POST URL: {{ form_var_rendez_vous_fillslot_url }} Paramètre POST: lock_code={{ session_hash_id }} confirm_after_lock=true ``` Autre commentaire: Des bidouilles sont faites pour les endpoints datetimes: * pour les évènemetns au niveau d'accesseur de Event et de api/views:get_events_from_slots pour avoir une vue des places disponibles des évènements excluant ou pas les inscriptions pré-bloquées * pour les rendez-vous dans Agenda.get_all_slots() (exclusion de certains évènements en fonction de la présence du paramètre lock_code, sur les agendas concernés, sur les ressources et sur les évènements agrégés en fonction du user_external_id) Coté w.c.s on peut utiliser les sources de données datetimes automatiques mais elle ne passent pas de paramètre lock_code et donc l'expérience n'est pas optimale, par exemple si on revient sur la page de sélection du créneau le slot en cours de préblocage ne sera pas disponible. Pour pallier à cela il faut créer une source de donnée manuelle qui passera le paramètre lock_code={{ session_hash_id }}.
bdauvergne added 2 commits 2023-08-22 12:47:09 +02:00
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 34e866f178 to fa6bb51121 2023-08-22 13:37:13 +02:00 Compare
vdeniaud reviewed 2023-08-24 15:19:56 +02:00
vdeniaud left a comment
Owner

Un train de remarques de forme suite à une lecture rapide.

Un train de remarques de forme suite à une lecture rapide.
@ -380,2 +380,3 @@
return [
MeetingType(duration=mt['duration'], label=mt['label'], slug=mt['slug'])
MeetingType(
duration=mt['duration'], label=mt['label'], slug=mt['slug'], agenda=self, id=Ellipsis
Owner

Dans le cadre du commit je crois pas que id=Ellipsis soit requis.

Dans le cadre du commit je crois pas que `id=Ellipsis` soit requis.
Author
Owner

ok.

ok.
bdauvergne marked this conversation as resolved
@ -1566,2 +1576,4 @@
return free_time
@property
def api_datetimes_url(self):
Owner

Si tu veux mais ça doit être une méthode normale et s'appeler get_datetimes_url pour coller à la sémantique générale.

Si tu veux mais ça doit être une méthode normale et s'appeler `get_datetimes_url` pour coller à la sémantique générale.
Author
Owner

ok.

ok.
bdauvergne marked this conversation as resolved
@ -1958,1 +1973,4 @@
@property
def api_datetimes_url(self):
assert self.agenda.kind in ('meetings', 'virtual')
Owner

Alors que assert self.agenda.kind != 'virtual' dans le save() plus haut ?

Alors que `assert self.agenda.kind != 'virtual'` dans le save() plus haut ?
Author
Owner

Ok, retiré.

Ok, retiré.
bdauvergne marked this conversation as resolved
@ -2195,1 +2227,4 @@
@staticmethod
def annotate_queryset_for_lock_code(qs, lock_code):
assert lock_code
Owner

Puisqu'on pourrait mettre ce genre de ligne partout je suis pour ne la mettre nulle part.

Puisqu'on pourrait mettre ce genre de ligne partout je suis pour ne la mettre nulle part.
Author
Owner

ok.

ok.
bdauvergne marked this conversation as resolved
@ -2196,0 +2232,4 @@
locked_booked_places=Count(
'booking',
filter=Q(
booking__cancellation_datetime__isnull=True,
Owner

Si jamais la règle de calcul des places change dans le trigger qui met à jour booked_places, cet endroit sera oublié.

J'imagine qu'on doit pouvoir additionner la colonne booked_places avec le nombre de booking avec lock code, ou juste annoter avec ce nombre et additionner en python aux endroits où c'est utilisé (peut-être que cette dernière option donnerait du code plus clair, car locked_booked_places veut dire littéralement « nombre de réservations avec lock code » alors que ce n'est actuellement pas ça).

Si jamais la règle de calcul des places change dans le trigger qui met à jour booked_places, cet endroit sera oublié. J'imagine qu'on doit pouvoir additionner la colonne booked_places avec le nombre de booking avec lock code, ou juste annoter avec ce nombre et additionner en python aux endroits où c'est utilisé (peut-être que cette dernière option donnerait du code plus clair, car locked_booked_places veut dire littéralement « nombre de réservations avec lock code » alors que ce n'est actuellement pas ça).
Author
Owner

Si jamais la règle de calcul des places change dans le trigger qui met à jour booked_places, cet endroit sera oublié.

Il ne faudra pas l'oublier alors, je ne vois vraiment de moyen de faire autrement, on ne va pas pré-calculer booked_places pour chaque valeur de lock_code.

J'imagine qu'on doit pouvoir additionner la colonne booked_places avec le nombre de booking avec lock code, ou juste annoter avec ce nombre et additionner en python aux endroits où c'est utilisé (peut-être que cette dernière option donnerait du code plus clair, car locked_booked_places veut dire littéralement « nombre de réservations avec lock code » alors que ce n'est actuellement pas ça).

Ça n'est pas possible il faudra quand même reprendre la règle de comptage en excluant ce qui sont lockés avec lock_code, par contre je prends ta remarque sur le nommage, je vais plutôt appeler ça nonlocked_booked_places.

> Si jamais la règle de calcul des places change dans le trigger qui met à jour booked_places, cet endroit sera oublié. Il ne faudra pas l'oublier alors, je ne vois vraiment de moyen de faire autrement, on ne va pas pré-calculer booked_places pour chaque valeur de lock_code. > J'imagine qu'on doit pouvoir additionner la colonne booked_places avec le nombre de booking avec lock code, ou juste annoter avec ce nombre et additionner en python aux endroits où c'est utilisé (peut-être que cette dernière option donnerait du code plus clair, car locked_booked_places veut dire littéralement « nombre de réservations avec lock code » alors que ce n'est actuellement pas ça). Ça n'est pas possible il faudra quand même reprendre la règle de comptage en excluant ce qui sont lockés avec lock_code, par contre je prends ta remarque sur le nommage, je vais plutôt appeler ça nonlocked_booked_places.
Owner

Ça n'est pas possible il faudra quand même reprendre la règle de comptage en excluant ce qui sont lockés avec lock_code

Ça dépend des cas j'imagine, si un jour on remplace cancellation_datetime par simplement un booléen cancelled et que ton annotation est écrite

            locked_places=Count(
                'booking',
                filter=Q(booking__lease__lock_code=lock_code),
            ),
            unlocked_booked_places=F('booked_places') - F('locked_places'), 

alors il y aurait rien besoin de changer. Mais je ne t'embête pas plus avec des cas théoriques, laissons comme ça.

Plus concret pour le calcul du flag full on a bien une règle spécifique aux agendas plages libres petite enfance (#78082) que tu n'as pas prise en compte, à toi de voir si tu l'ajoutes ou si on dit que ces agendas ne sont pas supportés (les deux me vont).

> Ça n'est pas possible il faudra quand même reprendre la règle de comptage en excluant ce qui sont lockés avec lock_code Ça dépend des cas j'imagine, si un jour on remplace cancellation_datetime par simplement un booléen cancelled et que ton annotation est écrite ``` locked_places=Count( 'booking', filter=Q(booking__lease__lock_code=lock_code), ), unlocked_booked_places=F('booked_places') - F('locked_places'), ``` alors il y aurait rien besoin de changer. Mais je ne t'embête pas plus avec des cas théoriques, laissons comme ça. Plus concret pour le calcul du flag full on a bien une règle spécifique aux agendas plages libres petite enfance (#78082) que tu n'as pas prise en compte, à toi de voir si tu l'ajoutes ou si on dit que ces agendas ne sont pas supportés (les deux me vont).
Author
Owner

Plus concret pour le calcul du flag full on a bien une règle spécifique aux agendas plages libres petite enfance (#78082) que tu n'as pas prise en compte, à toi de voir si tu l'ajoutes ou si on dit que ces agendas ne sont pas supportés (les deux me vont).

Je ne vois pas de conséquence sur le nombre de places qui est la seule chose dont je corrige la valeur. À bien y réfléchir si un agenda est en mode partial_booking, alors il n'est jamais plein et donc que ce soit avec des bookings temporaires ou définitifs ça ne change rien. Non ?

> Plus concret pour le calcul du flag full on a bien une règle spécifique aux agendas plages libres petite enfance (#78082) que tu n'as pas prise en compte, à toi de voir si tu l'ajoutes ou si on dit que ces agendas ne sont pas supportés (les deux me vont). Je ne vois pas de conséquence sur le nombre de places qui est la seule chose dont je corrige la valeur. À bien y réfléchir si un agenda est en mode partial_booking, alors il n'est jamais plein et donc que ce soit avec des bookings temporaires ou définitifs ça ne change rien. Non ?
Author
Owner

Je me corrige: tu as raison j'ai implémenté get_full, je vais prendre ça en compte.

Je me corrige: tu as raison j'ai implémenté get_full, je vais prendre ça en compte.
Author
Owner

Fait, j'ai regardé pour almost_full que j'avais ignoré, mais il semble que ce soit c'est simplement un indicateur pour envoyer des notifications aux responsables il me semble, ça n'a pas d'importance ici.

Fait, j'ai regardé pour almost_full que j'avais ignoré, mais il semble que ce soit c'est simplement un indicateur pour envoyer des notifications aux responsables il me semble, ça n'a pas d'importance ici.
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from fa6bb51121 to 52cdaa46ea 2023-08-31 11:34:08 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 52cdaa46ea to c86908d840 2023-08-31 13:00:51 +02:00 Compare
bdauvergne changed title from Préblocage d'une réservation (#80489) to WIP: Préblocage d'une réservation (#80489) 2023-08-31 13:01:34 +02:00
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from c86908d840 to 2c216d7bc0 2023-08-31 15:40:19 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 2c216d7bc0 to 1df930a851 2023-08-31 15:46:04 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 1df930a851 to 9b43f5575e 2023-08-31 15:50:09 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 9b43f5575e to 028306ee02 2023-08-31 17:21:39 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 028306ee02 to f1cd25bf5e 2023-08-31 17:57:04 +02:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from f1cd25bf5e to 8cb643db36 2023-08-31 18:18:10 +02:00 Compare
bdauvergne changed title from WIP: Préblocage d'une réservation (#80489) to Préblocage d'une réservation (#80489) 2023-08-31 18:24:55 +02:00
Author
Owner

Les parties du code concernant les évènements et les rendez-vous sont maintenant bien séparés dans deux commits et j'ai simplifié certains fillslot en posant le atomic() directement sur la méthode, ça simplifie aussi pas mal les calculs sur les places disponibles pendant le fillslot.

Les parties du code concernant les évènements et les rendez-vous sont maintenant bien séparés dans deux commits et j'ai simplifié certains fillslot en posant le atomic() directement sur la méthode, ça simplifie aussi pas mal les calculs sur les places disponibles pendant le fillslot.
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 8cb643db36 to b30a11292b 2023-09-15 09:52:50 +02:00 Compare
Owner

Le code est bon pour moi, je veux bien correction de build pour pouvoir tester en vrai puis valider.

Le code est bon pour moi, je veux bien correction de build pour pouvoir tester en vrai puis valider.
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from b30a11292b to 6ce7d52db2 2023-09-20 13:14:11 +02:00 Compare
Author
Owner

Le code est bon pour moi, je veux bien correction de build pour pouvoir tester en vrai puis valider.

C'est rebasé.

> Le code est bon pour moi, je veux bien correction de build pour pouvoir tester en vrai puis valider. C'est rebasé.
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 6ce7d52db2 to 62d78565ec 2023-09-20 15:26:45 +02:00 Compare
vdeniaud approved these changes 2023-09-20 17:25:48 +02:00
vdeniaud left a comment
Owner

Testé avec succès en local, le truc qui resterait à faire c'est d'afficher en BO que la réservation est un lock (par défaut ça donne un libellé « Anonyme » laconique), ça pourra être vu dans un autre ticket.

Testé avec succès en local, le truc qui resterait à faire c'est d'afficher en BO que la réservation est un lock (par défaut ça donne un libellé « Anonyme » laconique), ça pourra être vu dans un autre ticket.
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 62d78565ec to e8801306b4 2023-11-13 14:38:55 +01:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from e8801306b4 to 577bfdc5c5 2023-11-13 14:46:27 +01:00 Compare
bdauvergne force-pushed wip/80489-preblocage-d-une-reservation from 577bfdc5c5 to 5716d6b3dc 2023-11-16 10:45:05 +01:00 Compare
bdauvergne merged commit 5716d6b3dc into main 2023-11-16 10:53:43 +01:00
bdauvergne deleted branch wip/80489-preblocage-d-une-reservation 2023-11-16 10:53:43 +01:00
Sign in to join this conversation.
No reviewers
No Label
No Milestone
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: entrouvert/chrono#135
No description provided.