From 332b5ecd34824c2aed448b6cdc6fea0becbfa4a7 Mon Sep 17 00:00:00 2001
From: p2312013 <amira.rabehi@etu.univ-lyon1.fr>
Date: Sun, 12 Jan 2025 13:02:02 +0100
Subject: [PATCH] integration visu 4

---
 frontend/index.html  |  25 ++++++++
 static/css/style.css |  68 +++++++++++++++++++++
 static/js/main.js    | 138 ++++++++++++++++++++++++++++++++++++++++++-
 3 files changed, 230 insertions(+), 1 deletion(-)

diff --git a/frontend/index.html b/frontend/index.html
index a23ef04..0e0e854 100644
--- a/frontend/index.html
+++ b/frontend/index.html
@@ -162,6 +162,31 @@
     </div>
   </div>
 </section>
+<section id="sleep-analysis" class="details">
+  <div class="container">
+    <div class="row align-items-center">
+      <!-- Texte à gauche -->
+      <div class="col-md-6" data-aos="fade-right">
+        <h3>Analyse des Heures de Sommeil</h3>
+        <p>
+          Cette visualisation met en évidence les heures de sommeil moyennes par mois pour chaque utilisateur.
+          Utilisez le slider pour explorer les données par année et visualiser les tendances individuelles.
+        </p>
+        <ul>
+          <li><i class="bi bi-check"></i> Analyse des heures de sommeil moyen par mois et par année.</li>
+          <li><i class="bi bi-check"></i> Comparaison entre utilisateurs.</li>
+          <li><i class="bi bi-check"></i> Détails interactifs par jour pour un mois donné.</li>
+        </ul>
+      </div>
+
+      <!-- Visualisation à droite -->
+      <div class="col-md-6">
+        <div id="sleep-visualization" style="position: relative;"></div>
+        <div id="slider-container" style="margin-top: 20px;"></div>
+      </div>
+    </div>
+  </div>
+</section>
 
 
     <div class="container">
diff --git a/static/css/style.css b/static/css/style.css
index 1f68cc1..e02522e 100644
--- a/static/css/style.css
+++ b/static/css/style.css
@@ -1987,3 +1987,71 @@ button:hover {
   font-size: 14px; /* Police adaptée */
   z-index: 10;
 }
+/* Visu 4 */
+/* Visu 4 : Analyse du Sommeil */
+#sleep-analysis {
+  width: 100%;
+  height: auto;
+  padding: 20px;
+  border-radius: 8px;
+  overflow: hidden;
+  margin-bottom: 30px; /* Espacement entre les sections */
+
+}
+
+#visualization {
+  width: 100%;
+  height: auto;
+  padding: 10px;
+  border-radius: 8px;
+
+  overflow: hidden;
+}
+
+#slider-container {
+  margin-top: 15px;
+  text-align: center;
+}
+
+.details .row {
+  align-items: center; /* Alignement vertical des colonnes */
+  margin: 0;
+}
+
+#sleep-analysis h3 {
+  font-size: 24px;
+  font-weight: bold;
+  margin-bottom: 15px;
+  color: #333333;
+}
+
+#sleep-analysis p {
+  font-size: 16px;
+  line-height: 1.6;
+  color: #555555;
+}
+
+#sleep-analysis ul {
+  padding-left: 20px;
+  list-style-type: none; /* Supprimez les puces */
+}
+
+#sleep-analysis ul li {
+  margin-bottom: 10px;
+}
+
+#sleep-analysis ul li i {
+  color: #0f7e06; /* Couleur verte pour les icônes */
+  margin-right: 10px;
+}
+
+#tooltip {
+  position: absolute;
+  background-color: rgba(0, 0, 0, 0.8);
+  color: white;
+  padding: 8px;
+  border-radius: 5px;
+  font-size: 12px;
+  pointer-events: none;
+  display: none;
+}
diff --git a/static/js/main.js b/static/js/main.js
index c89dfa7..a266215 100644
--- a/static/js/main.js
+++ b/static/js/main.js
@@ -626,8 +626,144 @@ function renderCaloriesVisualization() {
       });
     });
 }
+// Visu4
+function renderSleepVisualization() {
+  fetch('../static/js/final_combined_with_all_data.json') // Adapter le chemin si nécessaire
+      .then((response) => response.json())
+      .then((data) => {
+          const parseDate = d3.timeParse("%Y-%m-%d");
+          const formatYear = d3.timeFormat("%Y");
+          const formatMonth = d3.timeFormat("%Y-%m");
+          const formatDay = d3.timeFormat("%d");
+          const members = ["Corentin", "Maya", "Anis", "Amira"];
+
+          data.forEach(d => d.date = parseDate(d.date));
+
+          const margin = { top: 50, right: 230, bottom: 150, left: 70 };
+          const width = 800 - margin.left - margin.right;
+          const height = 500 - margin.top - margin.bottom;
+
+          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})`);
+
+          const years = [2021, 2022, 2023, 2024];
+          const sliderContainer = d3.select("#sleep-slider-container");
+
+          const rangeSlider = sliderContainer.append("input")
+              .attr("type", "range")
+              .attr("min", 0)
+              .attr("max", years.length - 1)
+              .attr("value", years.indexOf(2024))
+              .style("width", "60%");
+
+          const yearDisplay = sliderContainer.append("span")
+              .text(years[years.indexOf(2024)]);
+
+          const tooltip = d3.select("body").append("div")
+              .attr("class", "tooltip")
+              .style("opacity", 0);
+
+          function updateVisualization(selectedYear) {
+              const filteredData = data.filter(d => formatYear(d.date) === selectedYear.toString());
+              const groupedData = d3.groups(filteredData, d => formatMonth(d.date));
+
+              const colorMap = {
+                  "Maya": "#0f7e06",
+                  "Corentin": "#1d38e3",
+                  "Anis": "#d6bff4",
+                  "Amira": "#7e09bd"
+              };
+
+              const aggregatedData = groupedData.map(([month, records]) => {
+                  const aggregated = { month };
+                  members.forEach(member => {
+                      aggregated[`Sleep_${member}`] = d3.mean(records, d => d[`Sleep_${member}`] || 0);
+                  });
+                  return aggregated;
+              });
+
+              svg.selectAll("*").remove();
+
+              svg.append("text")
+                  .attr("x", width / 2)
+                  .attr("y", margin.top - 60)
+                  .attr("text-anchor", "middle")
+                  .style("font-size", "16px")
+                  .style("font-weight", "bold")
+                  .text("Analyse des heures de sommeil par mois");
+
+              const xScale = d3.scaleBand()
+                  .domain(aggregatedData.map(d => d.month))
+                  .range([0, width])
+                  .padding(0.2);
+
+              const yScale = d3.scaleLinear()
+                  .domain([0, d3.max(aggregatedData, d => Math.max(...members.map(member => d[`Sleep_${member}`])))]).nice()
+                  .range([height, 0]);
+
+              svg.append("g")
+                  .attr("transform", `translate(0, ${height})`)
+                  .call(d3.axisBottom(xScale))
+                  .selectAll("text")
+                  .attr("transform", "rotate(-45)")
+                  .style("text-anchor", "end");
+
+              svg.append("text")
+                  .attr("x", width / 2)
+                  .attr("y", height + 70)
+                  .attr("text-anchor", "middle")
+                  .style("font-size", "14px")
+                  .text("Mois");
+
+              svg.append("g").call(d3.axisLeft(yScale));
+
+              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)");
+
+              members.forEach((member, i) => {
+                  svg.selectAll(`.bar-sleep-${member}`)
+                      .data(aggregatedData)
+                      .enter()
+                      .append("rect")
+                      .attr("x", d => xScale(d.month) + i * (xScale.bandwidth() / members.length))
+                      .attr("y", d => yScale(d[`Sleep_${member}`]))
+                      .attr("width", xScale.bandwidth() / members.length)
+                      .attr("height", d => height - yScale(d[`Sleep_${member}`]))
+                      .attr("fill", colorMap[member])
+                      .on("mouseover", function (event, d) {
+                          tooltip.transition().duration(200).style("opacity", 0.9);
+                          tooltip.html(`Mois : ${d.month}<br>Sommeil : ${d[`Sleep_${member}`].toFixed(2)} heures`)
+                              .style("left", (event.pageX + 5) + "px")
+                              .style("top", (event.pageY - 28) + "px");
+                      })
+                      .on("mouseout", function () {
+                          tooltip.transition().duration(500).style("opacity", 0);
+                      });
+              });
+          }
+
+          updateVisualization(years[3]);
+
+          rangeSlider.on("input", function () {
+              const selectedYear = years[this.value];
+              yearDisplay.text(selectedYear);
+              updateVisualization(selectedYear);
+          });
+      });
+}
+
 document.addEventListener("DOMContentLoaded", function () {
   renderStepsVisualization();
   renderDistanceVisualization();
   renderCaloriesVisualization();
-});
\ No newline at end of file
+  renderSleepVisualization();
+});
-- 
GitLab