Skip to main content
Cahier de textes

Paramétrage du cahier de textes par classe/groupe — module cahier-de-textes (fr.openent.diary)

Implémentation technique de l'écran de paramétrage du cahier de textes par classe ou groupe (activer/désactiver), exigence CCTP MOD11 — Cahier de textes : « permettre la gestion et le paramétrage du cahier de textes de la classe ou du groupe ». Avant ce développement, aucun objet « cahier de classe » dédié n'existait : la couverture reposait implicitement sur le paramétrage des services vie-scolaire (enseignant + matière + classe), sans possibilité d'activer/désactiver le cahier de textes à l'échelle d'une classe ou d'un groupe.

Fiche fonctionnelle

Ce que voit l'utilisateur (scénario personnel de direction / gestion vie scolaire) : Cahier de textes → Paramétrer le cahier de textes par classe/groupe (doc fonctionnelle du module cahier de textes).

Vue d'ensemble

Le paramétrage est exposé dans l'écran Vie Scolaire → Paramétrages → Cahier de texte, à côté des 3 onglets existants (Initialisation, Types de travail, Types de séance) — un 4e onglet « Cahier de textes par classe/groupe » y a été ajouté. Le module vie-scolaire héberge l'IHM (cohérence avec les 3 autres onglets déjà présents dans cet écran), mais les données et l'API appartiennent au module cahier-de-textes (fr.openent.diary), propriétaire fonctionnel du cahier de textes.

Backend

Table diary.audience_settings

Migration sql/035-diary-audience-settings.sql :

CREATE TABLE diary.audience_settings (
structure_id varchar(36) NOT NULL,
audience_id varchar(36) NOT NULL,
enabled boolean NOT NULL DEFAULT TRUE,
PRIMARY KEY (structure_id, audience_id)
);

Une classe/groupe sans ligne dans cette table est considérée enabled = true par défaut (état initial : tout est activé). Seuls les overrides (le plus souvent des désactivations) sont persistés.

API

AudienceSettingsController (controllers/AudienceSettingsController.java), protégée par le même droit que l'écran d'initialisation existant du module (@ResourceFilter(ViescoSettingInitialisationData.class), workflow viesco.setting.initalisation.data) :

RouteRôle
GET /diary/structures/:id/audience-settingsListe les classes/groupes de l'établissement avec leur état enabled et les enseignants qui y interviennent
PUT /diary/structures/:id/audience-settings/:audienceIdActive/désactive le cahier de textes pour une classe/groupe

DefaultAudienceSettingsService — agrégation classes + services + overrides

getAudienceSettings combine trois sources hétérogènes :

  1. les classes de l'établissement (bus viescolaire, action classe.getClasseEtablissement) ;
  2. les services enseignant/matière/classe (bus viescolaire, action service.getAllServices), pour en déduire les enseignants intervenant sur chaque classe/groupe ;
  3. les overrides locaux (diary.audience_settings, SQL direct) et les noms d'enseignants (Neo4j direct).

⚠️ Piège Vert.x rencontré : deux appels eb.request("viescolaire", …) lancés en parallèle (CompositeFuture.all) vers ce bus ne renvoient jamais leur réponse (aucun des deux callbacks ne se déclenche), alors que les mêmes appels fonctionnent correctement enchaînés en séquence. Cause racine non identifiée avec certitude (candidat : contention sur un lock Hazelcast observé au même moment dans les logs, lien de causalité non confirmé). Contournement appliqué : tous les appels vers le bus "viescolaire" sont chaînés via Future.compose() ; seuls les appels SQL et Neo4j directs (hors de ce bus) restent parallélisés entre eux via CompositeFuture.all.

Pont eventbus Viescolaire.java

eventbus/Viescolaire.java (singleton, déjà utilisé pour getSchoolYearPeriod) a été étendu avec getClasses et getServices. Les deux évitent volontairement MessageResponseHandler.messageJsonArrayHandler (helper générique du module diary) : ce helper lit le champ singulier "result", alors que getJsonArrayBusResultHandler côté vie-scolaire (EventBusController.java) répond avec le champ pluriel "results". Une méthode privée handleArrayReply lit directement ce champ.

Bugs plateforme vie-scolaire corrigés au passage

Deux bugs pré-existants dans EventBusController.serviceBusService (action getServices) rendaient les données remontées incomplètes ou vides, découverts en réutilisant cette action pour ce développement :

  1. evaluable = null : oService.put("evaluable", bEvaluable) était appelé sans garde if (bEvaluable != null), contrairement aux filtres voisins du même bloc — résultat : une clause SQL générée WHERE evaluable = NULL, toujours fausse, donc 0 résultat systématique dès que l'appelant ne précisait pas explicitement ce filtre.
  2. Source de données incomplète : l'action bus service.getServices ne lisait que la table SQL viesco.services (services ajustés manuellement, vide dans la plupart des établissements) — les vraies affectations enseignant↔classe viennent de Neo4j (relations TEACHES), agrégées uniquement par DefaultServicesService.getAllServices() (utilisée par l'API REST GET /viescolaire/services, pas par cette action bus). Une nouvelle action bus service.getAllServices a été ajoutée dans EventBusController.java, appelant getAllServicesNoFilter (même source que l'API REST), et Viescolaire.java (diary) repointé dessus.

Application du paramétrage à la recherche (DefaultSearchService)

Sans action supplémentaire, l'écran de paramétrage n'aurait eu aucun effet réel : la barre de recherche enseignant/classe de l'écran principal /diary (routes GET /diary/search et GET /diary/search/groups, contrôleur SearchController) continuait de proposer les classes/groupes désactivés, puisqu'elle interroge directement Neo4j et le bus viescolaire sans connaissance de diary.audience_settings.

DefaultSearchService a été complété avec getDisabledAudienceIds(structureId) (SELECT audience_id FROM diary.audience_settings WHERE structure_id = ? AND enabled = false), dont le résultat est croisé avec les résultats de recherche avant réponse, dans search (filtre sur id) et searchGroups (méthode filterDisabledGroups). Une classe/groupe désactivé n'est donc plus proposé dans aucune des deux recherches.

Frontend (Angular legacy, cahier-de-textes)

  • Nouveau sniplet audience_settings (public/ts/sniplets/audience_settings.ts + template public/template/behaviours/sniplet-audience_settings.html), enregistré dans behaviours.ts.
  • StructureService.ts étendu avec getAudienceSettings/setAudienceEnabled (GET/PUT /diary/structures/:id/audience-settings[/:id]).
  • Le sniplet affiche un tableau Classe/Groupe · Enseignants · Activé (case à cocher), les enseignants étant joints en texte (ou « Aucun enseignant » en italique si aucun service ne référence cette classe/groupe).
  • Intégration côté vie-scolaire : public/templates/viescolaire/param_cdt.html, 4e entrée <header> + bloc ng-if="currParam === 3" embarquant <sniplet template="audience_settings" application="diary" source="source">.

Périmètre non couvert

  • IHM React : ce paramétrage n'existe que côté Angular legacy (?ui=angular), l'écran React (?ui=react) n'a pas d'équivalent pour l'instant — hors scope de ce développement.
  • Blocage de la création côté IHM : désactiver une classe/groupe la retire de la recherche (donc empêche d'y créer/consulter un cahier de textes via ce chemin), mais ne bloque pas une création directe par une API externe qui ne passerait pas par cette recherche.