Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(api, admin, lib): 3594 - Créer des groupes de cohortes #4508

Open
wants to merge 20 commits into
base: main
Choose a base branch
from

Conversation

nicobret
Copy link
Collaborator

@nicobret nicobret commented Nov 5, 2024

Description

Les règles de changement de cohortes ne sont pas les même lorsqu'un volontaire change vers une cohorte de la même année (simple check d'éligibilité) ou bien vers la suivante (nécessité de mettre à jour les informations en passant par la réinscription).

Or il est difficile de distinguer les "cohortes de la même année" des autres à partir de leurs dates uniquement, notamment lorsque des ensembles de cohortes se chevauchent (exemple des cohortes de la Toussaint 2024, dont l'inscription fut ouverte en même temps que l'inscription pour les séjours 2025).

Solution : regrouper les cohortes en groupes d'éligibilité.

Evolution à faire avant la refonte du changement de séjour.

Todo

  • Lib - ajouter les groupes de cohortes dans le modèle de données
  • DB - créer une migration
  • API - créer le contrôleur
  • API - permettre de populate la cohorte avec les données de son groupe
  • API - ajouter les tests d'intégration
  • Front - ajouter un sélecteur avec option de création

Checklist

  • ⚠️ My code can have side-effects on other part of the code-base
  • I have performed a self-review of my code (and removed console.log)

Ticket / Issue

Fixes Notion ticket

Testing instructions

  1. Dans le paramétrage dynamique, véfrifier que les cohortes ont un groupe attribué.
  2. Changer une cohorte de groupe.
  3. Créer un nouveau groupe et attribuer une cohorte.

@nicobret nicobret marked this pull request as ready for review November 12, 2024 07:45
@nicobret nicobret changed the title feat(api, admin): 3594 - Créer des groupes de cohortes feat(api, admin, lib): 3594 - Créer des groupes de cohortes Nov 12, 2024
path: "/cohort-group",
});
const { ok, code, data } = await request();
if (!ok) throw new Error(code);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem pour le toast.error

method: "GET",
});
const { ok, code, data } = await request();
if (!ok) throw new Error(code);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Il manque le toast.error avant de lever une exception non?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bien vu, mais cette fonction ne sert qu'à récupérer les donnés. Le toast c'est de l'UI, donc c'est le rôle de React-Query de l'afficher. Je vais ajouter un toast dans le onError au niveau du hook.

}
}
} catch (error) {
console.error(error);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Plutôt utiliser le logger:
const { logger } = require("../src/logger"); avec logger.info / logger.error


const groups = await CohortModel.aggregate(pipeline);

for (const group of groups) {
Copy link
Collaborator

@Eric013 Eric013 Nov 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Je sais que c'est un fichier de migration mais pour ce cas avec les boucles for avec des instructions findBy* c'est un peu couteux en I/O et bloque le parallélisme:

  • Utiliser les requêtes en masse:
const allCohortIds = groups.flatMap(group => group.documents.map(doc => doc._id)).filter(id => id);
const cohorts = await CohortModel.find({ _id: { $in: allCohortIds } });
const cohortMap = new Map(cohorts.map(cohort => [cohort._id.toString(), cohort]));
  • utiliser le insertMany
  • Paralléliser les opérations

Par exemple:

try {
      // 1. Créer le groupe de réserve
      const reserveGroup = new CohortGroupModel({ name: "Réserve" });
      await reserveGroup.save({ fromUser });
      logger.info("Groupe de réserve créé avec succès.");

      // 2. Agréger les groupes existants
      const groups = await CohortModel.aggregate(pipeline);
      logger.info(`Agrégation terminée. Nombre de groupes trouvés: ${groups.length}`);

      if (groups.length === 0) {
        logger.info("Aucun groupe trouvé.");
        return;
      }

      // 3. Préparer les nouveaux groupes à insérer
      const newGroups = groups.map(group => {
        const { type, year } = group;
        const name = `${type === COHORT_TYPE.VOLONTAIRE ? "HTS" : "CLE"} ${year}`;
        return { type, year, name };
      });

      // 4. Insérer tous les nouveaux groupes en une seule opération
      const insertedGroups = await CohortGroupModel.insertMany(newGroups, { ordered: false });
      logger.info(`Insertion des nouveaux groupes terminée. Nombre de groupes insérés: ${insertedGroups.length}`);

      // 5. Créer une map des groupes insérés pour un accès rapide
      const groupMap = new Map(insertedGroups.map(group => [group.name, group._id]));
      logger.info("Map des groupes insérés créée.");

      // 6. Collecter tous les IDs de cohortes pour une récupération en masse + Filtrer les IDs valides
      const allCohortIds = groups
        .flatMap(group => group.documents.map(doc => doc._id))
        .filter(id => id); 

      // 7. Récupérer toutes les cohortes en une seule requête
      const cohorts = await CohortModel.find({ _id: { $in: allCohortIds } });
      logger.info(`Récupération des cohortes terminée. Nombre de cohortes trouvées: ${cohorts.length}`);

      // 8. Préparer les opérations de mise à jour
      const cohortUpdates = cohorts.map(cohort => {
        const groupName = `${cohort.type === COHORT_TYPE.VOLONTAIRE ? "HTS" : "CLE"} ${new Date(cohort.dateStart).getFullYear()}`;
        const targetGroupId = cohort.name === "à venir" ? reserveGroup._id : groupMap.get(groupName);

        if (!targetGroupId) {
          throw new Error(`Aucun groupe cible trouvé pour la cohorte: ${cohort._id}`);
        }

        // Définir le champ cohortGroupId
        cohort.cohortGroupId = targetGroupId;

        return cohort.save({ fromUser });
      });

      // 9. Exécuter toutes les mises à jour en parallèle
      await Promise.all(cohortUpdates);
      logger.info("Mises à jour des cohortes terminées avec succès.");

    } catch (error) {
      logger.error("Migration échouée:", error);
      throw error;
    }

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Question au sujet du insertMany sur les groupes : on perd l'info de l'auteur de la modif non ? (La propriété "fromUser" fournie à la fonction save(), qui sert à renseigner l'info user dans les patches).

Copy link
Collaborator Author

@nicobret nicobret Nov 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Autre question : pourquoi throw dans le catch ? C'est pour la lib mongo-migrate ? Pourquoi le try catch du coup ? A-t-on besoin de logger une erreur en plus de l'échec de la migration ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants