From 7cb4319a053f5acaf5f85649301f559f7ecf4cc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20W=C3=B6lfer?= Date: Wed, 7 May 2025 12:18:32 +0200 Subject: [PATCH] Improved layout, added warnings to result page --- main.ts | 23 ++++++++++++++++++++--- parse.ts | 28 ++++++++++++++++++++-------- write.ts | 8 +++++--- 3 files changed, 45 insertions(+), 14 deletions(-) diff --git a/main.ts b/main.ts index 4fc87e6..55b8628 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,7 @@ import { Mwn, RecentChange } from 'npm:mwn' import process, { title } from "node:process"; import { parseSummary } from "./parse.ts"; -import { bucketEvents, writeSection } from "./write.ts"; +import { bucketEvents, writeSections } from "./write.ts"; async function getWorkshopVideos(bot: Mwn): Promise { const response = await bot.request({ @@ -29,13 +29,20 @@ export interface VideoDescription { notes: string path: string title: string + nags: string[] } async function fetchPages(pages: string[], bot: Mwn): Promise { const d = await Promise.allSettled(pages.map(async (v) => parseSummary(await new bot.Page(v).text(), v) )) + + d.map((v, index) => ({ r: v, source: pages[index] })).filter((v): v is { source: string, r: PromiseRejectedResult } => v.r.status === "rejected").forEach( + v => console.warn(`Error parsing ${v.source}: ${v.r.reason}` + )) + return d.filter(v => v.status === "fulfilled").map(v => v.value) + } function sleep(ms: number): Promise { @@ -45,6 +52,16 @@ function sleep(ms: number): Promise { async function watchdog(bot: Mwn, onChange: (paths: string[]) => Promise) { let last_change: string | undefined = undefined; + let heartbeat_count = 0; + const heartbeat_count_max = 60; + + const heartbeat = () => { + heartbeat_count += 1 + if (heartbeat_count >= heartbeat_count_max) { + heartbeat_count = 0; + console.log(new Date(), "Heartbeat") + } + } while (true) { const { last_change: l, new_file_changes } = await queryChanges(bot, last_change); @@ -53,7 +70,7 @@ async function watchdog(bot: Mwn, onChange: (paths: string[]) => Promise) console.log(new_file_changes.length, "changes") await onChange(new_file_changes.map(v => v.title)) } - console.log(new Date(), "Heartbeat") + heartbeat() await sleep(30000); } } @@ -94,7 +111,7 @@ async function main() { await watchdog(bot, async (paths) => { const relevantFiles = await getWorkshopVideos(bot) const d = await fetchPages(relevantFiles, bot) - const t = writeSection(bucketEvents(d)) + const t = writeSections(bucketEvents(d)) const response = await bot.save(title, "{{TOC|limit=3}}\n\n" + t, 'Triggered by changes to ' + paths.map(v => "[[" + v + "]]").join(", ")); console.log(response) }) diff --git a/parse.ts b/parse.ts index dd66420..56f65e4 100644 --- a/parse.ts +++ b/parse.ts @@ -1,21 +1,22 @@ import { Mwn } from "mwn"; import { VideoDescription } from "./main.ts"; -function parseTemplate(description: string): Record { +function parseTemplate(description: string): { warnings: string[] } & { [k: string]: string } { const u = new (new Mwn().Wikitext)(description) const r = u.parseTemplates({ namePredicate: (name) => name === "DanceWorkshopDescription", }) + const warnings = [] if (r.length < 1) { throw new Error("Could not find DanceWorkshopDescription") } if (r.length > 1) { - console.warn("More than one DanceWorkshopDescription template found") + warnings.push("More than one DanceWorkshopDescription template found") } const p = r[0] const properties = p.parameters.map(v => ({ [v.name]: v.value })) - return Object.assign({}, ...properties) + return Object.assign({}, { warnings }, ...properties) } function parseBlock(header: string, text: string): string { @@ -35,9 +36,10 @@ export function parseSummary(description: string, name: string): VideoDescriptio const properties = parseTemplate(description) const b: Partial = {}; + b.nags = properties.warnings if (Object.hasOwn(properties, 'teachers')) { - console.warn(`Page ${name} has old template usage [contains 'teachers' key]`) + b.nags.push(`Contains 'teachers' key instead of 'leader'/'follower'`) const t = properties['teachers'].split('&').map(item => item.trim()); b.teachers = t } else { @@ -52,15 +54,25 @@ export function parseSummary(description: string, name: string): VideoDescriptio if (!Object.hasOwn(properties, 'patterns')) { - console.warn(`Page ${name} has old template usage [no 'patterns' key]`) - b.patterns = parseBlock("Shown Patterns", description) + b.nags.push(`No 'patterns' key`) + try { + b.patterns = parseBlock("Shown Patterns", description) + } catch (e) { + b.nags.push("Shown Patterns: " + e) + b.patterns = "" + } } else { b.patterns = properties['patterns'] } if (!Object.hasOwn(properties, 'notes')) { - console.warn(`Page ${name} has old template usage [no 'notes' key]`) - b.notes = parseBlock("Notes", description) + b.nags.push(`No 'notes' key`) + try { + b.notes = parseBlock("Notes", description) + } catch (e) { + b.nags.push("Notes: " + e) + b.notes = "" + } } else { b.notes = properties['notes'] } diff --git a/write.ts b/write.ts index 33ec996..88cb333 100644 --- a/write.ts +++ b/write.ts @@ -3,8 +3,9 @@ import { VideoDescription } from "./main.ts"; export function singleVideoDescription(video: VideoDescription): string { const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & "); + const nagElement = video.nags.length > 0 ? `🔴` : ""; return `=== ${video.title} === -Date:{{#time: Y-m-d (D) | ${video.date}}}
+Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}
Teachers: ${teachersList}
Level: ${video.level} @@ -21,13 +22,14 @@ ${video.notes}
`; } -export function writeSection(events: VideoDescription[][]): string { +export function writeSections(events: VideoDescription[][]): string { return events.map(v => { const token = v[0] const event_name = token.event === token.location ? token.event : token.event + " " + token.location // FIXME: This will break with videos in 75 years - const event_date = event_name.match(/\d{2}|20\d{2}/) ? "" : new Date(token.date).getFullYear().toString() + const event_contains_year = event_name.match(/\d{2}|20\d{2}/) === null + const event_date = event_contains_year ? "" : new Date(token.date).getFullYear().toString() let r = `== ${event_name} ${event_date} ==\n` r += `${v.length} Video${v.length <= 1 ? "" : "s"}\n`