Compare commits

..

2 Commits

Author SHA1 Message Date
Lukas Wölfer
86246a613e Added parse summary to commit summary 2025-06-05 23:11:24 +02:00
Lukas Wölfer
1fd15470f8 Added title casing 2025-06-05 23:01:25 +02:00
5 changed files with 98 additions and 22 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
.env .env
cov_profile/

25
main.ts
View File

@@ -32,16 +32,24 @@ export interface VideoDescription {
nags: string[] nags: string[]
} }
async function fetchPages(pages: string[], bot: Mwn): Promise<VideoDescription[]> { async function fetchPages(pages: string[], bot: Mwn): Promise<{ pages: VideoDescription[], errors: string[] }> {
const d = await Promise.allSettled(pages.map(async (v) => const d = await Promise.allSettled(pages.map(async (v) =>
parseSummary(await new bot.Page(v).text(), 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( const siteProblems: string[] = []
v => console.warn(`Error parsing ${v.source}: ${v.r.reason}`
))
return d.filter(v => v.status === "fulfilled").map(v => v.value) d.map((v, index) => ({ r: v, source: pages[index] }))
.filter((v): v is { source: string, r: PromiseRejectedResult } => v.r.status === "rejected")
.forEach(
v => {
const e = `Error parsing ${v.source}: ${v.r.reason}`
console.warn(e)
siteProblems.push(e)
}
)
return { pages: d.filter(v => v.status === "fulfilled").map(v => v.value), errors: siteProblems }
} }
@@ -110,9 +118,12 @@ async function main() {
await bot.login(); await bot.login();
await watchdog(bot, async (paths) => { await watchdog(bot, async (paths) => {
const relevantFiles = await getWorkshopVideos(bot) const relevantFiles = await getWorkshopVideos(bot)
const d = await fetchPages(relevantFiles, bot) const { pages: d, errors: parseErrors } = await fetchPages(relevantFiles, bot)
const t = writeSections(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(", ")); const trigger_summary = 'Triggered by changes to ' + paths.map(v => `[[${v}]]`).join(", ")
const error_summary = parseErrors.join("\n")
const summary = [trigger_summary, error_summary].filter(v => v !== undefined && v.length > 0).join("\n")
const response = await bot.save(title, "{{TOC|limit=3}}\n\n" + t, summary);
console.log(response) console.log(response)
}) })

2
run.sh
View File

@@ -1 +1 @@
deno run --env-file=.env --allow-read=./descriptions.json --allow-net=dancing.thasky.one:443 -E main.ts deno run --env-file=.env --allow-net=dancing.thasky.one:443 -E main.ts

1
test.sh Normal file
View File

@@ -0,0 +1 @@
deno test --allow-read=./test --coverage=cov_profile

View File

@@ -1,10 +1,71 @@
import { VideoDescription } from "./main.ts"; import { VideoDescription } from "./main.ts";
export const SMALL_WORDS = new Set([
"a",
"an",
"and",
"as",
"at",
"because",
"but",
"by",
"en",
"for",
"if",
"in",
"neither",
"nor",
"of",
"on",
"only",
"or",
"over",
"per",
"so",
"some",
"than",
"that",
"the",
"to",
"up",
"upon",
"v",
"versus",
"via",
"vs",
"when",
"with",
"without",
"yet",
]);
function capitalize(word: string): string {
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase();
}
function camelToTitleCase(camelCaseStr: string): string {
// Insert a space before each uppercase letter
const titleCaseStr = camelCaseStr.replace(/([A-Z])/g, ' $1');
// Split the string into words and process each word using map
const words = titleCaseStr.split(' ')
.map(v => v.toLowerCase())
.map(word => {
if (SMALL_WORDS.has(word)) {
return word;
}
return capitalize(word)
});
return capitalize(words.join(' '));
}
export function singleVideoDescription(video: VideoDescription): string { export function singleVideoDescription(video: VideoDescription): string {
const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & "); const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & ");
const nagElement = video.nags.length > 0 ? `<span title="${video.nags.join("&#010;")}">🔴</span>` : ""; const nagElement = video.nags.length > 0 ? `<span title="${video.nags.join("&#010;")}">🔴</span>` : "";
return `=== ${video.title} === return `=== ${camelToTitleCase(video.title)} ===
Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}<br> Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}<br>
Teachers: ${teachersList}<br> Teachers: ${teachersList}<br>
Level: ${video.level} Level: ${video.level}
@@ -38,21 +99,23 @@ export function writeSections(events: VideoDescription[][]): string {
}).join("\n\n\n") }).join("\n\n\n")
} }
export function bucketEvents(events: VideoDescription[]): VideoDescription[][] { /**
const buckets: Record<string, VideoDescription[]> = {} *
for (const e of events) { * @param videos
const tag = e.event + e.location + new Date(e.date).getFullYear().toString() * @returns Bucket of videos for of each event, grouped by `name`, `location` and `year`
if (tag in buckets) { */
buckets[tag].push(e) export function bucketEvents(videos: VideoDescription[]): VideoDescription[][] {
} else { const buckets = Object.groupBy(videos, (video) => {
buckets[tag] = [e] return `${video.event}${video.location}${new Date(video.date).getFullYear()}`;
}
}
Object.values(buckets) })
.forEach(b =>
const sortedBuckets = Object.values(buckets)
.filter(v => v !== undefined)
.map(b =>
b.sort((a, b) => b.sort((a, b) =>
new Date(a.date).getTime() - new Date(b.date).getTime())) new Date(a.date).getTime() - new Date(b.date).getTime()))
return Object.values(buckets).sort((a, b) => new Date(a[0].date).getTime() - new Date(b[0].date).getTime()) return sortedBuckets
.sort((a, b) => new Date(a[0].date).getTime() - new Date(b[0].date).getTime())
} }