Vue/JS : comparer 2 objets avec des dates pour détecter un conflit éventuel

une joyeuse prise de tête

Le problème exposé dans ce sujet a été résolu.

Hello!

J’aurais besoin d’un petit coup de main pour vérifier que je ne suis pas en train de faire des âneries et que je suis bien dans la bonne direction. Je n’atteins actuellement pas le résultat voulu mais je n’en suis pas loin.

Donc pour une Entité (un lieu, en l’occurrence), je stocke en BDD ses horaires d’ouverture / fermeture par jour. J’ai donc des entités avec des centaines d’horaires disponibles. Quand je crée l’entité, je fais évidemment preuve du plus de discernement possible pour éviter de créer des doublons ou des conflits d’horaire. Je me suis sévèrement pris la tête pour réussir à faire ce système qui est maintenant proche de "pas mal" :). Mais la mise en BDD de ces horaires n’est pas le problème sur lequel je bute. Non mon problème c’est que des fois, il est possible de créer des conflits entre deux plages horaires : soit les horaires se chevauchent, soit les horaires sont rigoureusement identiques. Je crée donc un Détecteur à Problèmes (avec Vue : du JS, quoi) qui doit se charger de régler ces conflits. Pour l’instant j’en suis à la détection, la résolution viendra quand je pourrai me fier à la détection…

J’ai donc en BDD une table avec comme champs : "id" / "activity_id" (le lieu) / "day" / "opening_time" / "closing_time". Avec Symfony 5 et Api Platform, je peux faire remonter la liste d’horaires pour un lieu donné ordonné par jour ASC puis par opening_time ASC, ce qui me donne un Array d’Objets de type {'@id': blabla, 'day’: bloblo, 'openingTime’: blibli, etc.}.

Tel que je suis parti:

    checkForConflicts() {
      this.searching = true;
      let hours = this.items;
      //On crée un espace de stockage vide
      var currentDay = '',
          openingHour = '',
          closingHour = '',
          prevItem = {};
      //On commence la boucle
      for (let item of hours) {
        //Si l'item est le premier ou si l'item commence une nouvelle journée
        //Il ne peut pas y avoir de conflit
        if (item.day !== currentDay) {
          currentDay = item.day;
          openingHour = item.openingTime;
          closingHour = item.closingTime;
          prevItem = item;
        } else {
          //Conflit si : item.openingTime < prevItem.closingTime ou l'inverse... ?!
          // C'est là que j'ai besoin d'aide, pour créer les bons embranchements !
          if (item.openingTime < closingHour || openingHour > item.closingTime) {
            this.conflictsCount++;            
            //Si ils sont rigoureusement identiques, on propose une résolution auto : suppression de l'un des deux
            //On les met dans doubles[]
            if (item.openingTime === openingHour && item.closingTime === closingHour) {
              this.exactMatch++;
              this.doubles.push({prevItem, item});             
            }     
            else {
              //Sinon on les met simplement dans Problems[]
              this.problems.push({prevItem, item})
            }
            //Item devient le prevItem : ajouté pour détecter aussi les problèmes de triple exemplaires
            currentDay = item.day;
            openingHour = item.openingTime;
            closingHour = item.closingTime;
            prevItem = item;
          }         
        }
      }//Fin de la boucle For item of hours
      this.searching = false;
      if (this.conflictsCount == 0) {        
        this.message = 'Aucun conflit détecté !';
        this.youShallContinue = true;       
      }
      else {
        this.message = this.conflictsCount + ' conflit(s) détecté(s)';
        this.youShallContinue = false;
        //ToDo : résoudre les conflits
        console.log(this.problems);
        console.log(this.doubles);
      }
    },//Fin de CheckForConflicts

Explications :

1- je crée un espace vide pour stocker le jour du premier objet du tableau "hours"

2- je commence par comparer la valeur de "day" de l’objet à celui en mémoire.

3- si ils sont différents, il n’y a pas de conflit possible, on stocke en mémoire l’objet qui vient d’être testé, currentDay devient donc "item.day", prevItem prend les valeurs de l’item inspecté.

4- boucle suivante

5- Si c’est la même valeur pour le jour, il peut y avoir un problème, il faut donc regarder de plus près

6- si l’item suivant a rigoureusement les mêmes horaires, on stocke prevItem et item dans Doubles[] pour en supprimer un des deux ultérieurement. Cette partie a l’air de fonctionner sur les entités tests.

7- si ça se chevauche, il faut les signaler et les soumettre à l’administrateur qui éditera la paire prevItem / item selon ce qui lui semble être le mieux (il pourra aussi en supprimer un ou les deux)

Donc c’est la partie 7 qui me pose problème (à partir de la ligne 21 du code). Je m’emmêle les pinceaux et n’arrive pas à créer les embranchements intelligents nécessaires au bon fonctionnement du code.

Quelqu’un voudrait-il bien prendre le temps de réfléchir avec moi à la meilleure solution ?

Merci.

+0 -0

Oui oui tout ça ne concerne que le Back-Office sécurisé (enfin sécurisé avec Symfony, hein, et toutes les méthodes utiles d’identification/authentification, que ce soit via Api Platform, les contrôleurs, les entités, les rôles, les pare-feux, etc.). Après, c’est mon premier site utilisant Api Platform, il y aura un long moment où je vérifierai toutes les possibilités de piratage/triche/abus.

Le rôle de ce script est d’aider la personne autorisée à corriger simplement les cas particuliers lorsqu’il génère les horaires d’une entité afin de limiter la taille en BDD et surtout simplifier les requêtes ultérieures qui proviendront du Front (qui n’est en gros qu’en lecture seule).

En fait, dans le back, j’ai créé un Component avec Vue/Vuetify qui permet de créer des objets {day / opening / closing} via différentes méthodes : sélectionner une plage de dates précise, sélectionner un ou plusieurs mois, sélectionner des patterns particuliers (tous les mardis, tous les week-ends, etc.), sélectionner les vacances scolaires ou les jours fériés, ou justement sélectionner l’inverse (tous les jours sauf…). Puis le script assemble tous les horaires créésvia ces différentes méthodes, les trie, et c’est là que cet autre Component entre en jeu pour détecter les problèmes éventuels. L’entité "mère" peut avoir des horaires complètement débiles (type "ouvert les week-ends de vacances de la Zone C, tout le mois de décembre, le 14 juillet, et du mardi au jeudi de 14h à 14h02") qu’il faut quand même réussir à créer sans le faire jour par jour, ce qui serait dément à faire.

Tout est au format DateTime ou Date, et non String. C’est d’ailleurs une joyeuse prise de tête à gérer en JavaScript, je ne connaissais pas trop les objets Date, ben c’est chiant _. Et API Platform remet en plus une couche de paramétrages particuliers type Timezone qui est bien perturbant aussi. A la création de la BDD on m’avait dit que ça permettrait par contre de faire des requêtes plus efficacement : le but du jeu final est en effet de pouvoir sortir rapidement une liste de tout ce qui est ouvert à un moment donné.

Oui, la liste est ordonnée par Opening_Time ASC qui contient des données sous forme "2020–11–11 09:00:00", pareil pour Closing_Time. Day est juste en Date "2020–11–11" (nb: API Platform persiste à convertir en DateTime quand il normalise, ce qui ajoute une opération supplémentaire en JS pour récupérer juste le jour - les 10 premiers caractères - . Sinon il ajoute l’heure de la requête, ce qui m’a perturbé au début le temps de comprendre ce qui se passait. Si quelqu’un sait comment dire à APIPlatform d’arrêter ça, ce serait un chouette bonus)

La seule sécurité sur la logique des données est assurée par le script Vue, et donc la bonne volonté de l’administrateur authentifié : je fais en sorte d’interdire si closingTime est inférieur à openingTime, et théoriquement, vu l’interface, on ne peut pas avoir un closingTime sur une autre journée que openingTime. Maintenant, ça reste du JS et un envoi à l’API. Quelqu’un de mal intentionné pourrait mettre des données incohérentes si il passe toutes les sécurités, et je ne vois pas encore comment m’assurer de la qualité des données avant de les insérer en BDD : comme je l’ai dit plus haut, c’est la première fois que j’utilise API Platform et il y a encore pas mal de choses qui m’énervent prodigieusement, et d’autres qui me sont encore très étrangères. Si tu connais et que tu peux m’orienter vers de la documentation, je suis intéressé.

Un exemple de données bidons tirées de la BDD : ID/ activity_id / day / opening_time / closing_time

1451    24  2021-02-22  2021-02-22 07:00:00     2021-02-22 11:00:00
1452    24  2021-02-23  2021-02-23 07:00:00     2021-02-23 13:30:00
1454    24  2021-02-23  2021-02-23 13:00:00     2021-02-23 17:00:00
1441    24  2021-02-24  2021-02-24 07:00:00     2021-02-24 11:00:00
1446    24  2021-02-24  2021-02-24 07:00:00     2021-02-24 11:00:00
1453    24  2021-02-24  2021-02-24 07:00:00     2021-02-24 11:00:00
1455    24  2021-02-24  2021-02-24 13:00:00     2021-02-24 17:00:00
1442    24  2021-02-25  2021-02-25 07:00:00     2021-02-25 11:00:00
1447    24  2021-02-25  2021-02-25 07:00:00     2021-02-25 11:00:00
1456    24  2021-02-25  2021-02-25 13:00:00     2021-02-25 17:00:00
1443    24  2021-02-26  2021-02-26 07:00:00     2021-02-26 11:00:00
1448    24  2021-02-26  2021-02-26 07:00:00     2021-02-26 11:00:00
1457    24  2021-02-26  2021-02-26 13:00:00     2021-02-26 17:00:00
1444    24  2021-02-27  2021-02-27 07:00:00     2021-02-27 11:00:00
1449    24  2021-02-27  2021-02-27 10:00:00     2021-02-27 22:00:00
1445    24  2021-02-28  2021-02-28 07:00:00     2021-02-28 11:00:00
1450    24  2021-02-28  2021-02-28 07:00:00     2021-02-28 11:00:00

1452 et 1454 se chevauchent, ainsi que 1444 et 1449. Beaucoup de doublons sinon.

En parallèle à cette conversation j’ai continué sur le script du premier post (que j’ai édité, du coup), les résultats me semblent encourageants. Avec ce set de données, il me sort 2 conflits et 5 doublons.

Connectez-vous pour pouvoir poster un message.
Connexion

Pas encore membre ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte