This repository has been archived on 2024-07-22. You can view files and clone it, but cannot push or open issues or pull requests.
Zaimki/src/stats.js

113 lines
3.6 KiB
JavaScript

const {decodeTime} = require('ulid');
// TODO all the duplication...
const buildDict = (fn, ...args) => {
const dict = {};
for (let [key, value] of fn(...args)) {
dict[key] = value;
}
return dict;
}
const zip = (list, reverse) => {
return buildDict(function* () {
for (let [k, v] of list) {
yield reverse ? [v, k] : [k, v];
}
});
}
const sortByValue = (obj, reverse = false, firstN = -1) => {
let list = [];
for (let i in obj) {
if (obj.hasOwnProperty(i)) {
list.push([parseInt(obj[i]), i]);
}
}
list = list.sort((a, b) => reverse ? b[0] - a[0] : a[0] - b[0]);
if (firstN >= 0) {
list = list.slice(0, firstN);
}
return zip(list, true);
}
const formatMonth = d => `${d.getFullYear()}-${(d.getMonth() + 1).toString().padStart(2, '0')}-${d.getDate().toString().padStart(2, '0')}`;
const buildChart = (rows) => {
const dates = rows.map(row => new Date(decodeTime(row.id)));
const chart = {};
let loop = dates[0];
const end = dates[dates.length - 1];
while(loop <= end){
chart[formatMonth(loop)] = 0;
loop = new Date(loop.setDate(loop.getDate() + 1));
}
chart[formatMonth(loop)] = 0;
for (let date of dates) {
chart[formatMonth(date)]++;
}
return chart;
}
module.exports.statsFile = process.env.STATS_FILE.replace('%projectdir%', __dirname + '/..')
module.exports.calculateStats = async (db, allLocales) => {
const users = {
overall: (await db.get(`SELECT count(*) AS c FROM users`)).c,
admins: (await db.get(`SELECT count(*) AS c FROM users WHERE roles!=''`)).c,
chart: buildChart(await db.all(`SELECT id FROM users ORDER BY id`)),
};
const locales = {};
for (let locale in allLocales) {
if (!allLocales.hasOwnProperty(locale)) { continue; }
const profiles = await db.all(`SELECT pronouns, flags FROM profiles WHERE locale='${locale}'`);
const pronouns = {}
const flags = {}
for (let profile of profiles) {
const pr = JSON.parse(profile.pronouns);
for (let pronoun in pr) {
if (!pr.hasOwnProperty(pronoun)) { continue; }
if (pronoun.includes(',') || pr[pronoun] < 0) {
continue;
}
const p = pronoun.replace(/^.*:\/\//, '').replace(/^\//, '').toLowerCase().replace(/^[a-z]+\.[^/]+\//, '');
if (pronouns[p] === undefined) {
pronouns[p] = 0;
}
pronouns[p] += pr[pronoun] === 1 ? 1 : 0.5;
}
const fl = JSON.parse(profile.flags);
for (let flag of fl) {
if (flags[flag] === undefined) {
flags[flag] = 0;
}
flags[flag] += 1;
}
}
locales[locale] = {
name: allLocales[locale].name,
url: allLocales[locale].url,
profiles: profiles.length,
pronouns: sortByValue(pronouns, true, 36),
flags: sortByValue(flags, true, 36),
nouns: {
approved: (await db.get(`SELECT count(*) AS c FROM nouns WHERE locale='${locale}' AND approved=1 AND deleted=0`)).c,
awaiting: (await db.get(`SELECT count(*) AS c FROM nouns WHERE locale='${locale}' AND approved=0 AND deleted=0`)).c,
},
chart: buildChart(await db.all(`SELECT id FROM profiles WHERE locale='${locale}' ORDER BY id`)),
};
}
return { calculatedAt: parseInt(new Date() / 1000), users, locales };
}