UNPKG

web-analyst

Version:

Web Analyst is a simple back-end tracking system to measure your web app performance.

647 lines (568 loc) 17.9 kB
import { generateBarChart, generateDataTables, generateGenericChart, generateMapChart, generatePieChart, getData, getPercentages } from "./wa-chart-generator.mjs"; import {CHART_DATA_FILES, VIEW_TYPE} from "./wa-constants.mjs"; import {CHART_OPTIONS} from "./user-options.mjs"; import {getWeekLabels, getYearLabels} from "./wa-fixed-label-generator.mjs"; export const buildVisitorChart = async function (pathname, { $chart, style1, style2, style3, style4, title, subTitle }) { try { const jsonData = await getData(pathname); // Build datasets const {dataVisitors: dataVisits, dataUniqueVisitors, dataReturningVisitors, labels} = jsonData; if (!dataVisits || !dataUniqueVisitors) { console.error(`Missing data visitors`); return; } for (let i = 0; i < dataReturningVisitors.length; ++i) { const returningVisitorList = dataReturningVisitors[i]; if (Array.isArray(returningVisitorList)) { dataReturningVisitors[i] = returningVisitorList.length; } else { dataReturningVisitors[i] = 0; } } const visitors = []; const n = Math.max(dataUniqueVisitors.length, dataReturningVisitors.length); for (let i = 0; i < n; ++i) { const uniqueVisitor = dataUniqueVisitors[i] || 0; const returningVisitor = dataReturningVisitors[i] || 0; visitors[i] = uniqueVisitor + returningVisitor; } const datasets = [ { label: "Visitors unique", data : dataUniqueVisitors, ...style1, }, { label: "Returning Visitors", data : dataReturningVisitors, ...style3, }, { label: "Total Visitors", data : visitors, ...style4, }, { label: "Visits", data : dataVisits, ...style2, }, ]; if (!labels) { console.error(`Missing labels`); return; } const data = {labels, datasets}; // Build DOM element const options1 = { responsive: true, plugins : { legend: { position: "top", }, title : { display: true, text : title } }, }; generateBarChart($chart, { title : subTitle, data, options : options1, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: "WA2437"}, e.message); } return false; }; export const buildViewsUsersChart = async function (arr, { $chart, title, subTitle }) { try { // Import data file const jsonData = {}; for (let index in arr) { const pathname = arr[index]; const data = await getData(pathname) || {}; Object.assign(jsonData, data); } const datasets = []; // Build data by category const months = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; // Define a palette of colors for your datasets const colors = [ "rgba(255, 99, 132, 0.7)", // Red "rgba(54, 162, 235, 0.7)", // Blue "rgba(255, 206, 86, 0.7)", // Yellow "rgba(75, 192, 192, 0.7)", // Green "rgba(153, 102, 255, 0.7)", // Purple "rgba(255, 159, 64, 0.7)", // Orange "rgba(199, 199, 199, 0.7)", // Grey "rgba(120,73,73,0.7)", ]; const categories = CHART_OPTIONS.PageViewsPerUsers.ranges || [2, 9, 10000]; let start = categories[0]; for (let index in categories) { const dataset = {}; const limit = categories[index]; const categoryIndex = parseInt(index); if (!categoryIndex) { dataset.label = `Number of users with less than ${limit} page views`; } else if (categoryIndex === categories.length - 1) { dataset.label = `Number of users with more than ${start} page views`; } else { dataset.label = `Number of users with ${start} to ${limit} page views`; } dataset.backgroundColor = colors[index]; // Value of page views users every month per range dataset.data = Array(categories.length).fill(0); for (let m = 0; m < months.length; m++) { for (let userIPToken in jsonData) { const info = jsonData[userIPToken]; const monthNumber = new Date(info.lastVisit || info.date || info.realDate || info.initialDate).getMonth(); if (m !== monthNumber) { continue; } if (info.visited <= limit) { dataset.data[m] = dataset.data[m] || []; ++dataset.data[m]; delete jsonData[userIPToken]; } } start = limit + 1; } datasets.push(dataset); } const data = { labels: months, datasets }; // Build DOM element const options1 = { responsive: true, plugins : { legend: { position: "top", }, title : { display: true, text : title } }, scales : { x: { stacked: true, }, y: { stacked: true } } }; generateBarChart($chart, { title : subTitle, data, options : options1, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: "WA2437"}, e.message); } return false; }; export const buildMoneyChart = async function (pathname, { view, $chart, style1, title, subTitle }) { try { const jsonData = await getData(pathname); // Build datasets let {months, days} = jsonData; // Remove earning past today as they should all be 0 const now = new Date(); const month = now.getMonth(); const dayOfWeek = now.getDay(); months.splice(month); days.splice(dayOfWeek); let labels, money; if (view === VIEW_TYPE.WEEK) { labels = getWeekLabels(); money = days; } else if (view === VIEW_TYPE.YEAR) { labels = getYearLabels(); money = months; } else { return false; } const datasets = [ { label: "Earnings", data : money, ...style1, } ]; const data = {labels, datasets}; // Build DOM element const options1 = { responsive: true, plugins : { legend: { position: "top", }, title : { display: true, text : title } }, scales : { x: { stacked: true, }, y: { stacked: true } } }; generateGenericChart($chart, { type : "line", title : subTitle, data, options : options1, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildPopularityGraph = async function (pathname, { $chart, style1 = { backgroundColor: [ "rgb(180,181,217)", "rgb(208,180,217)", "rgb(180,217,211)", "rgb(192,217,180)", "rgb(121,152,176)", "rgb(185,168,133)", "rgb(217,204,180)", "rgb(217,189,180)", "rgb(217,180,180)", "rgb(217,180,216)", "rgb(217,180,194)", "rgb(180,217,217)", "rgb(192,217,180)", "rgb(217,212,180)", "rgb(180,181,217)", ], }, title, subTitle }) { try { const jsonData = await getData(pathname); // Build datasets const {labels, percentages} = jsonData; if (!percentages) { console.error(`Missing data visitors`); return; } /**F * * @type {*[]} */ const datasets = [ { label : "Visits", data : percentages, hoverOffset: 4, ...style1, } ]; if (!labels) { console.error(`Missing labels`); return; } // Build DOM element const options = { responsive: true, plugins : { legend : { position: "top", }, title : { display: true, text : title }, datalabels: { /** * https://stackoverflow.com/questions/52044013/chartjs-datalabels-show-percentage-value-in-pie-piece * @param value * @param ctx * @returns {string} */ formatter: (value) => { return value + "%"; }, } } }; generatePieChart($chart, { title : subTitle, labels, datasets, options, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildPopularityPie = async function (pathname, category, { style1 = { backgroundColor: [ "rgb(180,181,217)", "rgb(208,180,217)", "rgb(180,217,211)", "rgb(192,217,180)", "rgb(121,152,176)", "rgb(185,168,133)", "rgb(217,204,180)", "rgb(217,189,180)", "rgb(217,180,180)", "rgb(217,180,216)", "rgb(217,180,194)", "rgb(180,217,217)", "rgb(192,217,180)", "rgb(217,212,180)", "rgb(180,181,217)", ], }, title, subTitle }) { try { const {labels, percentages} = await getPercentages(pathname, category) || {}; if (!labels || !percentages) { console.error(`Missing data visitors`); return; } const datasets = [ { label : title, data : percentages, hoverOffset: 4, ...style1, } ]; // Build DOM element const options = { responsive: true, plugins : { legend : { position: "top", }, title : { display: true, text : title }, datalabels: { /** * https://stackoverflow.com/questions/52044013/chartjs-datalabels-show-percentage-value-in-pie-piece * @param value * @param ctx * @returns {string} */ formatter: (value) => { return value + "%"; }, } } }; // Build the DOM element // Get the template and the container const template = document.getElementById("pie-chart-template"); const container = document.getElementById("chart-holder"); const $clone = template.content.cloneNode(true); const $title = $clone.querySelector(".title"); $title.textContent = title; const $chart = $clone.querySelector("canvas.canvas"); container.appendChild($clone); generatePieChart($chart, { title : subTitle, labels, datasets, options, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildPopularityMap = async function (pathname, category, { title, subTitle }) { try { const {labels, percentages} = await getPercentages(pathname, category, ["UTC", "Etc/Unknown"]) || {}; if (!labels || !percentages) { console.error(`Missing data visitors`); return; } // Build the DOM element // Get the template and the container const template = document.getElementById("map-chart-template"); const container = document.getElementById("chart-holder"); const $clone = template.content.cloneNode(true); const $title = $clone.querySelector(".title"); $title.textContent = title; const $chart = $clone.querySelector("div.chart"); container.appendChild($clone); generateMapChart($chart, { title : subTitle, labels, datasets: percentages, backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildVisitorGraph = async function (pathname, {$chart, title, subTitle}) { try { // Build DOM element await buildVisitorChart(pathname, { $chart, style1: { backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }, style2: { backgroundColor: "rgb(188,217,180)", borderColor : "rgb(76,134,123)", }, style3: { backgroundColor: "rgb(209,182,134)", borderColor : "rgb(122,102,66)", }, style4: { backgroundColor: "rgb(89,129,147)", borderColor : "rgb(120,165,184)", }, title, subTitle }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildViewsUsersGraph = async function (arr, {$chart, title, subTitle}) { try { // Build DOM element await buildViewsUsersChart(arr, { $chart, title, subTitle }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildEarningsGraph = async function (pathname, {$chart, title, subTitle, view}) { try { // Build DOM element await buildMoneyChart(CHART_DATA_FILES.EARNING_DATA_FILENAME, { view, $chart, style1: { backgroundColor: "rgb(180,181,217)", borderColor : "rgb(76,87,134)", }, style2: { backgroundColor: "rgb(188,217,180)", borderColor : "rgb(76,134,123)", }, title, subTitle }); return true; } catch (e) { console.error({lid: 2437}, e.message); } return false; }; export const buildDataTable = async function (pathname, selector) { try { // Data const dataTables = await getData(pathname); generateDataTables(selector, {data: dataTables}); return true; } catch (e) { console.error({lid: 2449}, e.message); } return false; };