Skip to content

Commit d77f9db

Browse files
committed
Add speakers.
1 parent 2bd84a8 commit d77f9db

File tree

11 files changed

+176
-50
lines changed

11 files changed

+176
-50
lines changed

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,11 @@ storybook-static/
138138
# Sentry Auth Token
139139
.sentryclirc
140140
.astro
141+
142+
# EuroPython website
143+
src/content/speakers
144+
src/content/sessions
145+
src/content/days
146+
src/data/speakers.json
147+
src/data/sessions.json
148+
src/data/schedule.json

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ pre:
3131

3232
install:
3333
pnpm install
34+
pnpm update_data
35+
pnpm generate_content
3436

3537
dev:
3638
pnpm dev
@@ -42,6 +44,7 @@ check:
4244
pnpm run astro check
4345

4446
build:
47+
pnpm update_data
4548
pnpm build
4649

4750
preview: RELEASES_DIR = $(VPS_PREVIEW_PATH)/$(SAFE_BRANCH)/releases

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
"scripts": {
66
"dev": "astro dev",
77
"start": "astro dev",
8-
"build": "astro check && astro build && pnpm pagefind --site dist",
8+
"build": "pnpm generate_content && astro check && astro build && pnpm pagefind --site dist",
9+
"update_data": "pipx run scripts/download-data.py",
10+
"generate_content": "pipx run scripts/generate_content.py",
911
"preview": "astro preview",
1012
"astro": "astro",
1113
"format": "prettier --write --plugin=prettier-plugin-astro ."

scripts/download-data.py

Lines changed: 19 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@
1010
import httpx
1111
import json
1212
import pathlib
13-
import shutil
14-
import yaml
13+
import os
1514

1615
ROOT = pathlib.Path(__file__).parents[1]
1716

@@ -21,59 +20,38 @@
2120
SCHEDULE_DATA = "https://programapi24.europython.eu/2024/schedule.json"
2221

2322

24-
def write_mdx(data: dict[str, Any], output_dir: pathlib.Path, content_key: str) -> None:
25-
if output_dir.exists():
26-
shutil.rmtree(output_dir)
27-
28-
output_dir.mkdir(parents=True, exist_ok=True)
29-
30-
for key, value in data.items():
31-
filename = f"{key}.mdx"
32-
path = output_dir / filename
33-
34-
content = value.pop(content_key) or ""
35-
36-
content = content.replace("<3", "❤️")
37-
38-
frontmatter = yaml.dump(value, sort_keys=True)
39-
40-
with path.open("w", encoding="utf-8") as f:
41-
f.write(f"---\n{frontmatter}---\n\n{content}")
4223

4324

4425
def download_data(url: str) -> dict[str, Any]:
45-
with httpx.Client() as client:
26+
#TODO: fix SSL cert and force verification
27+
with httpx.Client(verify=False) as client:
4628
response = client.get(url)
4729
response.raise_for_status()
4830
data = response.json()
4931

5032
return data
5133

5234

35+
def save_json_data(filename, data, directory="src/data"):
36+
os.makedirs(directory, exist_ok=True) # Ensure the directory exists
37+
file_path = os.path.join(directory, f"{filename}.json")
38+
39+
with open(file_path, "w", encoding="utf-8") as file:
40+
json.dump(data, file, indent=4, ensure_ascii=False)
41+
42+
print(f"Saved {filename}.json in {directory}")
43+
44+
45+
5346
def download() -> None:
5447
speakers = download_data(SPEAKERS_URL)
5548
sessions = download_data(SESSIONS_URL)
5649
schedule = download_data(SCHEDULE_DATA)
5750

58-
for session in sessions.values():
59-
session["speakers"] = [
60-
speakers[speaker_id]["slug"] for speaker_id in session.get("speakers", [])
61-
]
62-
63-
for speaker in speakers.values():
64-
speaker["submissions"] = [
65-
sessions[session_id]["slug"]
66-
for session_id in speaker.get("submissions", [])
67-
if session_id in sessions
68-
]
69-
70-
write_mdx(sessions, ROOT / "src/content/sessions", "abstract")
71-
write_mdx(speakers, ROOT / "src/content/speakers", "biography")
72-
73-
for day, data in schedule["days"].items():
74-
path = ROOT / f"src/content/days/{day}.json"
75-
with path.open("w", encoding="utf-8") as f:
76-
json.dump(data, f, indent=2)
51+
save_json_data("speakers", speakers)
52+
save_json_data("sessions", sessions)
53+
save_json_data("schedule", schedule)
7754

7855

79-
download()
56+
if __name__ == "__main__":
57+
download()

scripts/generate_content.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
# /// script
2+
# dependencies = [
3+
# "PyYAML",
4+
# ]
5+
# requires-python = ">=3.11"
6+
# ///
7+
8+
from typing import Any
9+
import pathlib
10+
import os
11+
import json
12+
import shutil
13+
import yaml
14+
15+
ROOT = pathlib.Path(__file__).parents[1]
16+
17+
18+
def write_mdx(data: dict[str, Any], output_dir: pathlib.Path, content_key: str) -> None:
19+
if output_dir.exists():
20+
shutil.rmtree(output_dir)
21+
22+
output_dir.mkdir(parents=True, exist_ok=True)
23+
24+
for key, value in data.items():
25+
filename = f"{key}.mdx"
26+
path = output_dir / filename
27+
28+
content = value.pop(content_key) or ""
29+
30+
content = content.replace("<3", "❤️")
31+
32+
frontmatter = yaml.dump(value, sort_keys=True)
33+
34+
with path.open("w", encoding="utf-8") as f:
35+
f.write(f"---\n{frontmatter}---\n\n{content}")
36+
37+
38+
def load_json_data(filename, directory="src/data"):
39+
file_path = os.path.join(directory, f"{filename}.json")
40+
41+
if not os.path.exists(file_path):
42+
raise FileNotFoundError(f"File {filename}.json not found in {directory}")
43+
44+
print(f"Load {filename}.json from {directory}")
45+
with open(file_path, "r", encoding="utf-8") as file:
46+
return json.load(file)
47+
48+
def generate_content() -> None:
49+
speakers = load_json_data("speakers")
50+
sessions = load_json_data("sessions")
51+
schedule = load_json_data("schedule")
52+
53+
for session in sessions.values():
54+
session["speakers"] = [
55+
speakers[speaker_id]["slug"] for speaker_id in session.get("speakers", [])
56+
]
57+
58+
for speaker in speakers.values():
59+
speaker["submissions"] = [
60+
sessions[session_id]["slug"]
61+
for session_id in speaker.get("submissions", [])
62+
if session_id in sessions
63+
]
64+
65+
write_mdx(sessions, ROOT / "src/content/sessions", "abstract")
66+
write_mdx(speakers, ROOT / "src/content/speakers", "biography")
67+
68+
for day, data in schedule["days"].items():
69+
path = ROOT / f"src/content/days/{day}.json"
70+
with path.open("w", encoding="utf-8") as f:
71+
json.dump(data, f, indent=2)
72+
73+
74+
if __name__ == "__main__":
75+
generate_content()

src/content/sessions/.gitkeep

Whitespace-only changes.

src/content/speakers/.gitkeep

Whitespace-only changes.

src/data/links.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@
1515
"name": "Community Voting",
1616
"path": "/programme/voting"
1717
},
18+
{
19+
"name": "List of Speakers",
20+
"path": "/speaker"
21+
},
1822
{
1923
"name": "Speaker Mentorship",
2024
"path": "/programme/mentorship"

src/pages/schedule/[day].astro

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,8 @@ const slots = Object.entries(sessionsByTime)
189189
};
190190
});
191191
192-
const gridRows = [];
192+
193+
const gridRows: string[] = [];
193194
194195
let row = 1;
195196
@@ -201,7 +202,7 @@ for (let i = 0; i < slots.length; i++) {
201202
202203
const currentTime = timeToNumber(current.startTime);
203204
204-
let nextTime;
205+
let nextTime: number;
205206
206207
if (next) {
207208
nextTime = timeToNumber(next.startTime);

src/pages/speaker/index.astro

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
---
2+
import { getCollection } from 'astro:content';
3+
import Layout from "../../layouts/Layout.astro";
4+
5+
6+
const speakers = await getCollection('speakers');
7+
8+
// Group speakers by first letter
9+
const groupedSpeakers = speakers.reduce((acc, speaker) => {
10+
const letter: string = speaker.data.name[0].toUpperCase();
11+
if (!acc[letter]) acc[letter] = [];
12+
acc[letter].push(speaker);
13+
return acc;
14+
}, {});
15+
16+
// Define sorted letters
17+
const letters = [
18+
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "R", "S", "T", "V", "X", "Y", "Z"
19+
];
20+
---
21+
<Layout title="" description="">
22+
<main id="main-content" class="layout-wrapper overflow-x-clip">
23+
<div class="px-6">
24+
<div class="mb-4 prose prose-xl prose-li:m-0 prose-ul:m-0 prose-ul:mb-4 prose-a:underline prose-h1:text-text prose-headings:font-title prose-headings:text-text prose-a:text-text hover:prose-a:text-primary-hover prose-strong:text-text prose-strong:font-bold prose-li:marker:text-text">
25+
26+
<h1>Speakers</h1>
27+
28+
<div class="flex text-3xl font-bold flex-wrap mb-6">
29+
{letters.map(letter => (
30+
<a class="mx-1" href={`#letter-${letter}`}>{letter}</a>
31+
))}
32+
</div>
33+
34+
<ol class="speakers">
35+
{letters.map(letter => (
36+
groupedSpeakers[letter] ? (
37+
<div id={`letter-${letter}`}>
38+
<h2 class="relative font-title text-primary font-bold mb-[0.6em] [&amp;>a]:border-0 [&amp;>a]:text-inherit text-4xl"> {letter} </h2>
39+
<ul>
40+
{groupedSpeakers[letter].map((speaker: any) => (
41+
<li class="mb-1">
42+
<a class="underline hover:text-primary-hover" href={speaker.slug}>{speaker.data.name}</a>
43+
</li>
44+
))}
45+
</ul>
46+
<hr class="border-none h-[2px] my-12 bg-secondary">
47+
</div>
48+
) : null
49+
))}
50+
</ol>
51+
</div>
52+
</div>
53+
</main>
54+
</Layout>

0 commit comments

Comments
 (0)