Added statistics page
This commit is contained in:
parent
30bd3ebdf9
commit
8d22435b90
9 changed files with 290 additions and 5 deletions
|
@ -10,6 +10,7 @@ const babel = require("gulp-babel");
|
||||||
const sourceJs = "javascripts/**/*.js";
|
const sourceJs = "javascripts/**/*.js";
|
||||||
const sourceRemoteJS = [
|
const sourceRemoteJS = [
|
||||||
"./node_modules/vue/dist/vue.global.prod.js",
|
"./node_modules/vue/dist/vue.global.prod.js",
|
||||||
|
"./node_modules/chart.js/dist/chart.umd.js",
|
||||||
"./node_modules/axios/dist/axios.min.js",
|
"./node_modules/axios/dist/axios.min.js",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
if (typeof email !== "undefined" && typeof username !== "undefined") {
|
if (typeof email !== "undefined" && typeof username !== "undefined") {
|
||||||
Vue.createApp({
|
Vue.createApp({
|
||||||
data() {
|
data() {
|
||||||
|
|
17
package-lock.json
generated
17
package-lock.json
generated
|
@ -15,6 +15,7 @@
|
||||||
"@babel/core": "^7.17.2",
|
"@babel/core": "^7.17.2",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"axios": "^0.26.0",
|
"axios": "^0.26.0",
|
||||||
|
"chart.js": "^4.4.1",
|
||||||
"connect-ensure-login": "^0.1.1",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
|
@ -5257,6 +5258,11 @@
|
||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/@mongodb-js/saslprep": {
|
||||||
"version": "1.1.4",
|
"version": "1.1.4",
|
||||||
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz",
|
"resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.4.tgz",
|
||||||
|
@ -7913,6 +7919,17 @@
|
||||||
"node": ">=4"
|
"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": {
|
"node_modules/chokidar": {
|
||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
|
|
|
@ -45,6 +45,7 @@
|
||||||
"@babel/core": "^7.17.2",
|
"@babel/core": "^7.17.2",
|
||||||
"@babel/preset-env": "^7.16.11",
|
"@babel/preset-env": "^7.16.11",
|
||||||
"axios": "^0.26.0",
|
"axios": "^0.26.0",
|
||||||
|
"chart.js": "^4.4.1",
|
||||||
"connect-ensure-login": "^0.1.1",
|
"connect-ensure-login": "^0.1.1",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"connect-mongo": "^4.6.0",
|
"connect-mongo": "^4.6.0",
|
||||||
|
|
|
@ -150,4 +150,6 @@ app.use((error, req, res, next) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
console.log("Server ready!");
|
||||||
|
|
||||||
export default app;
|
export default app;
|
||||||
|
|
|
@ -162,6 +162,29 @@ Publié automatiquement via #musictopus`;
|
||||||
return distincts;
|
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
|
* Méthode permettant de récupérer la liste des albums d'une collection
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
|
@ -500,6 +523,106 @@ Publié automatiquement via #musictopus`;
|
||||||
await this.loadItem();
|
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"
|
* 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 genres = await Albums.getAllDistincts("genres", userId);
|
||||||
const styles = await Albums.getAllDistincts("styles", userId);
|
const styles = await Albums.getAllDistincts("styles", userId);
|
||||||
|
|
||||||
this.setPageContent("username", user.username);
|
|
||||||
this.setPageTitle(`Collection publique de ${user.username}`);
|
this.setPageTitle(`Collection publique de ${user.username}`);
|
||||||
|
this.setPageContent("username", user.username);
|
||||||
this.setPageContent("artists", artists);
|
this.setPageContent("artists", artists);
|
||||||
this.setPageContent("formats", formats);
|
this.setPageContent("formats", formats);
|
||||||
this.setPageContent("years", years);
|
this.setPageContent("years", years);
|
||||||
|
|
|
@ -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
|
router
|
||||||
.route("/exporter")
|
.route("/exporter")
|
||||||
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
.get(ensureLoggedIn("/connexion"), async (req, res, next) => {
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
<link href="/css/main.css" rel="stylesheet" />
|
<link href="/css/main.css" rel="stylesheet" />
|
||||||
|
|
||||||
|
<script src="/js/libs.js"></script>
|
||||||
|
<script defer src="/js/main.js"></script>
|
||||||
|
|
||||||
<% if ( config.matomoUrl ) { %>
|
<% if ( config.matomoUrl ) { %>
|
||||||
<!-- Matomo -->
|
<!-- Matomo -->
|
||||||
<script>
|
<script>
|
||||||
|
@ -90,6 +93,9 @@
|
||||||
<a class="navbar-item" href="/ma-collection/on-air">
|
<a class="navbar-item" href="/ma-collection/on-air">
|
||||||
On air
|
On air
|
||||||
</a>
|
</a>
|
||||||
|
<a class="navbar-item" href="/ma-collection/statistiques">
|
||||||
|
Statistiques
|
||||||
|
</a>
|
||||||
<a class="navbar-item" href="/ma-collection/exporter">
|
<a class="navbar-item" href="/ma-collection/exporter">
|
||||||
Exporter ma collection
|
Exporter ma collection
|
||||||
</a>
|
</a>
|
||||||
|
@ -187,8 +193,5 @@
|
||||||
Fait avec ❤️ à Bordeaux.
|
Fait avec ❤️ à Bordeaux.
|
||||||
</p>
|
</p>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
<script defer src="/js/libs.js"></script>
|
|
||||||
<script defer src="/js/main.js"></script>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
122
views/pages/mon-compte/ma-collection/statistiques.ejs
Normal file
122
views/pages/mon-compte/ma-collection/statistiques.ejs
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
<main class="layout-maxed ma-collection-details" id="ma-collection-statistiques">
|
||||||
|
<h1>
|
||||||
|
Mes statistiques
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<h2>Mon top 10</h2>
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style="width: 40px;"></th>
|
||||||
|
<th>Artiste</th>
|
||||||
|
<th style="width: 80px;">Albums</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<% for ( let i = 0 ; i < page.top10.length ; i += 1 ) { %>
|
||||||
|
<tr>
|
||||||
|
<td><%= i+1 %></td>
|
||||||
|
<td><%= page.top10[i].name %></td>
|
||||||
|
<td><%= page.top10[i].count %></td>
|
||||||
|
</tr>
|
||||||
|
<% } %>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="grid gap-10 grid-cols-1 md:grid-cols-2">
|
||||||
|
<div>
|
||||||
|
<h2>Genres</h2>
|
||||||
|
<canvas id="byGenres"></canvas>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Styles</h2>
|
||||||
|
<canvas id="byStyles"></canvas>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<h2>Formats</h2>
|
||||||
|
<canvas id="byFormats"></canvas>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
const byGenres = <%- JSON.stringify(page.byGenres) %>;
|
||||||
|
const byStyles = <%- JSON.stringify(page.byStyles) %>;
|
||||||
|
const byFormats = <%- JSON.stringify(page.byFormats) %>;
|
||||||
|
|
||||||
|
const ctxGenres= document.getElementById('byGenres');
|
||||||
|
const ctxStyles = document.getElementById('byStyles');
|
||||||
|
const ctxFormats = document.getElementById('byFormats');
|
||||||
|
|
||||||
|
new Chart(ctxGenres, {
|
||||||
|
type: 'pie',
|
||||||
|
data: {
|
||||||
|
labels: Object.keys(byGenres).map((index) => {return byGenres[index].name}),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: Object.keys(byGenres).map((index) => {return byGenres[index].color}),
|
||||||
|
data: Object.keys(byGenres).map((index) => {return byGenres[index].count}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new Chart(ctxStyles, {
|
||||||
|
type: 'pie',
|
||||||
|
data: {
|
||||||
|
labels: Object.keys(byStyles).map((index) => {return byStyles[index].name}),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: Object.keys(byStyles).map((index) => {return byStyles[index].color}),
|
||||||
|
data: Object.keys(byStyles).map((index) => {return byStyles[index].count}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
new Chart(ctxFormats, {
|
||||||
|
type: 'pie',
|
||||||
|
data: {
|
||||||
|
labels: Object.keys(byFormats).map((index) => {return byFormats[index].name}),
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
backgroundColor: Object.keys(byFormats).map((index) => {return byFormats[index].color}),
|
||||||
|
data: Object.keys(byFormats).map((index) => {return byFormats[index].count}),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
Loading…
Reference in a new issue