Extract website to SREBOT-web repo: remove web/, move seasons/locales/constants to repo root, fix imports
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SEASONS_FILE = path.join(__dirname, '..', 'constants', 'seasons');
|
||||
|
||||
// Parse DD.MM from the "until eos" parenthetical to an end-of-day UTC unix ts
|
||||
// for the season's year (inferred from the first weekN timestamp).
|
||||
function _endOfDayUtc(year, day, month) {
|
||||
const d = new Date(Date.UTC(year, month - 1, day, 23, 59, 59));
|
||||
return Math.floor(d.getTime() / 1000);
|
||||
}
|
||||
|
||||
// Parse the seasons file into:
|
||||
// { '2026-I': {start, end, weeks}, '2026-II': {start, end, weeks}, ... }
|
||||
// where `end` is the last second (UTC) of the date in the "until eos" row.
|
||||
// Status is computed at access time by getSeasons(), not cached here.
|
||||
function parseSeasonsFile(content) {
|
||||
const lines = content.split('\n');
|
||||
const seasons = {};
|
||||
let current = null;
|
||||
let firstTs = null;
|
||||
let eosDay = null, eosMonth = null;
|
||||
let weekEntries = [];
|
||||
|
||||
const seasonHeader = /^\s*(\d{4})-(I{1,3}|IV|VI{0,3}|IX|X)\s*$/;
|
||||
const weekLine = /^(week\s+\d+|until\s+eos)\s+\((\d{2}\.\d{2})\s+[—-]\s+(\d{2}\.\d{2})\)\s+<t:(\d+):R>\s+max BR\s+(\d+(?:\.\d+)?)/i;
|
||||
|
||||
const commit = () => {
|
||||
if (current && firstTs !== null && eosDay !== null) {
|
||||
const year = parseInt(current.split('-')[0], 10);
|
||||
seasons[current] = {
|
||||
start: firstTs,
|
||||
end: _endOfDayUtc(year, eosDay, eosMonth),
|
||||
weeks: weekEntries,
|
||||
};
|
||||
}
|
||||
current = null; firstTs = null; eosDay = null; eosMonth = null;
|
||||
weekEntries = [];
|
||||
};
|
||||
|
||||
for (const line of lines) {
|
||||
const h = line.match(seasonHeader);
|
||||
if (h) { commit(); current = `${h[1]}-${h[2]}`; continue; }
|
||||
if (!current) continue;
|
||||
const w = line.match(weekLine);
|
||||
if (w) {
|
||||
const kind = w[1].toLowerCase();
|
||||
const startDate = w[2];
|
||||
const endDate = w[3];
|
||||
const ts = parseInt(w[4], 10);
|
||||
const maxBr = parseFloat(w[5]);
|
||||
weekEntries.push({
|
||||
label: kind,
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
start_ts: ts,
|
||||
max_br: maxBr,
|
||||
});
|
||||
if (firstTs === null) firstTs = ts;
|
||||
if (kind === 'until eos') {
|
||||
eosDay = parseInt(endDate.split('.')[0], 10);
|
||||
eosMonth = parseInt(endDate.split('.')[1], 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
commit();
|
||||
return seasons;
|
||||
}
|
||||
|
||||
// All "week N (…) <t:TS:R>" timestamps for a given season name, in order.
|
||||
// Used as x-axis faint vline markers by the Python renderer.
|
||||
function parseWeekBoundaries(content, seasonName) {
|
||||
const lines = content.split('\n');
|
||||
const out = [];
|
||||
let inSeason = false;
|
||||
const seasonHeader = /^\s*(\d{4})-(I{1,3}|IV|VI{0,3}|IX|X)\s*$/;
|
||||
const weekLine = /<t:(\d+):R>/;
|
||||
for (const line of lines) {
|
||||
const h = line.match(seasonHeader);
|
||||
if (h) { inSeason = `${h[1]}-${h[2]}` === seasonName; continue; }
|
||||
if (!inSeason) continue;
|
||||
const w = line.match(weekLine);
|
||||
if (w) out.push(parseInt(w[1], 10));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
let _cached = null;
|
||||
function _loadSeasons() {
|
||||
if (_cached) return _cached;
|
||||
const content = fs.readFileSync(SEASONS_FILE, 'utf8');
|
||||
const parsed = parseSeasonsFile(content);
|
||||
_cached = { content, parsed };
|
||||
return _cached;
|
||||
}
|
||||
|
||||
function getSeasons() {
|
||||
const { parsed } = _loadSeasons();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const out = {};
|
||||
for (const [name, range] of Object.entries(parsed)) {
|
||||
out[name] = {
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
status: range.end < now ? 'completed' : 'in_progress',
|
||||
};
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function getSeasonRange(name) {
|
||||
return getSeasons()[name] || null;
|
||||
}
|
||||
|
||||
function getSeasonDetails() {
|
||||
const { parsed } = _loadSeasons();
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const out = {};
|
||||
for (const [name, range] of Object.entries(parsed)) {
|
||||
out[name] = {
|
||||
start: range.start,
|
||||
end: range.end,
|
||||
status: range.end < now ? 'completed' : 'in_progress',
|
||||
weeks: range.weeks || [],
|
||||
};
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
function getWeekBoundaries(name) {
|
||||
const { content } = _loadSeasons();
|
||||
return parseWeekBoundaries(content, name);
|
||||
}
|
||||
|
||||
module.exports = { getSeasons, getSeasonRange, getSeasonDetails, getWeekBoundaries, parseSeasonsFile, parseWeekBoundaries };
|
||||
Reference in New Issue
Block a user