diff --git a/code/db/telereview.sql b/code/db/telereview.sql index 960a0ca..2ded268 100644 --- a/code/db/telereview.sql +++ b/code/db/telereview.sql @@ -116,8 +116,7 @@ CREATE TABLE `sources` ( INSERT INTO `sources` (`id`, `nom`) VALUES (1, 'website'), -(2, 'borne'), -(3, 'instagram'); +(2, 'borne'); -- -------------------------------------------------------- @@ -180,11 +179,12 @@ CREATE TABLE `stats_autres_semaine` ( CREATE TABLE `stats_general_annee` ( `id` int NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `moyenne_globale` float NOT NULL, - `moyenne_site` float NOT NULL, - `moyenne_borne` float NOT NULL, - `dist_age` text NOT NULL COMMENT 'Distribution de l''age des auteurs', - `dist_sexe` text NOT NULL COMMENT 'Distribution du sexe des auteurs' + `nb_avis` int NOT NULL, + `moyenne_globale` float DEFAULT NULL, + `moyenne_site` float DEFAULT NULL, + `moyenne_borne` float DEFAULT NULL, + `dist_age` text DEFAULT NULL COMMENT 'Distribution de l''age des auteurs', + `dist_sexe` text DEFAULT NULL COMMENT 'Distribution du sexe des auteurs' ) ; -- -------------------------------------------------------- @@ -196,11 +196,12 @@ CREATE TABLE `stats_general_annee` ( CREATE TABLE `stats_general_jour` ( `id` int NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, + `nb_avis` int NOT NULL, `moyenne_globale` float DEFAULT NULL, `moyenne_site` float DEFAULT NULL, `moyenne_borne` float DEFAULT NULL, - `dist_age` text COMMENT 'Distribution de l''age des auteurs', - `dist_sexe` text COMMENT 'Distribution du sexe des auteurs' + `dist_age` text DEFAULT NULL COMMENT 'Distribution de l''age des auteurs', + `dist_sexe` text DEFAULT NULL COMMENT 'Distribution du sexe des auteurs' ) ; -- -------------------------------------------------------- @@ -212,11 +213,12 @@ CREATE TABLE `stats_general_jour` ( CREATE TABLE `stats_general_mois` ( `id` int NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `moyenne_globale` float NOT NULL, - `moyenne_site` float NOT NULL, - `moyenne_borne` float NOT NULL, - `dist_age` text NOT NULL COMMENT 'Distribution de l''age des auteurs', - `dist_sexe` text NOT NULL COMMENT 'Distribution du sexe des auteurs' + `nb_avis` int NOT NULL, + `moyenne_globale` float DEFAULT NULL, + `moyenne_site` float DEFAULT NULL, + `moyenne_borne` float DEFAULT NULL, + `dist_age` text DEFAULT NULL COMMENT 'Distribution de l''age des auteurs', + `dist_sexe` text DEFAULT NULL COMMENT 'Distribution du sexe des auteurs' ) ; -- -------------------------------------------------------- @@ -228,11 +230,12 @@ CREATE TABLE `stats_general_mois` ( CREATE TABLE `stats_general_semaine` ( `id` int NOT NULL, `date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, - `moyenne_globale` float NOT NULL, - `moyenne_site` float NOT NULL, - `moyenne_borne` float NOT NULL, - `dist_age` text NOT NULL COMMENT 'Distribution de l''age des auteurs', - `dist_sexe` text NOT NULL COMMENT 'Distribution du sexe des auteurs' + `nb_avis` int NOT NULL, + `moyenne_globale` float DEFAULT NULL, + `moyenne_site` float DEFAULT NULL, + `moyenne_borne` float DEFAULT NULL, + `dist_age` text DEFAULT NULL COMMENT 'Distribution de l''age des auteurs', + `dist_sexe` text DEFAULT NULL COMMENT 'Distribution du sexe des auteurs' ) ; -- diff --git a/code/docker-compose.yaml b/code/docker-compose.yaml index 5b458b1..2e8b4dd 100644 --- a/code/docker-compose.yaml +++ b/code/docker-compose.yaml @@ -55,44 +55,44 @@ services: restart: always #Serveur web de l'interface de la borne - interface_borne: - image: httpd:latest - volumes: - - ./interface_borne:/usr/local/apache2/htdocs/ - container_name: interface_borne - ports: - - 8888:80 + # interface_borne: + # image: httpd:latest + # volumes: + # - ./interface_borne:/usr/local/apache2/htdocs/ + # container_name: interface_borne + # ports: + # - 8888:80 - #Serveur web de l'interface admin - interface_admin: - image: httpd:latest - volumes: - - ./interface_admin:/usr/local/apache2/htdocs/ - container_name: interface_admin - ports: - - 80:80 + # #Serveur web de l'interface admin + # interface_admin: + # image: httpd:latest + # volumes: + # - ./interface_admin:/usr/local/apache2/htdocs/ + # container_name: interface_admin + # ports: + # - 80:80 - #Backend de la borne : scripts pythons de reconnaissances video et audio - #Envoient les infos a l'interface de la borne par websocket pour mettre a jour l'interface rapidement - #Met a jour les avis en faisant des requêtes a l'API - backend_reconnaissance: - build: ./backend_reconnaissance - container_name: backend_reconnaissance - restart: always - devices: - - /dev/video3:/dev/video0 - environment: - - PORT=5000 - - HOST=backend_reconnaissance - ports: - #Ce container est le serveur websocker dont le client est l'interface de la borne qui tourne dans le navigateur - - 5000:5000 + # #Backend de la borne : scripts pythons de reconnaissances video et audio + # #Envoient les infos a l'interface de la borne par websocket pour mettre a jour l'interface rapidement + # #Met a jour les avis en faisant des requêtes a l'API + # backend_reconnaissance: + # build: ./backend_reconnaissance + # container_name: backend_reconnaissance + # restart: always + # devices: + # - /dev/video3:/dev/video0 + # environment: + # - PORT=5000 + # - HOST=backend_reconnaissance + # ports: + # #Ce container est le serveur websocker dont le client est l'interface de la borne qui tourne dans le navigateur + # - 5000:5000 - video_loopback: - build: ./video_loopback - container_name: video_loopback - restart: always - devices: - - /dev/video0:/dev/video0 - - /dev/video2:/dev/video1 - - /dev/video3:/dev/video2 \ No newline at end of file + # video_loopback: + # build: ./video_loopback + # container_name: video_loopback + # restart: always + # devices: + # - /dev/video0:/dev/video0 + # - /dev/video2:/dev/video1 + # - /dev/video3:/dev/video2 \ No newline at end of file diff --git a/code/interface_admin/hooks/stats.js b/code/interface_admin/hooks/stats.js index 40449bb..4b630ce 100644 --- a/code/interface_admin/hooks/stats.js +++ b/code/interface_admin/hooks/stats.js @@ -1,4 +1,4 @@ -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import { api } from "../config/reviewsApi"; export default function useStats(limit, interval) { @@ -7,7 +7,7 @@ export default function useStats(limit, interval) { const [error, setError] = useState(false); async function fetchData(limit, interval) { - const response = await fetch("http://" + api.HOST + `/get_stats?interval=${interval}&limit=${limit}`) + const response = await fetch("http://" + api.HOST + `/borne/get_stats?interval=${interval}&limit=${limit}`) if(response.ok) { const data = await response.json(); setStats(data); @@ -20,7 +20,7 @@ export default function useStats(limit, interval) { useEffect(() => { fetchData(limit, interval); - }) + }, []) return {stats, loading, error}; } \ No newline at end of file diff --git a/code/interface_admin/package.json b/code/interface_admin/package.json index 1bc8b7a..d100d6f 100644 --- a/code/interface_admin/package.json +++ b/code/interface_admin/package.json @@ -12,6 +12,7 @@ "@next/font": "13.1.6", "bootstrap": "^5.2.3", "chart.js": "^4.2.0", + "date-fns": "^2.29.3", "eslint": "8.33.0", "eslint-config-next": "13.1.6", "next": "13.1.6", diff --git a/code/interface_admin/pages/avis/index.js b/code/interface_admin/pages/avis/index.js index 8044f84..4ff8a39 100644 --- a/code/interface_admin/pages/avis/index.js +++ b/code/interface_admin/pages/avis/index.js @@ -57,8 +57,8 @@ export default function AvisListPage() {
Min : {minGrade}/10
- setMinGrade(e.target.value)} min="0" max="10" step="1" className={styles.slider}> - setMaxGrade(e.target.value)} min="0" max="10" step="1" className={styles.slider}> + setMinGrade(Number(e.target.value))} min="0" max="10" step="1" className={styles.slider}> + setMaxGrade(Number(e.target.value))} min="0" max="10" step="1" className={styles.slider}>
Max : {maxGrade}/10
diff --git a/code/interface_admin/pages/index.js b/code/interface_admin/pages/index.js index 8310496..553c24b 100644 --- a/code/interface_admin/pages/index.js +++ b/code/interface_admin/pages/index.js @@ -3,74 +3,132 @@ import { Card, Container } from 'react-bootstrap' import ComparativeBarChart from '../components/ComparativeBarChart' import { useEffect, useState } from 'react' import styles from "../styles/Home.module.css" +import useStats from '../hooks/stats' +import getDay from 'date-fns/getDay' +import getWeek from '../util' export default function Home() { const [datasets, setDatasets] = useState(null); const [averages, setAverages] = useState(null); const [differences, setDifferences] = useState(null); + useEffect(() => { if (datasets) { let newAverages = [] let newDifferences = [] for (let i = 0; i < datasets.length; i++) { - newAverages[i] = datasets[i].current.reduce((a, b) => a + b) / datasets[i].current.length - newDifferences[i] = newAverages[i] - datasets[i].previous.reduce((a, b) => a + b) / datasets[i].previous.length + let currentEntriesCount = 0; + let previousEntriesCount = 0; + for(let x of datasets[i].current) { + if(x != null) { + currentEntriesCount++; + } + } + for(let x of datasets[i].previous) { + if(x != null) { + previousEntriesCount++; + } + } + + if(currentEntriesCount != 0) { + newAverages[i] = datasets[i].current.reduce((a, b) => a + b) / currentEntriesCount; + if(previousEntriesCount > 0) { + newDifferences[i] = newAverages[i] - datasets[i].previous.reduce((a, b) => a + b) / datasets[i].previous.length + }else { + newDifferences[i] = newAverages[i] + } + }else { + newDifferences[i] = 0; + newAverages[i] = 0; + } } setAverages(newAverages); setDifferences(newDifferences); } }, [datasets]); - - useEffect(() => setDatasets([ - { title: "Nombre d'avis", current: [3, 2, 3, 4, 5, 6, 7], previous: [7, 6, 5, 4, 3, 2, 1] }, - { title: "Notes moyennes", current: [1, 2, 3, 4, 5, 6, 7], previous: [7, 6, 5, 4, 3, 2, 1] } - ]), []); + + const { stats, loading, error } = useStats(14, "jour"); + + useEffect(() => { + console.log(getDay(new Date("2023-03-20T14:00:00.000Z"))) + }) + + useEffect(() => { + if(!error && !loading) { + let reviewCount = [null,null,null,null,null,null,null]; + let reviewCountPrev = [null,null,null,null,null,null,null] + let reviewAvg = [null,null,null,null,null,null,null] + let reviewAvgPrev = [null,null,null,null,null,null,null] + + for(let i = 0; i < stats.length; i++) { + let date = new Date(Date.parse(stats[i].date)) + let now = new Date(); + let day = (date.getDay() - 1) % 7; + let week = getWeek(date, 1); + let thisWeek = getWeek(now, 1); + console.log({date,week,thisWeek,day}) + if(week == thisWeek) { + reviewCount[day] = stats[i].nb_avis; + reviewAvg[day] = stats[i].moyenne_globale; + }else if(week = thisWeek - 1){ + reviewAvgPrev[day] = stats[i].moyenne_globale; + reviewCountPrev[day] = stats[i].nb_avis; + } + } + setDatasets([ + { title: "Nombre d'avis", current: reviewCount, previous: reviewCountPrev }, + { title: "Notes moyennes", current: reviewAvg, previous: reviewAvgPrev } + ]) + } + }, [stats]); + function dataVisualizer(title, current, previous, average, difference) { return
-

{title}

- - Moyenne - -
- {Math.round(average * 1e2) / 1e2} -
-
= 0 ? styles.averagePositive : styles.averageNegative].join(' ')} - > - {(difference >= 0 ? "+" : "-") + Math.round(difference * 1e2) / 1e2} -
-
-
- -
+

{title}

+ + Moyenne + +
+ {Math.round(average * 1e2) / 1e2} +
+
= 0 ? styles.averagePositive : styles.averageNegative].join(' ')} + > + {(difference >= 0 ? "+" : "-") + Math.round(difference * 1e2) / 1e2} +
+
+
+ +
} - + return ( <> - - Create Next App - - - - - Vos performances cette semaine - - {datasets && averages && differences && datasets.map((set, i) => dataVisualizer(set.title, set.current, set.previous, averages[i], differences[i]))} - -
-
-
-
+ + Create Next App + + + + + Vos performances cette semaine + + {datasets && averages && differences && datasets.map((set, i) => dataVisualizer(set.title, set.current, set.previous, averages[i], differences[i]))} + +
+
+
+
- ) -} + ) + } + \ No newline at end of file diff --git a/code/interface_admin/util.js b/code/interface_admin/util.js new file mode 100644 index 0000000..3018508 --- /dev/null +++ b/code/interface_admin/util.js @@ -0,0 +1,34 @@ +/** + * Returns the week number for this date. dowOffset is the day of week the week + * "starts" on for your locale - it can be from 0 to 6. If dowOffset is 1 (Monday), + * the week returned is the ISO 8601 week number. + * @param int dowOffset + * @return int + */ +export default function getWeek (date,dowOffset) { +/*getWeek() was developed by Nick Baicoianu at MeanFreePath: http://www.meanfreepath.com */ + + dowOffset = typeof(dowOffset) == 'number' ? dowOffset : 0; //default dowOffset to zero + var newYear = new Date(date.getFullYear(),0,1); + var day = newYear.getDay() - dowOffset; //the day of week the year begins on + day = (day >= 0 ? day : day + 7); + var daynum = Math.floor((date.getTime() - newYear.getTime() - + (date.getTimezoneOffset()-newYear.getTimezoneOffset())*60000)/86400000) + 1; + var weeknum; + //if the year starts before the middle of a week + if(day < 4) { + weeknum = Math.floor((daynum+day-1)/7) + 1; + if(weeknum > 52) { + nYear = new Date(date.getFullYear() + 1,0,1); + nday = nYear.getDay() - dowOffset; + nday = nday >= 0 ? nday : nday + 7; + /*if the next year starts before the middle of + the week, it is week #1 of that year*/ + weeknum = nday < 4 ? 1 : 53; + } + } + else { + weeknum = Math.floor((daynum+day-1)/7); + } + return weeknum; +}; \ No newline at end of file diff --git a/code/reviews_api/borne/get_handler.js b/code/reviews_api/borne/get_handler.js index 9323708..ba43329 100644 --- a/code/reviews_api/borne/get_handler.js +++ b/code/reviews_api/borne/get_handler.js @@ -143,13 +143,23 @@ const getStats = (interval, limit = 10) => { Ces fonction sont des handlers pour les routes express, elles sont appelées par les routes et renvoient les données au format JSON */ export const handleGetLastReviews = (req, res) => { - getLastReviews(req.query.limit) - .then((reviews) => { - res.send(reviews); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + if (req.query.limit) { + getLastReviews(Number(req.query.limit)) + .then((reviews) => { + res.send(reviews); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } else { + getLastReviews() + .then((reviews) => { + res.send(reviews); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } } export const handleGetReview = (req, res) => { @@ -176,13 +186,23 @@ export const handleGetCriteres = (req, res) => { export const handleGetNotesAutres = (req, res) => { if (req.query.critere) { - getNotesAutresFromCritere(req.query.critere, req.query.limit) - .then((notes) => { - res.send(notes); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + if (req.query.limit) { + getNotesAutresFromCritere(req.query.critere, Number(req.query.limit)) + .then((notes) => { + res.send(notes); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } else { + getNotesAutresFromCritere(req.query.critere) + .then((notes) => { + res.send(notes); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } } else if (req.query.id) { getNotesAutresFromReview(req.query.id) .then((notes) => { @@ -197,11 +217,21 @@ export const handleGetNotesAutres = (req, res) => { } export const handleGetStats = (req, res) => { - getStats(req.query.interval, req.query.limit) - .then((stats) => { - res.send(stats); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + if (req.query.limit) { + getStats(req.query.interval, Number(req.query.limit)) + .then((stats) => { + res.send(stats); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } else { + getStats(req.query.interval) + .then((stats) => { + res.send(stats); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } } \ No newline at end of file diff --git a/code/reviews_api/index.js b/code/reviews_api/index.js index edbdf88..5d2a760 100644 --- a/code/reviews_api/index.js +++ b/code/reviews_api/index.js @@ -3,7 +3,7 @@ import express from 'express'; import bodyParser from 'body-parser'; import { addReviewFromRequest } from './borne/post_handler.js'; import { addSocialReviewFromRequest } from './reseaux_sociaux/post_handler.js'; -import { startCronJobs } from './stats/update_stats.js'; +import { startCronJobs, manualUpdateStats } from './stats/update_stats.js'; import * as borneHandler from './borne/get_handler.js'; import cors from "cors"; @@ -20,6 +20,10 @@ app.get('/borne/get_criteres', borneHandler.handleGetCriteres); app.get('/borne/notes_autres', borneHandler.handleGetNotesAutres); app.get('/borne/get_stats', borneHandler.handleGetStats); +app.get('/update_stats', (req, res) => { + manualUpdateStats(); + res.send("OK"); +}) startCronJobs(); diff --git a/code/reviews_api/stats/update_request.sql b/code/reviews_api/stats/update_request.sql index bc728b5..b5ea802 100644 --- a/code/reviews_api/stats/update_request.sql +++ b/code/reviews_api/stats/update_request.sql @@ -8,6 +8,10 @@ SET @date_limite = DATE_ADD(NOW(), INTERVAL -DAY_COUNT_DELAY DAY); On récupère les notes notes moyennes sur la periode, en séparant global, borne et site */ +SELECT @nb_avis:=COUNT(*) + FROM borne_avis + WHERE borne_avis.date > @date_limite; + SELECT @moyenne_globale:=AVG(note_principale) FROM borne_avis WHERE borne_avis.date > @date_limite; @@ -38,7 +42,7 @@ SELECT @stats_a:=COUNT(*) FROM borne_avis SET @dist_sexe = CONCAT(@stats_f,",",@stats_h,",",@stats_a); -INSERT INTO STATS_GENERAL_TABLE_NAME (moyenne_globale, moyenne_borne, moyenne_site, dist_sexe) VALUES (@moyenne_globale, @moyenne_borne, @moyenne_site, @dist_sexe); +INSERT INTO STATS_GENERAL_TABLE_NAME (moyenne_globale, nb_avis, moyenne_borne, moyenne_site, dist_sexe) VALUES (@moyenne_globale, @nb_avis, @moyenne_borne, @moyenne_site, @dist_sexe); INSERT INTO STATS_AUTRES_TABLE_NAME (critere_id, note) SELECT critere_id, AVG(note) as moyenne FROM borne_notes_autre diff --git a/code/reviews_api/stats/update_stats.js b/code/reviews_api/stats/update_stats.js index 6bcc30a..2c20668 100644 --- a/code/reviews_api/stats/update_stats.js +++ b/code/reviews_api/stats/update_stats.js @@ -66,4 +66,11 @@ export const startCronJobs = () => { true ) console.log("All cronjobs initiated") +} + +export function manualUpdateStats() { + computeStats(1, "stats_general_jour", "stats_autres_jour"); + computeStats(7, "stats_general_semaine", "stats_autres_jour"); + computeStats(30, "stats_general_mois", "stats_autres_mois"); + computeStats(365, "stats_general_annee", "stats_autres_annee"); } \ No newline at end of file