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