Improved layout, added warnings to result page
This commit is contained in:
23
main.ts
23
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<string[]> {
|
||||
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<VideoDescription[]> {
|
||||
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<void> {
|
||||
@@ -45,6 +52,16 @@ function sleep(ms: number): Promise<void> {
|
||||
async function watchdog(bot: Mwn, onChange: (paths: string[]) => Promise<void>) {
|
||||
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<void>)
|
||||
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)
|
||||
})
|
||||
|
||||
28
parse.ts
28
parse.ts
@@ -1,21 +1,22 @@
|
||||
import { Mwn } from "mwn";
|
||||
import { VideoDescription } from "./main.ts";
|
||||
|
||||
function parseTemplate(description: string): Record<string, string> {
|
||||
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<VideoDescription> = {};
|
||||
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']
|
||||
}
|
||||
|
||||
8
write.ts
8
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 ? `<span title="${video.nags.join("
")}">🔴</span>` : "";
|
||||
return `=== ${video.title} ===
|
||||
Date:{{#time: Y-m-d (D) | ${video.date}}}<br>
|
||||
Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}<br>
|
||||
Teachers: ${teachersList}<br>
|
||||
Level: ${video.level}
|
||||
|
||||
@@ -21,13 +22,14 @@ ${video.notes}
|
||||
<br clear=all>`;
|
||||
}
|
||||
|
||||
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`
|
||||
|
||||
Reference in New Issue
Block a user