diff --git a/code/backend_reconnaissance/exemple_utilisation.py b/code/backend_reconnaissance/exemple_utilisation.py index e81d3f5..638f13e 100644 --- a/code/backend_reconnaissance/exemple_utilisation.py +++ b/code/backend_reconnaissance/exemple_utilisation.py @@ -10,7 +10,7 @@ avis = { "commentaire": "Commentaire" } -# res = requests.post("http://localhost:8080/add_review", data=avis) +res = requests.post("http://localhost:8080/add_review", data=avis) # print(res.text) #Exemple ajout d'un commentaire trouvé sur les réseaux sociaux @@ -25,5 +25,5 @@ avis = { "auteur_lien": "https://instagram.com/michel", } -res = requests.post("http://localhost:8080/add_social_review", data=avis) +# res = requests.post("http://localhost:8080/add_social_review", data=avis) print(res.text) \ No newline at end of file diff --git a/code/interface_admin/components/Avis.jsx b/code/interface_admin/components/Avis.jsx index 771bf33..b2583a0 100644 --- a/code/interface_admin/components/Avis.jsx +++ b/code/interface_admin/components/Avis.jsx @@ -3,7 +3,8 @@ import { Card, Col, Row, Table } from 'react-bootstrap'; import { BsPersonFill } from 'react-icons/bs'; import styles from '../styles/Avis.module.css' -export default function Avis({ age, sex, text, date, grade, gradeOther }) { +export default function Avis({review}) { + const {date, note_principale,notes_autres, commentaire, sexe_auteur, nom_source, age_auteur} = review; return ( Avis @@ -14,9 +15,10 @@ export default function Avis({ age, sex, text, date, grade, gradeOther }) { -

Age : {age}

-

Sexe : {sex}

+

Age : {age_auteur}

+

Sexe : {sexe_auteur}

Date de publication : {date}

+

Source : {nom_source}

@@ -31,12 +33,12 @@ export default function Avis({ age, sex, text, date, grade, gradeOther }) { Général - {grade} / 10 + {note_principale} / 10 - {gradeOther.map(({ title, grade }) => { - return - {title} - {grade}/10 + {notes_autres && notes_autres.map(({ critere, note }) => { + return + {critere} + {note}/10 })} @@ -46,7 +48,7 @@ export default function Avis({ age, sex, text, date, grade, gradeOther }) { Commentaire - {text} + {commentaire} diff --git a/code/interface_admin/components/AvisList.jsx b/code/interface_admin/components/AvisList.jsx index 0f13477..187f349 100644 --- a/code/interface_admin/components/AvisList.jsx +++ b/code/interface_admin/components/AvisList.jsx @@ -16,14 +16,16 @@ export default function AvisList({ avis }) { Date Note globale Commentaire + Source - {avis.map(({ id, grade, comment, date }) => { + {avis.map(({ id, note_principale, commentaire, date, nom_source }) => { return handleClick(id)} key={id} className={styles.row}> {date} - {grade} / 10 - {comment} + {note_principale} / 10 + {commentaire} + {nom_source} })} diff --git a/code/interface_admin/config/reviewsApi.js b/code/interface_admin/config/reviewsApi.js new file mode 100644 index 0000000..e7bbf98 --- /dev/null +++ b/code/interface_admin/config/reviewsApi.js @@ -0,0 +1,3 @@ +export const api = { + HOST: 'localhost:8080' +} \ No newline at end of file diff --git a/code/interface_admin/hooks/review.js b/code/interface_admin/hooks/review.js new file mode 100644 index 0000000..bcff35e --- /dev/null +++ b/code/interface_admin/hooks/review.js @@ -0,0 +1,32 @@ +import { useEffect, useState } from "react"; +import { api } from "../config/reviewsApi"; + + +function useReview(reviewId) { + const [review, setReview] = useState({}); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + async function fetchData(id) { + const response = await fetch('http://' + api.HOST + `/borne/get_review?id=${id}`) + if (response.ok) { + const jsonData = await response.json(); + setReview(jsonData); + setLoading(false); + setError(false); + } else { + setError(true); + setLoading(false); + } + } + + useEffect(() => { + if (reviewId) { + fetchData(reviewId); + } + }, [reviewId]) + + return { review, loading, error } +} + +export default useReview; \ No newline at end of file diff --git a/code/interface_admin/hooks/reviews.js b/code/interface_admin/hooks/reviews.js new file mode 100644 index 0000000..6f534ab --- /dev/null +++ b/code/interface_admin/hooks/reviews.js @@ -0,0 +1,30 @@ +import { useEffect, useState } from "react"; +import { api } from "../config/reviewsApi"; + +export default function useReviews() { + const [reviews, setReviews] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(false); + + async function fetchLastReviews(limit=100) { + setLoading(true); + const response = await fetch('http://' + api.HOST + '/borne/get_last_reviews', { + method: 'GET' + }) + if(response.ok) { + let json = await response.json() + setReviews(json); + setError(false); + setLoading(false); + }else { + setLoading(false); + setError(true); + } + } + + useEffect(() => { + fetchLastReviews(); + }, []) + + return {reviews, error, loading, fetchLastReviews}; +} \ No newline at end of file diff --git a/code/interface_admin/pages/avis/[id].js b/code/interface_admin/pages/avis/[id].js index 9300e4a..5f4d504 100644 --- a/code/interface_admin/pages/avis/[id].js +++ b/code/interface_admin/pages/avis/[id].js @@ -1,10 +1,12 @@ import { useRouter } from 'next/router'; import React from 'react' import Avis from '../../components/Avis'; +import useReview from '../../hooks/review'; export default function AvisPage() { const router = useRouter(); const {id} = router.query; + const {review, loading, error} = useReview(id); return ( - + !loading && !error && ) } diff --git a/code/interface_admin/pages/avis/index.js b/code/interface_admin/pages/avis/index.js index ca8de87..11b2a2c 100644 --- a/code/interface_admin/pages/avis/index.js +++ b/code/interface_admin/pages/avis/index.js @@ -1,11 +1,13 @@ import React, { useEffect, useState } from 'react' import { Card, Container, Form, Row, Table } from 'react-bootstrap' import AvisList from '../../components/AvisList'; +import useReviews from '../../hooks/reviews'; import styles from '../../styles/AvisListPage.module.css' export default function AvisListPage() { const [minGrade, setMinGrade] = useState(0); const [maxGrade, setMaxGrade] = useState(10); + const {reviews, error, loading} = useReviews(); useEffect(() => { if(minGrade > maxGrade) { @@ -18,12 +20,6 @@ export default function AvisListPage() { } }, [minGrade]) - const [avis, setAvis] = useState([ - { id: 0, date: "2023-02-11", grade: 10, comment: "lorem ipsum lorem ipsum" }, - { id: 1, date: "2023-02-11", grade: 10, comment: "Lorem ipsum lorem ipsum" }, - { id: 2, date: "2023-02-11", grade: 10, comment: "Lorem ipsum lorem ipsum" }, - { id: 3, date: "2023-02-11", grade: 10, comment: "Lorem ipsum lorem ipsum" } - ]) return ( @@ -57,7 +53,7 @@ export default function AvisListPage() { - + {!loading && !error && }
diff --git a/code/reviews_api/borne/get_handler.js b/code/reviews_api/borne/get_handler.js index 8f59ced..9323708 100644 --- a/code/reviews_api/borne/get_handler.js +++ b/code/reviews_api/borne/get_handler.js @@ -5,7 +5,7 @@ import conn from '../database.js'; * @param {Number} limit Nombre d'avis a afficher * @returns Une liste d'objets de la forme {id: id de l'avis, date: date de l'avis, note_principale: note sur 10, commentaire: avis textuel, nom_source, sexe_auteur, age_auteur} */ -const getLastReviews = (limit=10) => { +const getLastReviews = (limit = 10) => { return new Promise((resolve, reject) => { let sql = `SELECT borne_avis.id,date,note_principale,commentaire,sources.nom as nom_source, borne_auteurs.sexe as sexe_auteur, borne_auteurs.age as age_auteur FROM borne_avis @@ -13,9 +13,9 @@ const getLastReviews = (limit=10) => { JOIN borne_auteurs ON borne_auteurs.id = id_auteur ORDER BY borne_avis.id DESC LIMIT ?`; conn.query(sql, [limit], (err, res) => { - if(err) { + if (err) { reject(err); - }else { + } else { resolve(res) } }) @@ -29,19 +29,24 @@ const getLastReviews = (limit=10) => { */ const getReviewFromId = (id) => { return new Promise((resolve, reject) => { - let sql = `SELECT * FROM borne_avis WHERE id = ? LIMIT 1`; + let sql = `SELECT borne_avis.id,date,note_principale,commentaire,sources.nom as nom_source, borne_auteurs.sexe as sexe_auteur, borne_auteurs.age as age_auteur + FROM borne_avis + JOIN sources ON sources.id = source_id + JOIN borne_auteurs ON borne_auteurs.id = id_auteur + WHERE borne_avis.id = ? + LIMIT 1`; conn.query(sql, [id], (err, res) => { - if(err) { + if (err) { reject(err); - }else { - if(res.length != 1) { + } else { + if (res.length != 1) { reject(new Error("Avis avec cet ID non trouvé")) - }else { + } else { resolve(res[0]) } - if(res.length != 1) { + if (res.length != 1) { reject(new Error("Avis avec cet ID non trouvé")) - }else { + } else { resolve(res[0]) } } @@ -57,9 +62,9 @@ const getCriteres = () => { return new Promise((resolve, reject) => { let sql = `SELECT * FROM borne_criteres`; conn.query(sql, [limit], (err, res) => { - if(err) { + if (err) { reject(err); - }else { + } else { resolve(res) } }) @@ -72,17 +77,17 @@ const getCriteres = () => { * @param {Number} limit Nombre max de note a afficher * @returns une liste d'objets de la forme {id,date,critere,note,avis_id:id de l'avis lié a cette note} */ -const getNotesAutresFromCritere = (critere,limit=10) => { +const getNotesAutresFromCritere = (critere, limit = 10) => { return new Promise((resolve, reject) => { let sql = `SELECT borne_notes_autre.id as id,date,borne_criteres.nom as critere, note, avis_id FROM borne_notes_autre JOIN borne_criteres ON borne_criteres.id = critere_id WHERE borne_criteres.nom = ? ORDER BY borne_notes_autre.id DESC LIMIT ? ;`; - conn.query(sql, [critere,limit], (err, res) => { - if(err) { + conn.query(sql, [critere, limit], (err, res) => { + if (err) { reject(err); - }else { + } else { resolve(res) } }) @@ -102,9 +107,9 @@ const getNotesAutresFromReview = (reviewId) => { WHERE avis_id = ? ORDER BY borne_notes_autre.id DESC`; conn.query(sql, [reviewId], (err, res) => { - if(err) { + if (err) { reject(err); - }else { + } else { resolve(res) } }) @@ -117,17 +122,17 @@ const getNotesAutresFromReview = (reviewId) => { * @param {Number} limit Nombre max de stats a renvoyer * @returns Une liste d'objet ou chaque objet correspond a une stat sur une periode donnée (par ex une stat hebdo datée du 07/01 correspond a une stat sur la semaine du 01/01 au 07/01), ces objets sont de la forme */ -const getStats = (interval, limit=10) => { +const getStats = (interval, limit = 10) => { return new Promise((resolve, reject) => { - if(!["jour","mois","semaine","annee"].includes(interval)) { + if (!["jour", "mois", "semaine", "annee"].includes(interval)) { reject(new Error("Invalid time interval")); return; } let sql = `SELECT * FROM stats_general_${interval} ORDER BY id DESC LIMIT ?;`; conn.query(sql, [limit], (err, res) => { - if(err) { + if (err) { reject(err); - }else { + } else { resolve(res) } }) @@ -139,62 +144,64 @@ Ces fonction sont des handlers pour les routes express, elles sont appelées par */ export const handleGetLastReviews = (req, res) => { getLastReviews(req.query.limit) - .then((reviews) => { - res.send(reviews); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + .then((reviews) => { + res.send(reviews); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); } export const handleGetReview = (req, res) => { getReviewFromId(req.query.id) - .then((review) => { - res.send(review); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + .then((review) => { + getNotesAutresFromReview(req.query.id).then((notesAutres) => { + res.send({ ...review, notes_autres: notesAutres }); + }) + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); } export const handleGetCriteres = (req, res) => { getCriteres() - .then((criteres) => { - res.send(criteres); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); + .then((criteres) => { + res.send(criteres); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); } export const handleGetNotesAutres = (req, res) => { - if(req.query.critere) { + 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); - }); - }else if(req.query.id) { + .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) => { - res.send(notes); - }) - .catch((err) => { - res.status(500).send("Error: " + err.message); - }); - }else { + .then((notes) => { + res.send(notes); + }) + .catch((err) => { + res.status(500).send("Error: " + err.message); + }); + } else { res.status(500).send("Error: no critere or id specified"); } } 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); - }); + .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 31439ca..edbdf88 100644 --- a/code/reviews_api/index.js +++ b/code/reviews_api/index.js @@ -5,9 +5,11 @@ import { addReviewFromRequest } from './borne/post_handler.js'; import { addSocialReviewFromRequest } from './reseaux_sociaux/post_handler.js'; import { startCronJobs } from './stats/update_stats.js'; import * as borneHandler from './borne/get_handler.js'; +import cors from "cors"; const app = express(); app.use(bodyParser.urlencoded({extended:true})) +app.use(cors({origin:'*'})) dotenv.config() app.post('/add_review', (req,res) => addReviewFromRequest(req,res)); app.post('/add_social_review', (req,res) => addSocialReviewFromRequest(req,res)); diff --git a/code/reviews_api/package-lock.json b/code/reviews_api/package-lock.json index 5cb9427..e3d3ad2 100644 --- a/code/reviews_api/package-lock.json +++ b/code/reviews_api/package-lock.json @@ -10,6 +10,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.20.1", + "cors": "^2.8.5", "cron": "^2.1.0", "dotenv": "^16.0.3", "express": "^4.18.2", @@ -108,6 +109,18 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/cron": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cron/-/cron-2.1.0.tgz", @@ -491,6 +504,14 @@ "node": ">= 0.6" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", @@ -802,6 +823,15 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, "cron": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/cron/-/cron-2.1.0.tgz", @@ -1101,6 +1131,11 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==" }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==" + }, "object-inspect": { "version": "1.12.2", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz", diff --git a/code/reviews_api/package.json b/code/reviews_api/package.json index 708948d..904e1c6 100644 --- a/code/reviews_api/package.json +++ b/code/reviews_api/package.json @@ -11,6 +11,7 @@ "license": "ISC", "dependencies": { "body-parser": "^1.20.1", + "cors": "^2.8.5", "cron": "^2.1.0", "dotenv": "^16.0.3", "express": "^4.18.2",