diff --git a/src/web/index.html b/src/web/index.html
index 09341b2..33673d4 100644
--- a/src/web/index.html
+++ b/src/web/index.html
@@ -1,20 +1,44 @@
- Spoti Stats
+
+
+
+
+
+
+
+
+
+
+
+
+ SpotiStats
-
-
-
-
-
+ Map of genre popularity
+
+
+ Charts
+
+
+
+
diff --git a/src/web/script.js b/src/web/script.js
index d59447f..f3258ee 100644
--- a/src/web/script.js
+++ b/src/web/script.js
@@ -3,59 +3,524 @@ const { server_vars } = require("./variables.js");
let myChart; // Variable to store the Chart instance
-async function fetchData(genre) {
- const response = await fetch(
- `http://${server_vars.address}:${server_vars.port}/getTracksPerYearForGenre?genre=${genre}`
- );
- if (!response.ok) {
- throw new Error(`HTTP error! Status: ${response.status}`);
+function shortenNumber(number) {
+ const billion = 1e9; // 1 billion
+ const million = 1e6; // 1 million
+ const thousand = 1e3; // 1 thousand
+
+ if (Math.abs(number) >= billion) {
+ return (number / billion).toFixed(1) + "B";
+ } else if (Math.abs(number) >= million) {
+ return (number / million).toFixed(1) + "M";
+ } else if (Math.abs(number) >= thousand) {
+ return (number / thousand).toFixed(1) + "K";
+ } else {
+ return number.toString();
}
- return response.json();
}
-function updateChart(selectedGenre) {
- fetchData(selectedGenre)
- .then((data) => {
- // Destroy the existing chart if it exists
- if (myChart) {
- myChart.destroy();
- }
+const AVAILABLE_CHARTS = [
+ {
+ type: "bar",
+ id: "bar_1",
+ name: "Number of tracks released each year for a selected genre",
+ requestMethod: "tracksByYears&genre=",
+ label: "Tracks released",
+ },
+ {
+ type: "bar",
+ id: "bar_2",
+ name: "Number of plays of the selected genre each year",
+ requestMethod: "playsByYears&genre=",
+ label: "Plays",
+ },
+ {
+ type: "bar",
+ id: "bar_3",
+ name: "Averaged music features of the selected genre",
+ requestMethod: "features&genre=",
+ label: "Value",
+ },
+
+ {
+ type: "pie",
+ id: "pie_1",
+ name: "Top genres by number of plays",
+ requestMethod: "genresByPlays",
+ label: "Plays",
+ },
+ {
+ type: "pie",
+ id: "pie_2",
+ name: "Top of the years by the total number of plays of all tracks released during them",
+ requestMethod: "yearsByReleases",
+ label: "Plays",
+ },
+ {
+ type: "pie",
+ id: "pie_3",
+ name: "Top 5 genres with the most number of explicit tracks",
+ requestMethod: "explicitByGenre",
+ label: "Tracks",
+ },
+
+ {
+ type: "map",
+ id: "map_1",
+ name: "Heat map of genre popularity by country",
+ requestMethod: "genreByCountries&genre=",
+ label: "Monthly plays",
+ },
+ {
+ type: "map",
+ id: "map_2",
+ name: "Map of countries with their top genres and artists",
+ requestMethod: "topGenreAndArtistByCountries",
+ label: "Artist and genre",
+ },
+];
+
+function clearChart() {
+ if (myChart) {
+ myChart.destroy();
+ }
+}
+
+function removeOptionInput() {
+ document.getElementById("option-select-container").innerHTML = "";
+}
+
+function createBarChart(chart, data) {
+ return new Chart(document.getElementById("chart"), {
+ type: chart.type,
+ options: {
+ animation: true,
+ plugins: {
+ legend: {
+ display: false,
+ },
+ tooltip: {
+ enabled: true,
+ },
+ },
+ },
+ data: {
+ labels: data.map((row) => row[0]),
+ datasets: [
+ {
+ label: chart.label,
+ data: data.map((row) => row[1]),
+ },
+ ],
+ },
+ });
+}
+
+function createPieChart(myChart, data) {
+ // Convert string values to numbers
+ const numericData = data.map(([label, value]) => [
+ label,
+ parseFloat(value),
+ ]);
+
+ // Calculate the total sum of numbers
+ const sumTotal = numericData.reduce((sum, item) => sum + item[1], 0);
+
+ // Calculate the percentage for each number
+ const percentages = numericData.map((item) => {
+ const number = parseInt(item[1], 10);
+ const percent = (number / sumTotal) * 100;
+ return percent;
+ });
+
+ console.log("Total Sum:", sumTotal);
+ console.log("Percentages:", percentages);
+
+ // // Sum the values of the categories below the threshold
+ // const otherSum = numericData.reduce(
+ // (sum, [, value]) =>
+ // (value / totalSum) * 100 < thresholdPercentage ? sum + value : sum,
+ // 0
+ // );
- // Create a new chart with the new data
- myChart = new Chart(document.getElementById("chart"), {
- type: "bar",
- options: {
- animation: true,
- plugins: {
- legend: {
- display: false,
+ // // Add the "other" category to the filtered data
+ // if (otherSum > 0) {
+ // filteredData.push(["Other", otherSum]);
+ // }
+
+ const N = 7; // Set the number of top labels to display in the legend
+
+ const sortedData = data.sort((a, b) => b[1] - a[1]);
+
+ // Extract the top N labels
+ const topLabels = sortedData.slice(0, N).map((row) => row[0]);
+ console.log(topLabels);
+
+ var options = {
+ type: myChart.type,
+ options: {
+ animation: true,
+ plugins: {
+ legend: {
+ labels: {
+ usePointStyle: true,
+ filter: (legendItem) => {
+ return legendItem.index < N;
},
- tooltip: {
- enabled: true,
+ font: {
+ size: 20,
},
},
},
- data: {
- labels: data.map((row) => row.year),
- datasets: [
- {
- label: "Tracks released",
- data: data.map((row) => row.count),
- },
- ],
+ tooltip: {
+ enabled: true,
},
- });
+ },
+ },
+ data: {
+ labels: data.map((row) => row[0]),
+ datasets: [
+ {
+ label: myChart.label,
+ data: data.map((row) => row[1]),
+ },
+ ],
+ },
+ };
+
+ return new Chart(document.getElementById("chart"), options);
+}
+
+function updateChart(chart, options = "") {
+ clearChart();
+ fetchData(chart.requestMethod + options)
+ .then((data) => {
+ clearChart();
+ switch (chart.type) {
+ case "bar":
+ myChart = createBarChart(chart, data);
+ break;
+ case "pie":
+ myChart = createPieChart(chart, data);
+ break;
+ default:
+ break;
+ }
+ myChart = createBarChart(chart, data);
})
.catch((error) => {
console.error("Error fetching data:", error.message);
});
}
-document.getElementById("genreSelect").addEventListener("change", function () {
- const selectedGenre = this.value;
- updateChart(selectedGenre);
+async function getGenres() {
+ const requestMethod = "genres";
+ try {
+ const data = await fetchData(requestMethod);
+ return data;
+ } catch (error) {
+ console.error("Error:", error);
+ throw error;
+ }
+}
+
+async function fetchData(requestMethod) {
+ const response = await fetch(
+ `http://${server_vars.address}:${server_vars.port}/query?type=${requestMethod}`
+ );
+ if (!response.ok) {
+ throw new Error(`HTTP error! Status: ${response.status}`);
+ }
+ return response.json();
+}
+
+function createChartSelects() {
+ var selectedChartTypeOption =
+ document.getElementById("chart-type-select").value;
+
+ var availableCharts = [];
+ switch (selectedChartTypeOption) {
+ case "bar":
+ availableCharts = AVAILABLE_CHARTS.filter(
+ (chart) => chart.type === "bar"
+ );
+ break;
+ case "pie":
+ availableCharts = AVAILABLE_CHARTS.filter(
+ (chart) => chart.type === "pie"
+ );
+ break;
+ case "map":
+ availableCharts = AVAILABLE_CHARTS.filter(
+ (chart) => chart.type === "map"
+ );
+ break;
+ default:
+ break;
+ }
+
+ // Create the