From 8d22435b90ea51b6151affc59be40f0da6769df0 Mon Sep 17 00:00:00 2001 From: Damien Broqua Date: Wed, 31 Jan 2024 10:43:50 +0100 Subject: [PATCH] Added statistics page --- gulpfile.js | 1 + javascripts/mon-compte/index.js | 1 - package-lock.json | 17 +++ package.json | 1 + src/app.js | 2 + src/middleware/Albums.js | 125 +++++++++++++++++- src/routes/ma-collection.js | 17 +++ views/index.ejs | 9 +- .../mon-compte/ma-collection/statistiques.ejs | 122 +++++++++++++++++ 9 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 views/pages/mon-compte/ma-collection/statistiques.ejs diff --git a/gulpfile.js b/gulpfile.js index 8e76f38..fe1fe6f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -10,6 +10,7 @@ const babel = require("gulp-babel"); const sourceJs = "javascripts/**/*.js"; const sourceRemoteJS = [ "./node_modules/vue/dist/vue.global.prod.js", + "./node_modules/chart.js/dist/chart.umd.js", "./node_modules/axios/dist/axios.min.js", ]; diff --git a/javascripts/mon-compte/index.js b/javascripts/mon-compte/index.js index 2fb6f0f..6faa615 100644 --- a/javascripts/mon-compte/index.js +++ b/javascripts/mon-compte/index.js @@ -1,4 +1,3 @@ - if (typeof email !== "undefined" && typeof username !== "undefined") { Vue.createApp({ data() { diff --git a/package-lock.json b/package-lock.json index 1a49ca9..64fdc92 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15,6 +15,7 @@ "@babel/core": "^7.17.2", "@babel/preset-env": "^7.16.11", "axios": "^0.26.0", + "chart.js": "^4.4.1", "connect-ensure-login": "^0.1.1", "connect-flash": "^0.1.1", "connect-mongo": "^4.6.0", @@ -5257,6 +5258,11 @@ "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", "license": "MIT" }, + "node_modules/@kurkle/color": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.2.tgz", + "integrity": "sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==" + }, "node_modules/@mongodb-js/saslprep": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz", @@ -7913,6 +7919,17 @@ "node": ">=4" } }, + "node_modules/chart.js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.4.1.tgz", + "integrity": "sha512-C74QN1bxwV1v2PEujhmKjOZ7iUM4w6BWs23Md/6aOZZSlwMzeCIDGuZay++rBgChYru7/+QFeoQW0fQoP534Dg==", + "dependencies": { + "@kurkle/color": "^0.3.0" + }, + "engines": { + "pnpm": ">=7" + } + }, "node_modules/chokidar": { "version": "3.5.3", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", diff --git a/package.json b/package.json index e8815fb..690739a 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "@babel/core": "^7.17.2", "@babel/preset-env": "^7.16.11", "axios": "^0.26.0", + "chart.js": "^4.4.1", "connect-ensure-login": "^0.1.1", "connect-flash": "^0.1.1", "connect-mongo": "^4.6.0", diff --git a/src/app.js b/src/app.js index b293a37..e9fcc0b 100644 --- a/src/app.js +++ b/src/app.js @@ -150,4 +150,6 @@ app.use((error, req, res, next) => { } }); +console.log("Server ready!"); + export default app; diff --git a/src/middleware/Albums.js b/src/middleware/Albums.js index 59d6c5c..749de9a 100644 --- a/src/middleware/Albums.js +++ b/src/middleware/Albums.js @@ -162,6 +162,29 @@ Publié automatiquement via #musictopus`; return distincts; } + constructor(req, viewname) { + super(req, viewname); + + this.colors = { + nord0: "#2e3440", + nord1: "#3b4252", + nord2: "#434c5e", + nord3: "#4C566A", + nord4: "#d8dee9", + nord5: "#e5e9f0", + nord6: "#eceff4", + nord7: "#8fbcbb", + nord8: "#88c0d0", + nord9: "#81a1c1", + nord10: "#5e81ac", + nord11: "#d08770", + nord12: "#bf616a", + nord13: "#ebcb8b", + nord14: "#a3be8c", + nord15: "#b48ead", + }; + } + /** * Méthode permettant de récupérer la liste des albums d'une collection * @return {Object} @@ -500,6 +523,106 @@ Publié automatiquement via #musictopus`; await this.loadItem(); } + /** + * Méthode permettant d'afficher des statistiques au sujet de ma collection + */ + async statistics() { + const { _id: User } = this.req.user; + const top = {}; + const byGenres = {}; + const byStyles = {}; + const byFormats = {}; + const top10 = []; + + const albums = await AlbumsModel.find({ + User, + artists: { $exists: true, $not: { $size: 0 } }, + }); + + for (let i = 0; i < albums.length; i += 1) { + const currentFormats = []; + const { artists, genres, styles, formats } = albums[i]; + + // INFO: On regroupe les artistes par nom pour en faire le top10 + for (let j = 0; j < artists.length; j += 1) { + const { name } = artists[j]; + if (!top[name]) { + top[name] = { + name, + count: 0, + }; + } + top[name].count += 1; + } + + // INFO: On regroupe les genres + for (let j = 0; j < genres.length; j += 1) { + const name = genres[j]; + if (!byGenres[name]) { + byGenres[name] = { + name, + count: 0, + color: this.colors[ + `nord${Object.keys(byGenres).length % 15}` + ], + }; + } + + byGenres[name].count += 1; + } + + // INFO: On regroupe les styles + for (let j = 0; j < styles.length; j += 1) { + const name = styles[j]; + if (!byStyles[name]) { + byStyles[name] = { + name, + count: 0, + color: this.colors[ + `nord${Object.keys(byStyles).length % 15}` + ], + }; + } + + byStyles[name].count += 1; + } + + // INFO: On regroupe les formats + for (let j = 0; j < formats.length; j += 1) { + const { name } = formats[j]; + // INFO: On évite qu'un album avec 2 vinyles soit compté 2x + if (!currentFormats.includes(name)) { + if (!byFormats[name]) { + byFormats[name] = { + name, + count: 0, + color: this.colors[ + `nord${Object.keys(byFormats).length % 15}` + ], + }; + } + + byFormats[name].count += 1; + currentFormats.push(name); + } + } + } + + // INFO: On convertit le top en tableau + Object.keys(top).forEach((index) => { + top10.push(top[index]); + }); + + // INFO: On ordonne par quantité pour on récupère le top 10 + top10.sort((a, b) => (a.count > b.count ? -1 : 1)); + + this.setPageTitle("Mes statistiques"); + this.setPageContent("top10", top10.splice(0, 10)); + this.setPageContent("byGenres", byGenres); + this.setPageContent("byStyles", byStyles); + this.setPageContent("byFormats", byFormats); + } + /** * Méthode permettant de créer la page "collection/:userId" */ @@ -522,8 +645,8 @@ Publié automatiquement via #musictopus`; const genres = await Albums.getAllDistincts("genres", userId); const styles = await Albums.getAllDistincts("styles", userId); - this.setPageContent("username", user.username); this.setPageTitle(`Collection publique de ${user.username}`); + this.setPageContent("username", user.username); this.setPageContent("artists", artists); this.setPageContent("formats", formats); this.setPageContent("years", years); diff --git a/src/routes/ma-collection.js b/src/routes/ma-collection.js index 1100bd9..f7a4084 100644 --- a/src/routes/ma-collection.js +++ b/src/routes/ma-collection.js @@ -38,6 +38,23 @@ router } }); +router + .route("/statistiques") + .get(ensureLoggedIn("/connexion"), async (req, res, next) => { + try { + const page = new Albums( + req, + "mon-compte/ma-collection/statistiques" + ); + + await page.statistics(); + + render(res, page); + } catch (err) { + next(err); + } + }); + router .route("/exporter") .get(ensureLoggedIn("/connexion"), async (req, res, next) => { diff --git a/views/index.ejs b/views/index.ejs index 2fca1a8..f8048b4 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -16,6 +16,9 @@ + + + <% if ( config.matomoUrl ) { %> - diff --git a/views/pages/mon-compte/ma-collection/statistiques.ejs b/views/pages/mon-compte/ma-collection/statistiques.ejs new file mode 100644 index 0000000..dff7b83 --- /dev/null +++ b/views/pages/mon-compte/ma-collection/statistiques.ejs @@ -0,0 +1,122 @@ +
+

+ Mes statistiques +

+ +

Mon top 10

+ + + + + + + + + + <% for ( let i = 0 ; i < page.top10.length ; i += 1 ) { %> + + + + + + <% } %> + +
ArtisteAlbums
<%= i+1 %><%= page.top10[i].name %><%= page.top10[i].count %>
+ +
+
+

Genres

+ +
+
+

Styles

+ +
+
+

Formats

+ +
+
+
+ +