From 6234fa02167a7b36561a7f215c7c1b3f305a067a Mon Sep 17 00:00:00 2001
From: p2312013 <amira.rabehi@etu.univ-lyon1.fr>
Date: Thu, 16 Jan 2025 19:06:39 +0100
Subject: [PATCH] started fixing the 4th visualisation

---
 static/js/main.js | 422 +++++++++++-----------------------------------
 1 file changed, 102 insertions(+), 320 deletions(-)

diff --git a/static/js/main.js b/static/js/main.js
index e578e1d..5e8ca82 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -999,8 +999,7 @@ function renderCaloriesVisualization() {
       }
     });
 }
-
-// Visu4
+// Visu4 - Découpe en 4 visualisations distinctes avec légende, modal et gestion des valeurs manquantes
 function renderSleepVisualization() {
   fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
     .then((response) => response.json())
@@ -1012,85 +1011,58 @@ function renderSleepVisualization() {
       const members = ["Corentin", "Maya", "Anis", "Amira"];
       data.forEach((d) => (d.date = parseDate(d.date)));
 
-      // Dimensions et marges
-      const margin = { top: 50, right: 230, bottom: 150, left: 70 };
-      const width = 800 - margin.left - margin.right;
-      const height = 500 - margin.top - margin.bottom;
+      // Dimensions et marges pour chaque sous-graphe
+      const margin = { top: 30, right: 30, bottom: 50, left: 50 };
+      const fullWidth = 600;
+      const fullHeight = 400;
+      const width = (fullWidth - margin.left - margin.right) / 2;
+      const height = (fullHeight - margin.top - margin.bottom) / 2;
 
       // Créer le conteneur SVG principal
       const svg = d3
         .select("#sleep-visualization")
         .append("svg")
-        .attr("width", width + margin.left + margin.right)
-        .attr("height", height + margin.top + margin.bottom)
-        .append("g")
-        .attr("transform", `translate(${margin.left},${margin.top})`);
+        .attr("width", fullWidth)
+        .attr("height", fullHeight);
 
-      // Créer un conteneur pour le graphique détaillé
-      const detailContainer = d3
-        .select("#sleep-visualization")
+      // Tooltip
+      const tooltip = d3
+        .select("body")
         .append("div")
-        .attr("id", "detail-container")
-        .style("margin-top", "20px");
+        .attr("class", "tooltip-sleep")
+        .style("opacity", 0);
 
-      // Définir les années et la plage d'affichage
-      const years = [2021, 2022, 2023, 2024];
-      // Créer le slider
+      // Modal pour afficher les détails
+      const modal = d3.select("#modal");
+      modal.select(".close").on("click", () => {
+        modal.style("display", "none");
+        d3.select("#detail-visualization").selectAll("*").remove();
+      });
+
+      // Navigation temporelle
       const prevYearBtn = document.getElementById("sleepPrevYear");
       const nextYearBtn = document.getElementById("sleepNextYear");
       const currentYearDisplay = document.getElementById("sleepCurrentYear");
+      const years = [2021, 2022, 2023, 2024];
 
-      // Gestion des événements des boutons
       prevYearBtn.addEventListener("click", () => {
-        const currentYearIndex = years.indexOf(
-          parseInt(currentYearDisplay.textContent)
-        );
+        const currentYearIndex = years.indexOf(parseInt(currentYearDisplay.textContent));
         if (currentYearIndex > 0) {
           const newYear = years[currentYearIndex - 1];
           currentYearDisplay.textContent = newYear;
-          updateVisualization(newYear); // Affiche la visualisation pour 2024 (par défaut)
-          updateAnalysis("sleep", newYear); // Assure que l'analyse correspondante est également affichée
-
-          // Activer/désactiver les boutons
-          nextYearBtn.disabled = false;
-          if (currentYearIndex - 1 === 0) {
-            prevYearBtn.disabled = true;
-          }
+          updateVisualization(newYear);
         }
       });
 
       nextYearBtn.addEventListener("click", () => {
-        const currentYearIndex = years.indexOf(
-          parseInt(currentYearDisplay.textContent)
-        );
+        const currentYearIndex = years.indexOf(parseInt(currentYearDisplay.textContent));
         if (currentYearIndex < years.length - 1) {
           const newYear = years[currentYearIndex + 1];
           currentYearDisplay.textContent = newYear;
-          updateVisualization(newYear); // Affiche la visualisation pour 2024 (par défaut)
-          updateAnalysis("sleep", newYear); // Assure que l'analyse correspondante est également affichée
-
-          // Activer/désactiver les boutons
-          prevYearBtn.disabled = false;
-          if (currentYearIndex + 1 === years.length - 1) {
-            nextYearBtn.disabled = true;
-          }
+          updateVisualization(newYear);
         }
       });
 
-      // Initialisation des boutons
-      prevYearBtn.disabled =
-        years.indexOf(parseInt(currentYearDisplay.textContent)) === 0;
-      nextYearBtn.disabled =
-        years.indexOf(parseInt(currentYearDisplay.textContent)) ===
-        years.length - 1;
-
-      // Tooltip
-      const tooltip = d3
-        .select("body")
-        .append("div")
-        .attr("class", "tooltip-sleep")
-        .style("opacity", 0);
-
       // Fonction de mise à jour de la visualisation
       function updateVisualization(selectedYear) {
         const filteredData = data.filter(
@@ -1110,303 +1082,113 @@ function renderSleepVisualization() {
           members.forEach((member) => {
             aggregated[`Sleep_${member}`] = d3.mean(
               records,
-              (d) => d[`Sleep_${member}`] || 0
+              (d) => d[`Sleep_${member}`] || -1
             );
           });
           return aggregated;
         });
 
-        svg.selectAll("*").remove();
+        svg.selectAll("g.member-group").remove();
 
-        svg
-          .append("text")
-          .attr("x", width / 2) // Centrer horizontalement
-          .attr("y", margin.top - 60) // Positionner légèrement au-dessus du graphique
-          .attr("text-anchor", "middle") // Centrer le texte
-          .style("font-size", "16px") // Taille de police
-          .style("font-weight", "bold") // Gras
-          .text("Analyse des heures de sommeil par mois");
+        // Créer un graphe pour chaque membre
+        members.forEach((member, memberIndex) => {
+          const row = Math.floor(memberIndex / 2);
+          const col = memberIndex % 2;
 
-        const xScale = d3
-          .scaleBand()
-          .domain(aggregatedData.map((d) => d.month))
-          .range([0, width])
-          .padding(0.2);
+          const memberGroup = svg
+            .append("g")
+            .attr("class", "member-group")
+            .attr(
+              "transform",
+              `translate(${margin.left + col * (width + margin.right)}, ${
+                margin.top + row * (height + margin.bottom)
+              })`
+            );
 
-        const yScale = d3
-          .scaleLinear()
-          .domain([
-            0,
-            d3.max(aggregatedData, (d) =>
-              Math.max(...members.map((member) => d[`Sleep_${member}`]))
-            ),
-          ])
-          .nice()
-          .range([height, 0]);
+          const memberData = aggregatedData.map((d) => ({
+            month: d.month,
+            sleep: d[`Sleep_${member}`],
+          }));
 
-        const colorScale = d3.scaleOrdinal(d3.schemeCategory10).domain(members);
+          const xScale = d3
+            .scaleBand()
+            .domain(memberData.map((d) => d.month))
+            .range([0, width])
+            .padding(0.2);
 
-        // Axe X
-        svg
+          const yScale = d3
+            .scaleLinear()
+            .domain([0, d3.max(memberData, (d) => (d.sleep === -1 ? 10 : d.sleep))])
+            .nice()
+            .range([height, 0]);
+
+
+                      // Axe X avec rotation des labels
+          memberGroup
           .append("g")
           .attr("transform", `translate(0, ${height})`)
           .call(d3.axisBottom(xScale))
           .selectAll("text")
-          .attr("transform", "rotate(-45)")
-          .style("text-anchor", "end");
-
-        // Légende de l'axe X
-        svg
-          .append("text")
-          .attr("x", width / 2)
-          .attr("y", height + 70)
-          .attr("text-anchor", "middle")
-          .style("font-size", "14px")
-          .text("Mois");
-
-        // Axe Y
-        svg.append("g").call(d3.axisLeft(yScale));
-
-        // Légende de l'axe Y
-        svg
-          .append("text")
-          .attr("transform", "rotate(-90)")
-          .attr("x", -height / 2)
-          .attr("y", -50)
-          .attr("text-anchor", "middle")
-          .style("font-size", "14px")
-          .text("Sommeil moyen (heures)");
+          .attr("transform", "rotate(-45)") // Rotation pour éviter les chevauchements
+          .style("text-anchor", "end") // Alignement à droite pour plus de clarté
+          .style("font-size", "10px"); // Taille des labels ajustée pour s'adapter à l'espace
 
-        // Légende des membres (rectangles colorés)
-        // Créer un conteneur pour la légende
-        const legend = svg
-          .append("g")
-          .attr(
-            "transform",
-            `translate(${width / 3}, ${height + margin.bottom - 50})`
-          ) // Positionner la légende en bas, centrée
-          .attr("text-anchor", "middle");
-
-        // Ajouter les éléments de la légende
-        members.forEach((member, i) => {
-          legend
-            .append("rect")
-            .attr("x", i * 100 - members.length * 50) // Espacement horizontal entre les rectangles
-            .attr("y", 0)
-            .attr("width", 15)
-            .attr("height", 15)
-            .attr("fill", colorMap[member]);
+         
+          // Axe Y
+          memberGroup.append("g").call(d3.axisLeft(yScale));
 
-          legend
+          // Titre du sous-graphe
+          memberGroup
             .append("text")
-            .attr("x", i * 100 - members.length * 50 + 20) // Texte à côté du rectangle
-            .attr("y", 12)
-            .text(member)
-            .style("font-size", "12px")
-            .attr("text-anchor", "start");
-        });
-
-        // Ajouter un élément pour "Pas de données"
-        legend
-          .append("rect")
-          .attr("x", members.length * 100 - members.length * 50) // Position pour le rectangle gris
-          .attr("y", 0)
-          .attr("width", 15)
-          .attr("height", 15)
-          .attr("fill", "lightgrey");
-
-        legend
-          .append("text")
-          .attr("x", members.length * 100 - members.length * 50 + 20) // Texte à côté du rectangle gris
-          .attr("y", 12)
-          .text("Pas de données")
-          .style("font-size", "12px")
-          .attr("text-anchor", "start");
-
-        // Barres pour chaque membre
-        members.forEach((member, i) => {
-          svg
-            .selectAll(`.bar-sleep-${member}`)
-            .data(aggregatedData)
+            .attr("x", width / 2)
+            .attr("y", -10)
+            .attr("text-anchor", "middle")
+            .style("font-size", "14px")
+            .style("font-weight", "bold")
+            .text(`Sommeil de ${member}`);
+
+          // Barres pour le membre
+          memberGroup
+            .selectAll(".bar-sleep")
+            .data(memberData)
             .enter()
             .append("rect")
-            .attr(
-              "x",
-              (d) => xScale(d.month) + i * (xScale.bandwidth() / members.length)
+            .attr("x", (d) => xScale(d.month))
+            .attr("y", (d) => (d.sleep === -1 ? yScale(yScale.domain()[1]) : yScale(d.sleep)))
+            .attr("width", xScale.bandwidth())
+            .attr("height", (d) =>
+              d.sleep === -1 ? height - yScale(10) : height - yScale(d.sleep)
             )
-            .attr("y", (d) => {
-              const value = d[`Sleep_${member}`];
-              return value === -1.0 ? yScale(2) : yScale(value); // Placer les -1.0 à une hauteur fixe, ici 2 heures
-            })
-            .attr("width", xScale.bandwidth() / members.length)
-            .attr("height", (d) => {
-              const value = d[`Sleep_${member}`];
-              return value === -1.0
-                ? height - yScale(2)
-                : height - yScale(value); // Barres grisées si -1.0
-            })
-            .attr("fill", (d) => {
-              const value = d[`Sleep_${member}`];
-              return value === -1.0 ? "#D3D3D3" : colorMap[member]; // Gris pour -1.0
-            })
+            .attr("fill", (d) => (d.sleep === -1 ? "#D3D3D3" : colorMap[member]))
             .on("mouseover", function (event, d) {
-              tooltip.transition().duration(200).style("opacity", 0.9); // Transition d'apparition du tooltip
+              tooltip.transition().duration(200).style("opacity", 1);
+              tooltip.html(`
+                <div style="text-align: center;">
+                  <strong>Mois :</strong> ${d.month}<br>
+                  <strong>Sommeil moyen :</strong> ${
+                    d.sleep === -1 ? "Pas de données" : d.sleep.toFixed(2)
+                  } heures
+                </div>
+              `);
+            })
+            .on("mousemove", function (event) {
               tooltip
-                .html(
-                  `Mois : ${d.month}<br>Sommeil moyen : ${
-                    d[`Sleep_${member}`] === -1.0
-                      ? "Pas de données"
-                      : d[`Sleep_${member}`].toFixed(2)
-                  } heures`
-                ) // Affichage du tooltip
-                .style("left", event.pageX + 5 + "px")
-                .style("top", event.pageY - 28 + "px");
+                .style("left", event.pageX + 15 + "px")
+                .style("top", event.pageY + 15 + "px");
             })
             .on("mouseout", function () {
-              tooltip.transition().duration(500).style("opacity", 0); // Transition de disparition du tooltip
-            })
-            .on("click", (event, d) => {
-              const memberColor = colorMap[member]; // Récupérer la couleur du membre
-              showDetailChart(d.month, member, selectedYear, memberColor); // Passer la couleur à la fonction showDetailChart
+              tooltip.transition().duration(500).style("opacity", 0);
             });
         });
       }
 
-      // Fonction pour afficher les détails
-      function showDetailChart(month, member, year, memberColor) {
-        // Affiche le modal
-        const modal = d3.select("#modal");
-        modal.style("display", "block");
-
-        // Fermer le modal
-        modal.select(".close").on("click", () => {
-          modal.style("display", "none");
-          d3.select("#detail-visualization").selectAll("*").remove();
-        });
-
-        const detailContainer = d3.select("#detail-visualization");
-        detailContainer.selectAll("*").remove();
-
-        const filteredData = data.filter(
-          (d) =>
-            formatYear(d.date) === year.toString() &&
-            formatMonth(d.date) === month
-        );
-
-        const dailyData = d3
-          .groups(filteredData, (d) => formatDay(d.date))
-          .map(([day, records]) => ({
-            day: day,
-            value: d3.mean(records, (d) => d[`Sleep_${member}`] || 0),
-          }));
-
-        const detailSvg = detailContainer
-          .append("svg")
-          .attr("width", 600)
-          .attr("height", 400)
-          .append("g")
-          .attr("transform", "translate(50, 50)");
-
-        const xScale = d3
-          .scaleBand()
-          .domain(dailyData.map((d) => d.day))
-          .range([0, 500])
-          .padding(0.1);
-
-        const yScale = d3
-          .scaleLinear()
-          .domain([0, d3.max(dailyData, (d) => d.value)])
-          .nice()
-          .range([300, 0]);
-
-        // Ajout de l'axe X
-        detailSvg
-          .append("g")
-          .attr("transform", "translate(0, 300)")
-          .call(d3.axisBottom(xScale));
-
-        // Ajout de l'axe Y
-        detailSvg.append("g").call(d3.axisLeft(yScale));
-
-        // Titre du graphique
-        detailSvg
-          .append("text")
-          .attr("x", 250)
-          .attr("y", -20)
-          .attr("text-anchor", "middle")
-          .style("font-size", "16px")
-          .text(`${member} - Sommeil du mois de ${month} (${year})`);
-
-        // Ajouter la légende de l'axe X (Jours)
-        detailSvg
-          .append("text")
-          .attr("x", 250)
-          .attr("y", 340)
-          .attr("text-anchor", "middle")
-          .style("font-size", "12px")
-          .text("Jours du mois");
-
-        // Ajouter la légende de l'axe Y (Heures de sommeil)
-        detailSvg
-          .append("text")
-          .attr("transform", "rotate(-90)")
-          .attr("x", -200)
-          .attr("y", -40)
-          .attr("text-anchor", "middle")
-          .style("font-size", "12px")
-          .text("Heures de sommeil moyen");
-
-        // Dessin des barres
-        detailSvg
-          .selectAll(".bar")
-          .data(dailyData)
-          .enter()
-          .append("rect")
-          .attr("x", (d) => xScale(d.day))
-          .attr("y", (d) => {
-            const value = d.value;
-            return value < 0 || value === null ? yScale(2) : yScale(value);
-          })
-          .attr("width", xScale.bandwidth())
-          .attr("height", (d) => {
-            const value = d.value;
-            return value < 0 || value === null
-              ? 300 - yScale(2)
-              : 300 - yScale(value);
-          })
-          .attr("fill", (d) => {
-            const value = d.value;
-            return value < 0 || value === null ? "lightgrey" : memberColor;
-          })
-          .on("mouseover", function (event, d) {
-            tooltip.transition().duration(200).style("opacity", 1);
-            tooltip.html(`
-                <div style="text-align: center;">
-                  <strong>Jour :</strong> ${d.day}<br>
-                  <strong>Sommeil :</strong> ${
-                    d.value === -1.0 || d.value === null
-                      ? "Pas de données"
-                      : d.value.toFixed(2)
-                  } heures
-                </div>
-            `);
-          })
-          .on("mousemove", function (event) {
-            tooltip
-              .style("left", event.pageX + 15 + "px") // Décalage pour positionner le tooltip
-              .style("top", event.pageY + 15 + "px");
-          })
-          .on("mouseout", function () {
-            tooltip.transition().duration(500).style("opacity", 0);
-          });
-      }
-
       // Initialisation de la visualisation
-      updateVisualization(years[3]);
-      
+      updateVisualization(2024);
     });
 }
 
+
+
 // Visu 5
 function renderSleepActivityVisualization() {
   fetch("../static/js/final_combined_with_all_data.json") // Adapter le chemin si nécessaire
-- 
GitLab