diff --git a/.github/workflows/update_genuary.yaml b/.github/workflows/update_genuary.yaml new file mode 100644 index 000000000..7cc413b3d --- /dev/null +++ b/.github/workflows/update_genuary.yaml @@ -0,0 +1,50 @@ +name: Update Genuary + +on: + workflow_dispatch: + schedule: + # We want '0 9 * 1 *' in Pacific Time, but GitHub Actions doesn't support timezones. + # So we use '0 17 * 1 *' in UTC, which is 9 AM in Pacific Time. + - cron: '0 17 * * *' + +jobs: + update-genuary: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - run: | + go run github.com/ethanthatonekid/pins@latest \ + get -o /tmp/pins $GUILD_ID "channel_parent_id == $CHANNEL_ID" + env: + GUILD_ID: ${{ secrets.GUILD_ID }} + CHANNEL_ID: ${{ secrets.GENUARY_CHANNEL_ID }} + DISCORD_TOKEN: ${{ secrets.GENUARY_DISCORD_TOKEN }} + + - id: transform-genuary + run: | + year=$(date +%Y) + echo "year=$year" >> $GITHUB_OUTPUT + + node scripts/transform-genuary.js /tmp/pins/*.json \ + > "src/routes/(site)/genuary/$year/data.json" + + if git diff --exit-code --quiet; then + echo "updated=0" >> $GITHUB_OUTPUT + else + echo "updated=1" >> $GITHUB_OUTPUT + fi + + - if: steps.transform-genuary.outputs.updated == 1 + uses: peter-evans/create-pull-request@v4 + with: + title: |- + Update Genuary ${{ steps.transform-genuary.outputs.year }} :calendar: :sparkles: :art: + body: |- + This pull request was automatically generated by GitHub Actions. + + :sparkles: :sparkles: :sparkles: + commit-message: |- + Update Genuary ${{ steps.transform-genuary.outputs.year }} :calendar: :sparkles: :art: + labels: genuary + branch: genuary diff --git a/scripts/transform-genuary.js b/scripts/transform-genuary.js new file mode 100644 index 000000000..a173b2eba --- /dev/null +++ b/scripts/transform-genuary.js @@ -0,0 +1,50 @@ +import * as fs from 'node:fs/promises'; + +async function main() { + const year = new Date().getFullYear(); + const filepath = process.argv[2]; + const content = JSON.parse(await fs.readFile(filepath)); + + let out = []; + for (const pin of content.pins) { + if (!pin.attachments) { + continue; + } + + const attachment = pin.attachments[0]; + const src = attachment.proxy_url || null; + const alt = content.channel_names[pin.channel_id] || ''; + if (!/^(\d+) /.test(alt)) { + continue; + } + + let view = 'normal'; + switch (true) { + case attachment.width * 1.2 > attachment.height: { + view = 'wide'; + break; + } + case attachment.height * 1.2 > attachment.width: { + view = 'tall'; + break; + } + } + + out.push({ + src, + alt, + view, + during_challenge: pin.timestamp.startsWith(`${year}-01-`), + }); + } + + out.sort((a, b) => { + const na = a.alt.match(/^(\d+) /); + const nb = b.alt.match(/^(\d+) /); + return parseInt(na) - parseInt(nb); + }); + + console.log(JSON.stringify(out, null, 2)); +} + +main(); diff --git a/src/routes/(site)/genuary/+server.ts b/src/routes/(site)/genuary/+server.ts new file mode 100644 index 000000000..3fd27b6d5 --- /dev/null +++ b/src/routes/(site)/genuary/+server.ts @@ -0,0 +1,33 @@ +import type { RequestEvent } from '@sveltejs/kit'; + +/** + * The server-side load function for Genuary. + * + * Redirects to the latest year on file. + */ +export async function GET({ url }: RequestEvent) { + const years = await import.meta.glob('./*/+page.svelte', { eager: true, as: 'raw' }); + const latest = findLatest(Object.keys(years).map(fromKey)); + const destination = new URL(`/genuary/${latest}`, url); + return Response.redirect(destination, 302); +} + +/** Example: key === "./2023/+page.svelte" */ +function fromKey(key: string): number { + const match = key.match(/\/(\d{4})\//); + if (!match) { + return -Infinity; + } + return Number(match[1]); +} + +function findLatest(items: number[]): number { + const found = items.sort((a, b) => a - b).pop(); + if (found === undefined) { + return EARLIEST; + } + + return found; +} + +const EARLIEST = 2023; diff --git a/src/routes/(site)/genuary/2023/+page.svelte b/src/routes/(site)/genuary/2023/+page.svelte new file mode 100644 index 000000000..5f94e06c0 --- /dev/null +++ b/src/routes/(site)/genuary/2023/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/src/routes/(site)/genuary/2023/+page.ts b/src/routes/(site)/genuary/2023/+page.ts new file mode 100644 index 000000000..50c721ede --- /dev/null +++ b/src/routes/(site)/genuary/2023/+page.ts @@ -0,0 +1,10 @@ +import type { CollagePageData } from '../collage'; +import { pageDataFrom } from '../collage'; + +import { default as GENUARY_2023_PIECES } from './data.json' assert { type: 'json' }; + +const GENUARY_2023_PAGE_DATA = pageDataFrom(GENUARY_2023_PIECES as CollagePageData['pieces']); + +export async function load(): Promise { + return GENUARY_2023_PAGE_DATA; +} diff --git a/src/routes/(site)/genuary/2023/data.json b/src/routes/(site)/genuary/2023/data.json new file mode 100644 index 000000000..4236b4dc8 --- /dev/null +++ b/src/routes/(site)/genuary/2023/data.json @@ -0,0 +1,317 @@ +[ + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1060129927053185085/genuary-2023-01.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1059699390224277625/genuary-2023-01-01.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1059326995521081364/mySketch.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1059340935126855720/genuary-2023-01.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1059339287574880280/day1_gif_AdobeExpress_1.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055678461643063296/1059303466650054746/ezgif.com-gif-maker_92.gif", + "alt": "1 Perfect loop Infinite loop endless GIFs", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679604775465001/1060065767573422080/image.png", + "alt": "2 Made in 10 minutes", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679604775465001/1059582594867527771/mySketch3.gif", + "alt": "2 Made in 10 minutes", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679604775465001/1059590463922897016/day2.mov", + "alt": "2 Made in 10 minutes", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679604775465001/1059590066319675392/Screen_Recording_2023-01-02_at_1.48.12_PM.mov", + "alt": "2 Made in 10 minutes", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679808719302657/1060071000282239056/junkyard_cut_compress.mp4", + "alt": "3 Glitch art", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679808719302657/1060065925765791825/Glitch_Main2.mp4", + "alt": "3 Glitch art", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679808719302657/1060064855626887249/day3.mov", + "alt": "3 Glitch art", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679808719302657/1060005976264146954/glitched_earth.gif", + "alt": "3 Glitch art", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679808719302657/1059919576034451587/Altirra64_2023-01-03_11-36-46.gif", + "alt": "3 Glitch art", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679922707898429/1060367160444203058/genuary-2023-04_3.gif", + "alt": "4 Intersections", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679922707898429/1060360425549606973/Jan_04.gif", + "alt": "4 Intersections", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679922707898429/1060359086954270892/day4.gif", + "alt": "4 Intersections", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679994073980938/1060780322540769330/debug_view_day5.mov", + "alt": "5 Debug view", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679994073980938/1060738785219973260/day5.gif", + "alt": "5 Debug view", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679994073980938/1060719469024006204/mandelbrot.gif", + "alt": "5 Debug view", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055679994073980938/1060718991422800003/mySketch5.gif", + "alt": "5 Debug view", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680102140231751/1061092183295406140/day6.png", + "alt": "6 Steal like an artist", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680102140231751/1061160303414485002/ezgif.com-gif-maker_96.gif", + "alt": "6 Steal like an artist", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680102140231751/1061070536228216832/Jan_062.gif", + "alt": "6 Steal like an artist", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680245740605450/1061950331430371408/day7.png", + "alt": "7 Sample a color palette from your favorite movie or album cover", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680245740605450/1061515218653548564/Screen_Recording_2023-01-07_at_9.20.04_PM.mov", + "alt": "7 Sample a color palette from your favorite movie or album cover", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680245740605450/1061481923219820576/2023-01-07_19-07-53.mp4", + "alt": "7 Sample a color palette from your favorite movie or album cover", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680245740605450/1061382773685895198/jan_07.gif", + "alt": "7 Sample a color palette from your favorite movie or album cover", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680591284150362/1062297535991001098/day8.png", + "alt": "8 Signed distance functions", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680591284150362/1061857594689462282/Screen_Recording_2023-01-08_at_8.01.11_PM.mov", + "alt": "8 Signed distance functions", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055680591284150362/1061807411616292884/Jan_0710.gif", + "alt": "8 Signed distance functions", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055683410619793488/1062602997235986452/lsystems.mp4", + "alt": "9 Plants", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055683410619793488/1062260480837685309/untitled.png", + "alt": "9 Plants", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055683410619793488/1062241414395732019/Screen_Shot_2023-01-09_at_9.26.16_PM.png", + "alt": "9 Plants", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055683410619793488/1062138597525962853/jan_09.gif", + "alt": "9 Plants", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055683785544450071/1063240365789937694/Jan_10.mp4", + "alt": "10 Generative music", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684466997207111/1063366293790670948/day11.png", + "alt": "11 Suprematism", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684466997207111/1063214240187568169/image.png", + "alt": "11 Suprematism", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684466997207111/1062975398905909268/firefox_2023-01-11_22-03-04.gif", + "alt": "11 Suprematism", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684466997207111/1062941102811922462/Jan_11.png", + "alt": "11 Suprematism", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684466997207111/1062931497050443787/chrome_2023-01-11_19-09-35.gif", + "alt": "11 Suprematism", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684665329066055/1064367277316767744/day12.png", + "alt": "12 Tessellation", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684665329066055/1063367312863928410/chrome_ac3WUoeFs7.gif", + "alt": "12 Tessellation", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055684665329066055/1063227601746931762/Jan_12.png", + "alt": "12 Tessellation", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055685593784393789/1064367367632732241/day14.png", + "alt": "14 Aesemic", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055685593784393789/1063910953973977150/Jan_14.png", + "alt": "14 Aesemic", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686307403268126/1064366681813700688/day15.gif", + "alt": "15 Sine waves", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686307403268126/1064326517246677125/Jan_15.gif", + "alt": "15 Sine waves", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686490333651004/1065451924930965615/day16.gif", + "alt": "16 Reflection of a reflection", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686490333651004/1065047330002325644/Jan_16.gif", + "alt": "16 Reflection of a reflection", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686569480167494/1065767867062161418/image.png", + "alt": "17 A grid inside a grid inside a grid", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686569480167494/1065468317541859359/day17.gif", + "alt": "17 A grid inside a grid inside a grid", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686569480167494/1065047356216713257/Jan_17.gif", + "alt": "17 A grid inside a grid inside a grid", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686801370648618/1065409830866657323/image.png", + "alt": "18 Definitely not a grid", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686878101245953/1066960361024139306/day19.png", + "alt": "19 Black and white", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686878101245953/1065717906664607865/Jan_19.gif", + "alt": "19 Black and white", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686946317418577/1066149998150037595/image.png", + "alt": "20 Art Deco", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055686946317418577/1066149604929843210/image.png", + "alt": "20 Art Deco", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055687712650297365/1066549945609502790/IMG_2041.jpg", + "alt": "21 Persian rug", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055687861762015293/1067620928978161735/image.png", + "alt": "22 Shadows", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055688868474671216/1068403708704079882/Screenshot_2023-01-26_at_9.34.13_PM.png", + "alt": "26 My kid could have made that", + "view": "normal" + }, + { + "src": "https://cdn.discordapp.com/attachments/1055689379592548393/1069793701326499860/image.png", + "alt": "30 Minimalism", + "view": "normal" + } +] diff --git a/src/routes/(site)/genuary/collage.svelte b/src/routes/(site)/genuary/collage.svelte new file mode 100644 index 000000000..04ee3d1b9 --- /dev/null +++ b/src/routes/(site)/genuary/collage.svelte @@ -0,0 +1,176 @@ + + +
+
+ {#each data as d, i} + {@const alt = d.alt || d.src} + {@const ext = d.src.split('.').pop()} + + {/each} +
+
+ + + diff --git a/src/routes/(site)/genuary/collage.ts b/src/routes/(site)/genuary/collage.ts new file mode 100644 index 000000000..3fd25f822 --- /dev/null +++ b/src/routes/(site)/genuary/collage.ts @@ -0,0 +1,38 @@ +/** + * This is the output of the Genuary page. + */ +export interface CollagePageData { + pieces: CollagePiece[]; +} + +/** + * This is a piece on the Genuary page. + */ +export interface CollagePiece { + src: string; + during_challenge?: boolean; + view?: 'normal' | 'wide' | 'tall' | 'big'; + alt?: string; +} + +/** + * pageDataFrom is a helper function to create a page data object from a list of + * pieces. + */ +export function pageDataFrom(pieces: CollagePiece[]): CollagePageData { + return { + pieces: pieces.map(({ src, alt, view, during_challenge }) => { + const data: CollagePiece = { src, alt, during_challenge: during_challenge ?? false }; + switch (view) { + case 'normal': + case 'wide': + case 'tall': + case 'big': { + data.view = view; + } + } + + return data; + }), + }; +} diff --git a/src/routes/(site)/genuary/dialog.svelte b/src/routes/(site)/genuary/dialog.svelte new file mode 100644 index 000000000..ed36b0cf5 --- /dev/null +++ b/src/routes/(site)/genuary/dialog.svelte @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + diff --git a/src/routes/(site)/genuary/genuary-page.svelte b/src/routes/(site)/genuary/genuary-page.svelte new file mode 100644 index 000000000..457540ca0 --- /dev/null +++ b/src/routes/(site)/genuary/genuary-page.svelte @@ -0,0 +1,71 @@ + + + + Genuary {year} | ACM at CSUF + + + + + +

Genuary {year}

+

+ Our chapter members' take on the Genuary challenge. +
+
+ + Genuary is a challenge to create a piece of generative art every day in January. +
+
+ The artworks shared by members of our club throughout the course of Genuary 2023 are listed below. +
+
+
+ Last updated January 31st, 2023 +

+
+ + + + + + + + +
+ +
+
+ + + + diff --git a/src/routes/(site)/genuary/media.svelte b/src/routes/(site)/genuary/media.svelte new file mode 100644 index 000000000..d08659c5b --- /dev/null +++ b/src/routes/(site)/genuary/media.svelte @@ -0,0 +1,64 @@ + + + + + diff --git a/src/routes/(site)/genuary/page.test.ts b/src/routes/(site)/genuary/page.test.ts new file mode 100644 index 000000000..ae105ad6d --- /dev/null +++ b/src/routes/(site)/genuary/page.test.ts @@ -0,0 +1,8 @@ +import { expect, test } from '@playwright/test'; + +test.describe.configure({ mode: 'parallel' }); + +test('genuary page has expected h1', async ({ page }) => { + await page.goto('/genuary/placekitten'); + expect(await page.textContent('h1')).toBe('Genuary Placekitten'); +}); diff --git a/src/routes/(site)/genuary/placekitten/+page.svelte b/src/routes/(site)/genuary/placekitten/+page.svelte new file mode 100644 index 000000000..54de2c2d4 --- /dev/null +++ b/src/routes/(site)/genuary/placekitten/+page.svelte @@ -0,0 +1,8 @@ + + + diff --git a/src/routes/(site)/genuary/placekitten/+page.ts b/src/routes/(site)/genuary/placekitten/+page.ts new file mode 100644 index 000000000..3b448be48 --- /dev/null +++ b/src/routes/(site)/genuary/placekitten/+page.ts @@ -0,0 +1,13 @@ +import type { CollagePageData } from '../collage'; + +import { pageDataFrom } from '../collage'; + +import { default as GENUARY_PLACEKITTEN_PIECES } from './data.json' assert { type: 'json' }; + +const GENUARY_PLACEKITTEN_PAGE_DATA = pageDataFrom( + GENUARY_PLACEKITTEN_PIECES as CollagePageData['pieces'] +); + +export async function load(): Promise { + return GENUARY_PLACEKITTEN_PAGE_DATA; +} diff --git a/src/routes/(site)/genuary/placekitten/data.json b/src/routes/(site)/genuary/placekitten/data.json new file mode 100644 index 000000000..05c21a444 --- /dev/null +++ b/src/routes/(site)/genuary/placekitten/data.json @@ -0,0 +1,57 @@ +[ + { + "src": "https://placekitten.com/g/200/300", + "alt": "A picture of a fluffy white cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/350/250", + "alt": "A picture of a black and white cat", + "view": "wide" + }, + { + "src": "https://placekitten.com/g/300/400", + "alt": "A picture of a Siamese cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/350/450", + "alt": "A picture of a Scottish Fold cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/500/400", + "alt": "A picture of a Maine Coon cat", + "view": "wide" + }, + { + "src": "https://placekitten.com/g/800/1200", + "alt": "A picture of a Maine Coon cat", + "view": "big" + }, + { + "src": "https://placekitten.com/g/450/550", + "alt": "A picture of a Persian cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/500/600", + "alt": "A picture of a Sphynx cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/650/550", + "alt": "A picture of a British Shorthair cat", + "view": "wide" + }, + { + "src": "https://placekitten.com/g/600/700", + "alt": "A picture of a Bengal cat", + "view": "tall" + }, + { + "src": "https://placekitten.com/g/650/750", + "alt": "A picture of a Russian Blue cat", + "view": "tall" + } +] diff --git a/src/routes/(site)/why-join.svelte b/src/routes/(site)/why-join.svelte index 06927cfa7..2c606251e 100644 --- a/src/routes/(site)/why-join.svelte +++ b/src/routes/(site)/why-join.svelte @@ -1,6 +1,6 @@
- Social event with ACM Chapter members. + Social event with ACM Chapter members.

Your journey into tech starts here