diff --git a/.gitignore b/.gitignore index 4c49bd7..3ecbce0 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ .env +cov_profile/ diff --git a/main.ts b/main.ts index 06fd5db..5a4eddf 100644 --- a/main.ts +++ b/main.ts @@ -37,9 +37,11 @@ async function fetchPages(pages: string[], bot: Mwn): Promise ({ 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}` - )) + 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) diff --git a/run.sh b/run.sh index 04f2543..d7bcf37 100644 --- a/run.sh +++ b/run.sh @@ -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 diff --git a/test.sh b/test.sh new file mode 100644 index 0000000..b105111 --- /dev/null +++ b/test.sh @@ -0,0 +1 @@ +deno test --allow-read=./test --coverage=cov_profile diff --git a/write.ts b/write.ts index 67442ea..38c8ca7 100644 --- a/write.ts +++ b/write.ts @@ -1,10 +1,71 @@ 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 { const teachersList = video.teachers.map(v => "[[" + v + "]]").join(" & "); const nagElement = video.nags.length > 0 ? `🔴` : ""; - return `=== ${video.title} === + return `=== ${camelToTitleCase(video.title)} === Date: {{#time: Y-m-d (D) | ${video.date}}} ${nagElement}
Teachers: ${teachersList}
Level: ${video.level} @@ -38,21 +99,23 @@ export function writeSections(events: VideoDescription[][]): string { }).join("\n\n\n") } -export function bucketEvents(events: VideoDescription[]): VideoDescription[][] { - const buckets: Record = {} - for (const e of events) { - const tag = e.event + e.location + new Date(e.date).getFullYear().toString() - if (tag in buckets) { - buckets[tag].push(e) - } else { - buckets[tag] = [e] - } - } +/** + * + * @param videos + * @returns Bucket of videos for of each event, grouped by `name`, `location` and `year` + */ +export function bucketEvents(videos: VideoDescription[]): VideoDescription[][] { + const buckets = Object.groupBy(videos, (video) => { + 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) => 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()) } \ No newline at end of file