From cc0860cd02b7811a9f02bddef8c040cf887e6fb6 Mon Sep 17 00:00:00 2001 From: Niko Date: Mon, 7 Apr 2025 11:49:41 +0200 Subject: [PATCH 1/9] Add sessions and speakers with preloaded images. --- .gitignore | 10 ++ Makefile | 3 +- astro.config.mjs | 7 +- package.json | 4 + pnpm-lock.yaml | 29 +++++ .../assets/preloaded}/.gitkeep | 0 public/favicon.ico | Bin 0 -> 57986 bytes public/placeholder.png | Bin 0 -> 5474 bytes scripts/download-data.py | 79 -------------- src/assets/placeholder.png | Bin 0 -> 5474 bytes src/assets/placeholder.svg | 1 + src/components/header/header-actions.astro | 1 - src/components/keynoters/keynoter.astro | 2 - src/components/schedule/speakers.astro | 2 +- src/components/sections/prague.astro | 1 - src/components/sections/updates.astro | 1 - src/components/session-speakers.astro | 2 +- src/components/ui/Headline.astro | 28 ++++- src/components/ui/Markdown.astro | 14 +++ src/content/config.ts | 100 ++++++++++++++++- src/content/speakers/.gitkeep | 0 src/data/links.json | 6 +- src/pages/session/[slug].astro | 35 +++--- src/pages/sessions.astro | 101 ++++++++++++++++++ src/pages/speaker/[slug].astro | 64 +++++------ src/pages/speakers.astro | 93 ++++++++++++++++ tsconfig.json | 1 + 27 files changed, 443 insertions(+), 141 deletions(-) rename {src/content/sessions => public/assets/preloaded}/.gitkeep (100%) create mode 100644 public/favicon.ico create mode 100644 public/placeholder.png delete mode 100644 scripts/download-data.py create mode 100644 src/assets/placeholder.png create mode 100644 src/assets/placeholder.svg create mode 100644 src/components/ui/Markdown.astro delete mode 100644 src/content/speakers/.gitkeep create mode 100644 src/pages/sessions.astro create mode 100644 src/pages/speakers.astro diff --git a/.gitignore b/.gitignore index 694c8bf36..1a172417c 100644 --- a/.gitignore +++ b/.gitignore @@ -128,6 +128,7 @@ dmypy.json .pyre/ .vscode/ +.idea/ out/ .next/ .DS_Store @@ -138,3 +139,12 @@ storybook-static/ # Sentry Auth Token .sentryclirc .astro + +# EuroPython website +src/content/speakers +src/content/sessions +src/content/days +src/data/speakers.json +src/data/sessions.json +src/data/schedule.json +public/cache/ diff --git a/Makefile b/Makefile index 381952333..e434ba371 100644 --- a/Makefile +++ b/Makefile @@ -19,10 +19,10 @@ BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD) # Replace "/" and other non-alphanumeric characters with "-" SAFE_BRANCH := $(shell echo "$(BRANCH)" | sed 's/[^A-Za-z0-9-]/-/g') FORCE_DEPLOY ?= false +SITE_URL ?= "https://$(SAFE_BRANCH).ep-preview.click" .PHONY: build deploy dev clean install - safe_branch: @echo $(SAFE_BRANCH) @@ -47,6 +47,7 @@ build: preview: RELEASES_DIR = $(VPS_PREVIEW_PATH)/$(SAFE_BRANCH)/releases preview: TARGET = $(RELEASES_DIR)/$(TIMESTAMP) preview: + @echo "Preview site URL: $(SITE_URL)" # Output preview URL echo $(TARGET) @echo "\n\n**** Deploying preview of a branch '$(BRANCH)' (safe: $(SAFE_BRANCH)) to $(TARGET)...\n\n" $(REMOTE_CMD) "mkdir -p $(TARGET)" diff --git a/astro.config.mjs b/astro.config.mjs index bdd7f84d3..cf8ded4da 100644 --- a/astro.config.mjs +++ b/astro.config.mjs @@ -10,6 +10,7 @@ import rehypeAutolinkHeadings from "rehype-autolink-headings"; import metaTags from "astro-meta-tags"; import pagefind from "astro-pagefind"; import deleteUnusedImages from "astro-delete-unused-images"; +import preload from "astro-preload"; // https://astro.build/config export default defineConfig({ @@ -59,9 +60,9 @@ export default defineConfig({ "/sponsor/": "/sponsorship/sponsor/", "/voting/": "/programme/voting/", "/wasm-summit/": "/programme/wasm-summit/", - "/sessions/": "/programme/sessions/", }, integrations: [ + preload(), mdx(), sitemap(), react(), @@ -76,6 +77,10 @@ export default defineConfig({ build: { minify: true, }, + image: { + remotePatterns: [{ protocol: "https" }], + domains: ["programme.europython.eu", "placehold.co"], + }, experimental: { svg: true, }, diff --git a/package.json b/package.json index 443d0c0ac..83864ba4a 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,13 @@ "astro-delete-unused-images": "^1.0.3", "astro-meta-tags": "^0.3.1", "astro-pagefind": "^1.8.1", + "astro-preload": "^1.1.2", "clsx": "^2.1.1", "date-fns": "^4.1.0", "date-fns-tz": "^3.2.0", "hastscript": "^9.0.0", + "js-yaml": "^4.1.0", + "marked": "^15.0.7", "pagefind": "^1.3.0", "react": "^19.1.0", "react-dom": "^19.1.0", @@ -40,6 +43,7 @@ "typescript": "^5.8.3" }, "devDependencies": { + "@types/js-yaml": "^4.0.9", "prettier": "^3.4.2", "prettier-plugin-astro": "^0.14.1" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b0594a015..1b9a1447a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -50,6 +50,9 @@ importers: astro-pagefind: specifier: ^1.8.1 version: 1.8.3(astro@5.5.2(jiti@1.21.7)(rollup@4.39.0)(typescript@5.8.3)(yaml@2.7.0)) + astro-preload: + specifier: ^1.1.2 + version: 1.1.2 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -62,6 +65,12 @@ importers: hastscript: specifier: ^9.0.0 version: 9.0.1 + js-yaml: + specifier: ^4.1.0 + version: 4.1.0 + marked: + specifier: ^15.0.7 + version: 15.0.7 pagefind: specifier: ^1.3.0 version: 1.3.0 @@ -90,6 +99,9 @@ importers: specifier: ^5.8.3 version: 5.8.3 devDependencies: + '@types/js-yaml': + specifier: ^4.0.9 + version: 4.0.9 prettier: specifier: ^3.4.2 version: 3.5.3 @@ -1160,6 +1172,9 @@ packages: '@types/hast@3.0.4': resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + '@types/js-yaml@4.0.9': + resolution: {integrity: sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==} + '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -1299,6 +1314,9 @@ packages: peerDependencies: astro: ^2.0.4 || ^3 || ^4 || ^5 + astro-preload@1.1.2: + resolution: {integrity: sha512-96UHdATRp/K8iVysylYu0V5QMD3FXBqqj2+cUwHOmc0/+lstY5Xnb+A5UgmecbrfHpwMws5SQaOYZ1c63cQu/A==} + astro@5.5.2: resolution: {integrity: sha512-SOTJxB8mqxe/KEYEJiLIot0YULiCffyfTEclwmdeaASitDJ7eLM/KYrJ9sx3U5hq9GVI17Z4Y0P/1T2aQ0ZN3A==} engines: {node: ^18.17.1 || ^20.3.0 || >=22.0.0, npm: '>=9.6.5', pnpm: '>=7.1.0'} @@ -1877,6 +1895,11 @@ packages: markdown-table@3.0.4: resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + marked@15.0.7: + resolution: {integrity: sha512-dgLIeKGLx5FwziAnsk4ONoGwHwGPJzselimvlVskE9XLN4Orv9u2VA3GWw/lYUqjfA0rUT/6fqKwfZJapP9BEg==} + engines: {node: '>= 18'} + hasBin: true + mdast-util-definitions@6.0.0: resolution: {integrity: sha512-scTllyX6pnYNZH/AIp/0ePz6s4cZtARxImwoPJ7kS42n+MnVsI4XbnG6d4ibehRIldYMWM2LD7ImQblVhUejVQ==} @@ -3958,6 +3981,8 @@ snapshots: dependencies: '@types/unist': 3.0.3 + '@types/js-yaml@4.0.9': {} + '@types/mdast@4.0.4': dependencies: '@types/unist': 3.0.3 @@ -4110,6 +4135,8 @@ snapshots: pagefind: 1.3.0 sirv: 3.0.1 + astro-preload@1.1.2: {} + astro@5.5.2(jiti@1.21.7)(rollup@4.39.0)(typescript@5.8.3)(yaml@2.7.0): dependencies: '@astrojs/compiler': 2.11.0 @@ -4854,6 +4881,8 @@ snapshots: markdown-table@3.0.4: {} + marked@15.0.7: {} + mdast-util-definitions@6.0.0: dependencies: '@types/mdast': 4.0.4 diff --git a/src/content/sessions/.gitkeep b/public/assets/preloaded/.gitkeep similarity index 100% rename from src/content/sessions/.gitkeep rename to public/assets/preloaded/.gitkeep diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8aed2cd00f9515080e6c4aef1edae45e2222fa98 GIT binary patch literal 57986 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%0A4n)i$sNyW1g*#rw9Pk+A_T?r$ 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 ZkxJ%AR#R2fhu#5xnyR`gl}eVd{{cq(<=p@P literal 0 HcmV?d00001 diff --git a/public/placeholder.png b/public/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..25d8b896440e179fb8bc5a9ac14e985748df2153 GIT binary patch literal 5474 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 None: - if output_dir.exists(): - shutil.rmtree(output_dir) - - output_dir.mkdir(parents=True, exist_ok=True) - - for key, value in data.items(): - filename = f"{key}.mdx" - path = output_dir / filename - - content = value.pop(content_key) or "" - - content = content.replace("<3", "❤️") - - frontmatter = yaml.dump(value, sort_keys=True) - - with path.open("w", encoding="utf-8") as f: - f.write(f"---\n{frontmatter}---\n\n{content}") - - -def download_data(url: str) -> dict[str, Any]: - with httpx.Client() as client: - response = client.get(url) - response.raise_for_status() - data = response.json() - - return data - - -def download() -> None: - speakers = download_data(SPEAKERS_URL) - sessions = download_data(SESSIONS_URL) - schedule = download_data(SCHEDULE_DATA) - - for session in sessions.values(): - session["speakers"] = [ - speakers[speaker_id]["slug"] for speaker_id in session.get("speakers", []) - ] - - for speaker in speakers.values(): - speaker["submissions"] = [ - sessions[session_id]["slug"] - for session_id in speaker.get("submissions", []) - if session_id in sessions - ] - - write_mdx(sessions, ROOT / "src/content/sessions", "abstract") - write_mdx(speakers, ROOT / "src/content/speakers", "biography") - - for day, data in schedule["days"].items(): - path = ROOT / f"src/content/days/{day}.json" - with path.open("w", encoding="utf-8") as f: - json.dump(data, f, indent=2) - - -download() diff --git a/src/assets/placeholder.png b/src/assets/placeholder.png new file mode 100644 index 0000000000000000000000000000000000000000..25d8b896440e179fb8bc5a9ac14e985748df2153 GIT binary patch literal 5474 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 \ No newline at end of file diff --git a/src/components/header/header-actions.astro b/src/components/header/header-actions.astro index 0138c1dfa..60bcfdb9c 100644 --- a/src/components/header/header-actions.astro +++ b/src/components/header/header-actions.astro @@ -1,5 +1,4 @@ --- -import ButtonLink from "../button-link/button-link.astro"; import Button from "@ui/Button.astro"; import HeaderButton from "./header-button.astro"; import Search from "astro-pagefind/components/Search"; diff --git a/src/components/keynoters/keynoter.astro b/src/components/keynoters/keynoter.astro index cc8c91af0..9767b0337 100644 --- a/src/components/keynoters/keynoter.astro +++ b/src/components/keynoters/keynoter.astro @@ -19,11 +19,9 @@ export interface Props { const { name, - slug, tagline, image, placeholder, - order, class: className, } = Astro.props; diff --git a/src/components/schedule/speakers.astro b/src/components/schedule/speakers.astro index a69b4015f..33e851e80 100644 --- a/src/components/schedule/speakers.astro +++ b/src/components/schedule/speakers.astro @@ -20,7 +20,7 @@ const speakers = Astro.props.speakers { speakers.map((speaker, index) => ( - + {speaker.data.name} {index < speakers.length - 1 ? ", " : ""} diff --git a/src/components/sections/prague.astro b/src/components/sections/prague.astro index 0988fb2bb..b9419bf27 100644 --- a/src/components/sections/prague.astro +++ b/src/components/sections/prague.astro @@ -1,6 +1,5 @@ --- import Button from "@ui/Button.astro"; -import { Title } from "@components/typography/title"; import { Image } from "astro:assets"; import pragueImage from "@assets/prague2.jpg"; import Logo from "@assets/prague.svg"; diff --git a/src/components/sections/updates.astro b/src/components/sections/updates.astro index 4d9887f0c..f9b6a3be9 100644 --- a/src/components/sections/updates.astro +++ b/src/components/sections/updates.astro @@ -1,7 +1,6 @@ --- import { CardContainer } from "@components/card/card-container"; -import { Title } from "@components/typography/title"; import DeadlineCard from "@components/deadline-card.astro"; import Headline from "@ui/Headline.astro" import { getCollection } from "astro:content"; diff --git a/src/components/session-speakers.astro b/src/components/session-speakers.astro index 4baa0fbad..fc2be7e80 100644 --- a/src/components/session-speakers.astro +++ b/src/components/session-speakers.astro @@ -21,7 +21,7 @@ const speakers = await getEntries( { speakers.map((speaker, index) => ( - + {speaker.data.name} {index < speakers.length - 1 ? ", " : ""} diff --git a/src/components/ui/Headline.astro b/src/components/ui/Headline.astro index fae8a6d54..4d26fdfc4 100644 --- a/src/components/ui/Headline.astro +++ b/src/components/ui/Headline.astro @@ -6,7 +6,7 @@ const isAnchor = !!id; const isLink = !!href; --- - + {isAnchor && ( {Title} @@ -14,3 +14,29 @@ const isLink = !!href; )} {isLink && {Title} } + + diff --git a/src/components/ui/Markdown.astro b/src/components/ui/Markdown.astro new file mode 100644 index 000000000..ca465b4a9 --- /dev/null +++ b/src/components/ui/Markdown.astro @@ -0,0 +1,14 @@ +--- +import { marked } from 'marked'; + +interface Props { + content: string; +} + +const { content } = Astro.props; +const html = marked.parse(content); +--- + +
+
+
diff --git a/src/content/config.ts b/src/content/config.ts index 33e828647..633ff3a69 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -1,4 +1,31 @@ import { defineCollection, reference, z } from "astro:content"; +import fs from "fs/promises"; +import path from "path"; + +const CACHE_DIR = ".cache/data"; + +async function ensureCacheDir() { + await fs.mkdir(CACHE_DIR, { recursive: true }); +} + +async function fetchWithCache(url: string, filename: string): Promise { + const filePath = path.join(CACHE_DIR, filename); + + try { + // Return cached if available + const data = await fs.readFile(filePath); + console.log(`Fetch from cache: ${filePath}`); + return data; + } catch { + const res = await fetch(url); + if (!res.ok) throw new Error(`Failed to fetch: ${url}`); + + const arrayBuffer = await res.arrayBuffer(); + const buffer: Buffer = Buffer.from(arrayBuffer); + await fs.writeFile(filePath, new Uint8Array(buffer)); + return buffer; + } +} const tiers = [ "Keystone", @@ -54,12 +81,67 @@ const keynoters = defineCollection({ }), }); +// Shared data fetching function +async function getCollectionsData() { + // Only fetch if not already cached + await ensureCacheDir(); + + const speakersBuffer = await fetchWithCache( + "https://gist.github.com/egeakman/469f9abb23a787df16d8787f438dfdb6/raw/62d2b7e77c1b078a0e27578c72598a505f9fafbf/speakers.json", + "speakers.json" + ); + + const sessionsBuffer = await fetchWithCache( + "https://gist.githubusercontent.com/egeakman/eddfb15f32ae805e8cfb4c5856ae304b/raw/466f8c20c17a9f6c5875f973acaec60e4e4d0fae/sessions.json", + "sessions.json" + ); + + const cachedSpeakersData = JSON.parse(speakersBuffer.toString("utf-8")); + const cachedSessionsData = JSON.parse(sessionsBuffer.toString("utf-8")); + + // Create indexed versions for efficient lookups + const speakersById = Object.entries(cachedSpeakersData).reduce( + (acc, [id, speaker]: [string, any]) => { + acc[id] = { id, ...speaker }; + return acc; + }, + {} as Record + ); + + const sessionsById = Object.entries(cachedSessionsData).reduce( + (acc, [id, session]: [string, any]) => { + acc[id] = { id, ...session }; + return acc; + }, + {} as Record + ); + + return { + speakersData: cachedSpeakersData, + sessionsData: cachedSessionsData, + speakersById, + sessionsById, + }; +} + const speakers = defineCollection({ - type: "content", + loader: async (): Promise => { + const { speakersData, sessionsById } = await getCollectionsData(); + + return Object.values(speakersData).map((speaker: any) => ({ + id: speaker.slug, + ...speaker, + submissions: (speaker.submissions || []) + .filter((sessionId: string) => sessionId in sessionsById) + .map((sessionId: string) => sessionsById[sessionId].slug), + })); + }, schema: z.object({ code: z.string(), name: z.string(), + slug: z.string(), avatar: z.string(), + biography: z.string().nullable(), submissions: z.array(reference("sessions")), affiliation: z.string().nullable(), homepage: z.string().nullable(), @@ -71,10 +153,22 @@ const speakers = defineCollection({ }); const sessions = defineCollection({ - type: "content", + loader: async (): Promise => { + const { sessionsData, speakersById } = await getCollectionsData(); + + return Object.values(sessionsData).map((session: any) => ({ + id: session.slug, + ...session, + speakers: (session.speakers || []) + .filter((speakerId: string) => speakerId in speakersById) + .map((speakerId: string) => speakersById[speakerId].slug), + })); + }, schema: z.object({ code: z.string(), title: z.string(), + slug: z.string(), + abstract: z.string().nullable(), speakers: z.array(reference("speakers")), session_type: z.string(), track: z.string().nullable(), @@ -85,7 +179,7 @@ const sessions = defineCollection({ .nullable(), duration: z.string(), level: z.enum(["beginner", "intermediate", "advanced"]), - delivery: z.enum(["in-person", "remote"]), + delivery: z.enum(["in-person", "remote", ""]), room: z.string().nullable(), start: z.string().nullable(), end: z.string().nullable(), diff --git a/src/content/speakers/.gitkeep b/src/content/speakers/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/data/links.json b/src/data/links.json index fa02dcbd8..9faae6aba 100644 --- a/src/data/links.json +++ b/src/data/links.json @@ -5,7 +5,7 @@ "items": [ { "name": "Sessions Previews", - "path": "/programme/sessions" + "path": "/sessions" }, { "name": "Tracks", @@ -19,6 +19,10 @@ "name": "Community Voting", "path": "/programme/voting" }, + { + "name": "List of Speakers", + "path": "/speakers" + }, { "name": "Speaker Mentorship", "path": "/programme/mentorship" diff --git a/src/pages/session/[slug].astro b/src/pages/session/[slug].astro index a04265334..c467c9d15 100644 --- a/src/pages/session/[slug].astro +++ b/src/pages/session/[slug].astro @@ -5,11 +5,13 @@ import Prose from "../../components/prose/prose.astro"; import { Separator } from "../../components/separator/separator"; import { formatInTimeZone } from "date-fns-tz"; import { YouTube } from "@astro-community/astro-embed-youtube"; +import { Picture } from "astro:assets"; +import Markdown from "@ui/Markdown.astro"; export async function getStaticPaths() { const sessions = await getCollection("sessions"); return sessions.map((entry) => ({ - params: { slug: entry.slug }, + params: { slug: entry.id}, props: { entry }, })); } @@ -18,15 +20,9 @@ export async function getStaticPaths() { const sessions = await getCollection("sessions"); const { entry } = Astro.props; -const { Content } = await entry.render(); const speakers = await getEntries(entry.data.speakers); -for (const speaker of speakers) { - // @ts-ignore - speaker.Content = (await speaker.render()).Content; -} - // Resolve session codes to session data const resolveSessions = (codes: string[]) => codes.map((code) => sessions.find((s) => s?.data?.code === code)); @@ -106,7 +102,7 @@ const nextSessionsOrdered = sameRoomNextSession

Abstract

- +
{ @@ -155,11 +151,15 @@ const nextSessionsOrdered = sameRoomNextSession
{speaker.data.avatar ? (
- {speaker.data.name} +
+ +
) : ( @@ -201,7 +200,7 @@ const nextSessionsOrdered = sameRoomNextSession {parallelSessions.map((session: any) => (
  • - + {session.data.title} @@ -219,7 +218,7 @@ const nextSessionsOrdered = sameRoomNextSession {nextSessionsOrdered.map((session: any) => (
  • {session.data.title} diff --git a/src/pages/sessions.astro b/src/pages/sessions.astro new file mode 100644 index 000000000..4b1b477d2 --- /dev/null +++ b/src/pages/sessions.astro @@ -0,0 +1,101 @@ +--- +import { getCollection } from "astro:content"; +import Layout from "../layouts/Layout.astro"; +import Prose from "../components/prose/prose.astro"; +import { Separator } from "../components/separator/separator"; + +// Fetch all speaker entries +const sessionsCollection = await getCollection("sessions"); + +// Define the type for the groups object +type Session = { + id: string; + data: { + title: string; + session_type: string; + }; +}; + +type Groups = { + [key: string]: Session[]; +}; + +const groups: Groups = sessionsCollection + .filter((session: Session) => !!session.data.session_type) + .reduce((acc: Groups, session: Session) => { + const sessionType = session.data.session_type; + if (!acc[sessionType]) { + acc[sessionType] = []; + } + acc[sessionType].push(session); + return acc; + }, {} as Groups); + +// Sort session types alphabetically +const sessionTypes = Object.keys(groups).sort((a, b) => a.localeCompare(b)); + +const title = "Sessions"; + +const description = + "List of all confirmed sessions for the conference, sorted by session type."; +--- + + +
    + +

    Sessions

    +

    + Check out all the sessions at the conference. You can filter them by track, type, and level. +

    + + +

    Go to session type:

    +
    + +
      + { + sessionTypes.map((sessionType, index) => ( + <> +
      +

      + {sessionType} +

      + +
        + {groups[sessionType] + .sort((a, b) => a.data.title.localeCompare(b.data.title)) + .map((session) => ( +
      • + + {session.data.title} + +
      • + ))} +
      +
      + + {index !== sessionTypes.length - 1 ? ( + + ) : ( +
      + )} + + )) + } +
    + +
    + diff --git a/src/pages/speaker/[slug].astro b/src/pages/speaker/[slug].astro index cd4dea0d3..211d7c2a9 100644 --- a/src/pages/speaker/[slug].astro +++ b/src/pages/speaker/[slug].astro @@ -2,20 +2,36 @@ import { getCollection, getEntries } from "astro:content"; import Layout from "../../layouts/Layout.astro"; import Prose from "../../components/prose/prose.astro"; +import { Image } from "astro:assets"; +import Markdown from "@ui/Markdown.astro"; +import Headline from "@ui/Headline.astro"; + export async function getStaticPaths() { const entries = await getCollection("speakers"); return entries.map((entry) => ({ - params: { slug: entry.slug }, + params: { slug: entry.id }, props: { entry }, })); } const { entry } = Astro.props; -const { Content } = await entry.render(); const sessions = await getEntries(entry.data.submissions); +function isUrl(str: string): boolean { + str = ensureHttps(str); + const regex = /^(https?:\/\/)?([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,6})([\/\w@.-]*)$/; + return regex.test(str); +} + +function ensureHttps(str: string): string { + if (!/^https?:\/\//i.test(str)) { + return "https://" + str; + } + return str; +} + // Get @username from Twitter URL function getTwitterUsername(url: string): string | undefined { const username = url.split("/").pop(); @@ -46,50 +62,38 @@ function getGitHosting(url: string): string | undefined { } return undefined; } - -function isUrl(str: string): boolean { - str = ensureHttps(str); - const regex = - /^(https?:\/\/)?([a-zA-Z0-9.-]+)\.([a-zA-Z]{2,6})([\/\w@.-]*)*(\?.*)?$/; - return regex.test(str); -} - -function ensureHttps(str: string): string { - if (!/^https?:\/\//i.test(str)) { - return `https://${str}`; - } - return str; -} --- -
    -

    - {entry.data.name} -

    +
    + +
    { - entry.data.avatar && ( -
    - +
    + {entry.data.name}
    +
    ) } +
    { - entry.body ? ( + entry.data.biography ? ( <>

    Biography

    - + ) : null @@ -211,7 +215,7 @@ function ensureHttps(str: string): string {
  • {session.data.title} diff --git a/src/pages/speakers.astro b/src/pages/speakers.astro new file mode 100644 index 000000000..f72dc19e6 --- /dev/null +++ b/src/pages/speakers.astro @@ -0,0 +1,93 @@ +--- +import { getCollection } from "astro:content"; +import Layout from "../layouts/Layout.astro"; +import Prose from "../components/prose/prose.astro"; +import { Separator } from "../components/separator/separator"; + +// Fetch all speaker entries +const speakersCollection = await getCollection("speakers"); + +// Define the type for the groups object +type Speaker = { + id: string; + data: { + name: string; + }; +}; + +type Groups = { + [key: string]: Speaker[]; +}; + +// Group speakers by the first letter of their name +const groups: Groups = speakersCollection + .filter((speaker: Speaker) => !!speaker.data.name) + .reduce((acc: Groups, speaker: Speaker) => { + const letter = speaker.data.name[0].toUpperCase(); + if (!acc[letter]) { + acc[letter] = []; + } + acc[letter].push(speaker); + return acc; + }, {} as Groups); + +const letters = Object.keys(groups).sort((a, b) => a.localeCompare(b)); + +const title = "Speakers"; + +const description = + "Alphabetical list of all confirmed speakers for the conference"; +--- + + +
    + +

    Speakers

    +
    + +
    + { + letters.map((letter) => ( +

    + {letter} +

    + )) + } +
    + +
      + { + letters.map((letter, index) => ( + <> +
      +

      + {letter} +

      + +
        + {groups[letter] + .sort((a, b) => a.data.name.localeCompare(b.data.name)) + .map((speaker) => ( +
      • + + {speaker.data.name} + +
      • + ))} +
      +
      + + {index !== letters.length - 1 ? ( + + ) : ( +
      + )} + + )) + } +
    +
    +
    diff --git a/tsconfig.json b/tsconfig.json index 6046422f2..5ef51ac37 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,6 +2,7 @@ "extends": "astro/tsconfigs/strict", "compilerOptions": { "strictNullChecks": true, + "noImplicitAny": false, "jsx": "react-jsx", "jsxImportSource": "react", "paths": { From d37c81f3765062a02c627ac55155a9a89d05d6d7 Mon Sep 17 00:00:00 2001 From: Niko Date: Fri, 11 Apr 2025 16:09:37 +0200 Subject: [PATCH 2/9] Clean up gitignore. --- .gitignore | 6 ------ 1 file changed, 6 deletions(-) diff --git a/.gitignore b/.gitignore index 1a172417c..dde0a7c4f 100644 --- a/.gitignore +++ b/.gitignore @@ -141,10 +141,4 @@ storybook-static/ .astro # EuroPython website -src/content/speakers -src/content/sessions src/content/days -src/data/speakers.json -src/data/sessions.json -src/data/schedule.json -public/cache/ From c3d6e10ec6fe24d98c6a458a5a2b9df6aae9c53f Mon Sep 17 00:00:00 2001 From: Niko Date: Sat, 12 Apr 2025 11:16:09 +0200 Subject: [PATCH 3/9] Update sessions page following 2024 page. --- src/components/session-speakers.astro | 4 +- src/components/sessions/list-sessions.astro | 6 +- src/components/tag/tag.tsx | 4 +- src/content/pages/programme/sessions.mdx | 162 -------------------- src/pages/sessions.astro | 102 +++--------- 5 files changed, 29 insertions(+), 249 deletions(-) delete mode 100644 src/content/pages/programme/sessions.mdx diff --git a/src/components/session-speakers.astro b/src/components/session-speakers.astro index fc2be7e80..e429c46ac 100644 --- a/src/components/session-speakers.astro +++ b/src/components/session-speakers.astro @@ -3,7 +3,7 @@ import { getEntries } from "astro:content"; interface Props { speakers: Array<{ - slug: string; + id: string; collection: "speakers"; }>; } @@ -13,7 +13,7 @@ const { speakers: speakersEntries } = Astro.props; const speakers = await getEntries( speakersEntries.map((entry) => ({ collection: entry.collection, - id: entry.slug, + id: entry.id, })), ); --- diff --git a/src/components/sessions/list-sessions.astro b/src/components/sessions/list-sessions.astro index ad18289a5..4b1309cab 100644 --- a/src/components/sessions/list-sessions.astro +++ b/src/components/sessions/list-sessions.astro @@ -8,14 +8,14 @@ type Session = { data: { title: string; speakers: Array<{ + id: string; collection: "speakers"; - slug: string; }>; session_type: string; track: string; level: string; }; - slug: string; + id: string; }; --- @@ -30,7 +30,7 @@ type Session = { >

    - + {session.data.title}

    diff --git a/src/components/tag/tag.tsx b/src/components/tag/tag.tsx index 8d34d9fd3..7e705dcb2 100644 --- a/src/components/tag/tag.tsx +++ b/src/components/tag/tag.tsx @@ -11,8 +11,8 @@ export const Tag = ({ children, className, href }: TagProps) => { return (
  • From 3ca79dbb0d5d71fc371b4e3df007967c7dea4698 Mon Sep 17 00:00:00 2001 From: Niko Date: Sat, 12 Apr 2025 17:30:34 +0200 Subject: [PATCH 6/9] Add Bluesky usernames. --- src/content/config.ts | 3 ++- src/pages/speaker/[slug].astro | 19 +++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/content/config.ts b/src/content/config.ts index 633ff3a69..1dcd08359 100644 --- a/src/content/config.ts +++ b/src/content/config.ts @@ -87,7 +87,7 @@ async function getCollectionsData() { await ensureCacheDir(); const speakersBuffer = await fetchWithCache( - "https://gist.github.com/egeakman/469f9abb23a787df16d8787f438dfdb6/raw/62d2b7e77c1b078a0e27578c72598a505f9fafbf/speakers.json", + "https://gist.githubusercontent.com/nikoshell/d8efd41f90961cc6298519c0eec04843/raw/d13a7b1d35f61be1773404e7faf8395dd4862313/speakers.json", "speakers.json" ); @@ -148,6 +148,7 @@ const speakers = defineCollection({ gitx: z.string().nullable(), linkedin_url: z.string().url().nullable(), mastodon_url: z.string().url().nullable(), + bluesky_url: z.string().url().nullable(), twitter_url: z.string().url().nullable(), }), }); diff --git a/src/pages/speaker/[slug].astro b/src/pages/speaker/[slug].astro index 211d7c2a9..bd833339a 100644 --- a/src/pages/speaker/[slug].astro +++ b/src/pages/speaker/[slug].astro @@ -38,6 +38,12 @@ function getTwitterUsername(url: string): string | undefined { return (username ?? url).startsWith("@") ? username : `@${username}`; } +// Get @username from Twitter URL +function getBlueskyUsername(url: string): string | undefined { + const username = url.split("/").pop(); + return (username ?? url).startsWith("@") ? username : `@${username}`; +} + // Get @username@instance.tld from Mastodon URL function getMastodonUsername(url: string): string | null { const match = url.match(/https?:\/\/([^\/]+)\/@([^\/]+)(\/|\?|$)/); @@ -197,6 +203,19 @@ function getGitHosting(url: string): string | undefined { )} + {entry.data.bluesky_url && ( + <> +
    Bluesky
    +
    + + {getBlueskyUsername(entry.data.bluesky_url)} + +
    + + )} ) : null From b52a4a03bc0daef9c7852f62b96d9c6795e8f7c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Niko=C5=9B?= Date: Sat, 12 Apr 2025 17:42:03 +0200 Subject: [PATCH 7/9] Update src/data/links.json MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Mia Bajić <38294198+clytaemnestra@users.noreply.github.com> --- src/data/links.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/data/links.json b/src/data/links.json index 9faae6aba..4e37616e4 100644 --- a/src/data/links.json +++ b/src/data/links.json @@ -4,7 +4,7 @@ "name": "Programme", "items": [ { - "name": "Sessions Previews", + "name": "List of Sessions", "path": "/sessions" }, { From 75e93e060d96799902df446e462a866d591fd9a5 Mon Sep 17 00:00:00 2001 From: Mia Date: Sun, 13 Apr 2025 12:58:43 +0200 Subject: [PATCH 8/9] Add program & events overview --- src/content/pages/overview.mdx | 98 ++++++++++++++++++++++++++++++++++ src/data/links.json | 4 ++ 2 files changed, 102 insertions(+) create mode 100644 src/content/pages/overview.mdx diff --git a/src/content/pages/overview.mdx b/src/content/pages/overview.mdx new file mode 100644 index 000000000..c7af7b91f --- /dev/null +++ b/src/content/pages/overview.mdx @@ -0,0 +1,98 @@ +--- +title: Programme & Events Overview +subtitle: Explore everything happening at EuroPython 2025 +--- +### Programme & Events Overview + +The conference is organised into three phases: + +1. **Monday & Tuesday (14 & 15 July):** Tutorial Days +2. **Wednesday – Friday (16 – 18 July):** Main Conference Days +3. **Saturday & Sunday (19 & 20 July):** Sprint Days + +You can look forward to: + +* ~120 talks +* ~20 tutorials +* 5–6 keynotes +* Open spaces +* 3 summits +* Community organisers’ sessions +* PyLadies workshops +* Sprint projects +* Tutorials for less-represented groups in tech +* Beginner-friendly unconference day +* Social and networking events + +--- + +## Sessions + +### Talks +Talks are 30–45 minute presentations, held across six parallel tracks during the Main Conference Days. + +### Tutorials +Tutorials are 180-minute hands-on sessions focused on learning by doing, held during the Tutorial Days. + +#### List of sessions +Can be found on the [sessions page](/sessions). + +--- + +## Summits +Summits are full-day, topic-focused unconference-style gatherings for in-depth discussions, held during the Tutorial Days. + +#### List of summits +- [Rust Summit](/programme/rust-summit) +- [WASM Summit](/programme/wasm-summit) +- [C API Summit](/programme/c-api-summit) + +--- + +## Open Spaces +Open spaces are informal sessions where anyone can gather and discuss any topic. There will be a table at the venue — if there's a free slot, simply write down your topic, and others can join the conversation. + +#### List of spaces +Will be published soon. + +--- + +## Sprints +Our sprint days offer a chance to contribute to open-source projects and collaborate with others in the community. Sprints take place over the weekend. + +#### List of participating projects +Will be published soon. + +--- + +## PyLadies Sessions +Workshops organised by and for PyLadies, focused on supporting less-represented groups in tech. + +#### List of sessions +Will be added soon. + +--- + +## Beginner’s Day Unconference Space +Running in parallel with the sprints, this unconference space is designed for beginners. It includes Q&A sessions, AMAs, and career-focused discussions with experienced professionals. + +#### List of sessions +Will be added soon. + +--- + +## Tutorials for Less-Represented Groups in Tech +Introductory tutorials designed to help beginners from less-represented groups start learning programming. + +#### List of tutorials +- Django Girls – more info coming soon +- Humble Data – more info coming soon + +--- + +## Events +Our events bring the community together for fun and connection throughout the conference. + +#### List of events +- Main social event – details coming soon +- Speakers’ dinner – details coming soon diff --git a/src/data/links.json b/src/data/links.json index 4e37616e4..0a552df3c 100644 --- a/src/data/links.json +++ b/src/data/links.json @@ -3,6 +3,10 @@ { "name": "Programme", "items": [ + { + "name": "Overview", + "path": "/overview" + }, { "name": "List of Sessions", "path": "/sessions" From 47a2834943aa42f1bbb5cce56c7fef5441b6a7aa Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Sun, 13 Apr 2025 11:04:19 +0000 Subject: [PATCH 9/9] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- src/content/pages/overview.mdx | 72 +++++++++++++++++----------------- 1 file changed, 36 insertions(+), 36 deletions(-) diff --git a/src/content/pages/overview.mdx b/src/content/pages/overview.mdx index c7af7b91f..48ffe7748 100644 --- a/src/content/pages/overview.mdx +++ b/src/content/pages/overview.mdx @@ -1,71 +1,71 @@ --- -title: Programme & Events Overview -subtitle: Explore everything happening at EuroPython 2025 +title: Programme & Events Overview +subtitle: Explore everything happening at EuroPython 2025 --- -### Programme & Events Overview +### Programme & Events Overview The conference is organised into three phases: -1. **Monday & Tuesday (14 & 15 July):** Tutorial Days -2. **Wednesday – Friday (16 – 18 July):** Main Conference Days +1. **Monday & Tuesday (14 & 15 July):** Tutorial Days +2. **Wednesday – Friday (16 – 18 July):** Main Conference Days 3. **Saturday & Sunday (19 & 20 July):** Sprint Days You can look forward to: -* ~120 talks -* ~20 tutorials -* 5–6 keynotes -* Open spaces -* 3 summits -* Community organisers’ sessions -* PyLadies workshops -* Sprint projects -* Tutorials for less-represented groups in tech -* Beginner-friendly unconference day -* Social and networking events +* ~120 talks +* ~20 tutorials +* 5–6 keynotes +* Open spaces +* 3 summits +* Community organisers’ sessions +* PyLadies workshops +* Sprint projects +* Tutorials for less-represented groups in tech +* Beginner-friendly unconference day +* Social and networking events --- ## Sessions -### Talks +### Talks Talks are 30–45 minute presentations, held across six parallel tracks during the Main Conference Days. -### Tutorials +### Tutorials Tutorials are 180-minute hands-on sessions focused on learning by doing, held during the Tutorial Days. -#### List of sessions +#### List of sessions Can be found on the [sessions page](/sessions). --- -## Summits +## Summits Summits are full-day, topic-focused unconference-style gatherings for in-depth discussions, held during the Tutorial Days. -#### List of summits -- [Rust Summit](/programme/rust-summit) -- [WASM Summit](/programme/wasm-summit) -- [C API Summit](/programme/c-api-summit) +#### List of summits +- [Rust Summit](/programme/rust-summit) +- [WASM Summit](/programme/wasm-summit) +- [C API Summit](/programme/c-api-summit) --- -## Open Spaces +## Open Spaces Open spaces are informal sessions where anyone can gather and discuss any topic. There will be a table at the venue — if there's a free slot, simply write down your topic, and others can join the conversation. -#### List of spaces +#### List of spaces Will be published soon. --- -## Sprints +## Sprints Our sprint days offer a chance to contribute to open-source projects and collaborate with others in the community. Sprints take place over the weekend. -#### List of participating projects +#### List of participating projects Will be published soon. --- -## PyLadies Sessions +## PyLadies Sessions Workshops organised by and for PyLadies, focused on supporting less-represented groups in tech. #### List of sessions @@ -73,7 +73,7 @@ Will be added soon. --- -## Beginner’s Day Unconference Space +## Beginner’s Day Unconference Space Running in parallel with the sprints, this unconference space is designed for beginners. It includes Q&A sessions, AMAs, and career-focused discussions with experienced professionals. #### List of sessions @@ -81,18 +81,18 @@ Will be added soon. --- -## Tutorials for Less-Represented Groups in Tech +## Tutorials for Less-Represented Groups in Tech Introductory tutorials designed to help beginners from less-represented groups start learning programming. #### List of tutorials -- Django Girls – more info coming soon -- Humble Data – more info coming soon +- Django Girls – more info coming soon +- Humble Data – more info coming soon --- -## Events +## Events Our events bring the community together for fun and connection throughout the conference. #### List of events -- Main social event – details coming soon -- Speakers’ dinner – details coming soon +- Main social event – details coming soon +- Speakers’ dinner – details coming soon