45 lines
1.9 KiB
JavaScript
45 lines
1.9 KiB
JavaScript
'use strict';
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const RATINGS = Object.freeze(['flame', 'good', 'neutral', 'skip']);
|
|
const WEIGHT = Object.freeze({ flame: 3, good: 1, neutral: 0, skip: -2 });
|
|
const normalize = value => String(value || '').normalize('NFKD').toLowerCase().replace(/[^a-z0-9]+/g, '');
|
|
|
|
function loadCatalog(file = path.join(__dirname, 'catalog.json')) {
|
|
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
if (parsed.schema !== 1 || !Array.isArray(parsed.entries)) throw new Error('Invalid Greatest of All Time catalog');
|
|
const ids = new Set();
|
|
for (const entry of parsed.entries) {
|
|
if (!/^[a-z0-9-]+$/.test(entry.id) || ids.has(entry.id) || !entry.title || !entry.period || !Number.isInteger(entry.rank))
|
|
throw new Error(`Invalid or duplicate Greatest of All Time entry: ${entry.id}`);
|
|
if (entry.cover && !/^https:\/\//.test(entry.cover)) throw new Error(`Invalid cover URL for ${entry.id}`);
|
|
if (entry.source?.url && !/^https:\/\//.test(entry.source.url)) throw new Error(`Invalid source URL for ${entry.id}`);
|
|
ids.add(entry.id);
|
|
}
|
|
return parsed;
|
|
}
|
|
|
|
function findOwned(entry, mangas) {
|
|
const names = new Set([entry.title, ...(entry.aliases || [])].map(normalize));
|
|
return mangas.find(m => {
|
|
const alt = m.meta?.altTitles;
|
|
return [m.title, ...(Array.isArray(alt) ? alt : alt ? [alt] : [])].some(name => names.has(normalize(name)));
|
|
}) || null;
|
|
}
|
|
|
|
function countsFor(entryId, votes, users) {
|
|
const counts = { flame: 0, good: 0, neutral: 0, skip: 0 };
|
|
for (const name of Object.keys(users)) {
|
|
const rating = votes[name]?.[entryId];
|
|
if (RATINGS.includes(rating)) counts[rating]++;
|
|
}
|
|
return counts;
|
|
}
|
|
|
|
function scoreFor(counts) {
|
|
return RATINGS.reduce((sum, rating) => sum + counts[rating] * WEIGHT[rating], 0);
|
|
}
|
|
|
|
module.exports = { RATINGS, WEIGHT, normalize, loadCatalog, findOwned, countsFor, scoreFor };
|