Skip to content

Commit 9cea80d

Browse files
committed
feat: Fetch speakers from Pretalx
1 parent ab80312 commit 9cea80d

7 files changed

Lines changed: 5038 additions & 19 deletions

File tree

app/home/templates/home/sections/speakers-section.html

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,30 @@ <h2 class="font-td_pinoy text-6xl text-orange-2 text-center m-0">Speakers</h2>
5151
</div>
5252
<div class="speakers-wrapper flex items-center gap-[30px] flex-row flex-wrap justify-between">
5353
{% for speaker in speakers %}
54+
{% if speaker.avatar_url %}
5455
<div class="rounded-[24px] flex flex-col gap-5 w-full max-w-[100%] lg:max-w-[250px] grow items-center">
5556
<div class="overflow-hidden flex-shrink-0 w-[250px] h-[250px]">
56-
{% if speaker.photo_url %}
57-
<img src="{{ speaker.photo_url }}" class="w-[250px] h-[250px] object-cover rounded-[85px]"
58-
alt="{{ speaker.full_name }} - Image" style="height: 250px;">
57+
{% if speaker.avatar_url %}
58+
<img src="{{ speaker.avatar_url }}" class="w-[250px] h-[250px] object-cover rounded-[85px]"
59+
alt="{{ speaker.name }} - Image" width="250" height="250">
5960
{% else %}
60-
<img src="#" class="w-[250px] h-[250px] object-cover rounded-[85px]"
61-
alt="{{ speaker.full_name }} - Image" style="height: 250px;">
61+
<img src="{% static 'img/speakers/speakers-img-1.jpg' %}" class="w-[250px] h-[250px] object-cover rounded-[85px]"
62+
alt="{{ speaker.name }} - Image" width="250" height="250">
6263
{% endif %}
6364
</div>
6465
<div>
65-
<h3 class="font-bantayog text-brown-1 text-2xl uppercase">{{ speaker.full_name }}</h3>
66+
<h3 class="font-bantayog text-brown-1 text-2xl uppercase">{{ speaker.name }}</h3>
6667
{% if speaker.title %}<p class="font-nunito text-brown-2 text-[13px] mb-2">{{ speaker.title }}</p>{% endif %}
6768
{% if speaker.introduction %}
6869
<p class="font-nunito text-brown-2 text-[13px]">{{ speaker.introduction|truncatewords:30 }}</p>
6970
{% elif speaker.bio %}
7071
<p class="font-nunito text-brown-2 text-[13px]">{{ speaker.bio|truncatewords:30 }}</p>
72+
{% elif speaker.biography %}
73+
<p class="font-nunito text-brown-2 text-[13px]">{{ speaker.biography|truncatewords:30 }}</p>
7174
{% endif %}
7275
</div>
7376
</div>
77+
{% endif %}
7478
{% empty %}
7579
<p class="text-center text-brown-2">No speakers available yet.</p>
7680
{% endfor %}

app/home/views.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from app.speakers.models import Speaker
77
from app.sponsors.models import Sponsor
88
from config.constants import SPONSOR_TYPE_ORDER
9+
from services.pretalx_service import PretalxService
910

1011

1112
class HomeView(BuildableTemplateView):
@@ -25,9 +26,14 @@ def get_sponsors(self):
2526

2627
def get_context_data(self, **kwargs):
2728
context = super().get_context_data(**kwargs)
29+
service = PretalxService(base_url="https://pretalx.com")
2830
context["sponsors_by_type"] = self.get_sponsors()
29-
context["featured_speakers"] = Speaker.objects.filter(is_featured=True).order_by("first_name", "last_name")
30-
context["speakers"] = Speaker.objects.filter(is_featured=False).order_by("first_name", "last_name")
31+
featured_speakers = Speaker.objects.filter(is_featured=True).order_by("first_name", "last_name")
32+
context["featured_speakers"] = featured_speakers
33+
keynote_names = {s.full_name for s in featured_speakers}
34+
context["speakers"] = [
35+
s for s in service.get_speakers("python-asia-2026")["results"] if s.get("name") not in keynote_names
36+
]
3137
return context
3238

3339

config/environment.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,4 +58,8 @@ def get_db_config(self):
5858
return {"NAME": name, "HOST": host, "USER": user, "PASSWORD": pwd, "PORT": 5432}
5959

6060

61-
settings = Settings()
61+
settings = Settings(
62+
PRETALX=PretalxConfig(
63+
BASE_URL="https://pretalx.com",
64+
)
65+
)

config/settings.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,13 @@
206206

207207
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
208208

209+
CACHES = {
210+
"default": {
211+
"BACKEND": "django.core.cache.backends.db.DatabaseCache",
212+
"LOCATION": "django_cache",
213+
}
214+
}
215+
209216
# Build directory for bakery
210217
BUILD_DIR = BASE_DIR / "build"
211218
BAKERY_VIEWS = ("app.home.views.HomeView",)

deploy/build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ uv sync
1313
echo "Applying database migrations..."
1414
uv run manage.py migrate --noinput
1515

16+
# Create cache table
17+
echo "Creating cache table..."
18+
uv run manage.py createcachetable
19+
1620
# Collect static files
1721
echo "Collecting static files..."
1822
uv run manage.py collectstatic --noinput

services/pretalx_service.py

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1+
import json
2+
13
import requests
4+
from django.core.cache import cache
25

36
from config.environment import settings
47

58

69
class PretalxService:
7-
def __init__(self, base_url: str):
8-
self.base_url = settings.PRETALX.BASE_URL.rstrip("/")
10+
def __init__(self, base_url: str | None = None):
11+
self.base_url = base_url or settings.PRETALX.BASE_URL.rstrip("/")
912
self.headers = {
10-
"Authorization": f"Token {settings.PRETALX.API_TOKEN}",
1113
"Content-Type": "application/json",
1214
}
1315

@@ -17,14 +19,39 @@ def get_event(self, event_slug: str):
1719
return response.json() if response.ok else response.raise_for_status()
1820

1921
def get_submissions(self, event_slug: str):
20-
url = f"{self.base_url}/api/events/{event_slug}/submissions/"
22+
cache_key = f"pretalx_submissions_{event_slug}"
23+
cached = cache.get(cache_key)
24+
if cached:
25+
return cached
26+
url = f"{self.base_url}/api/events/{event_slug}/submissions/?page_size=999"
2127
response = requests.get(url, headers=self.headers)
22-
return response.json() if response.ok else response.raise_for_status()
28+
data = response.json() if response.ok else response.raise_for_status()
29+
cache.set(cache_key, data)
30+
return data
2331

2432
def get_speakers(self, event_slug: str):
25-
url = f"{self.base_url}/api/events/{event_slug}/speakers/"
26-
response = requests.get(url, headers=self.headers)
27-
return response.json() if response.ok else response.raise_for_status()
33+
try:
34+
cache_key = f"pretalx_speakers_{event_slug}"
35+
cached = cache.get(cache_key)
36+
if cached:
37+
return cached
38+
39+
url = f"{self.base_url}/api/events/{event_slug}/speakers/?page_size=999"
40+
response = requests.get(url, headers=self.headers)
41+
data = response.json() if response.ok else response.raise_for_status()
42+
43+
submissions = self.get_submissions(event_slug)
44+
confirmed_codes = {sub["code"] for sub in submissions.get("results", []) if sub.get("state") == "confirmed"}
45+
46+
data["results"] = [
47+
speaker
48+
for speaker in data.get("results", [])
49+
if any(code in confirmed_codes for code in speaker.get("submissions", []))
50+
]
51+
cache.set(cache_key, data)
52+
return data
53+
except requests.RequestException:
54+
return {"results": []}
2855

2956
def get_talks(self, event_slug: str):
3057
url = f"{self.base_url}/api/events/{event_slug}/talks?limit=999&state=confirmed"
@@ -40,3 +67,8 @@ def send_feedback(self, event_slug: str, submission_id: str, feedback: dict):
4067
url = f"{self.base_url}/api/events/{event_slug}/submissions/{submission_id}/feedback/"
4168
response = requests.post(url, json=feedback, headers=self.headers)
4269
return response.json() if response.ok else response.raise_for_status()
70+
71+
def get_sessions(self, event_slug: str):
72+
url = f"{self.base_url}/api/events/{event_slug}/sessions/"
73+
response = requests.get(url, headers=self.headers)
74+
return response.json() if response.ok else response.raise_for_status()

static/css/app.css

Lines changed: 4964 additions & 2 deletions
Large diffs are not rendered by default.

0 commit comments

Comments
 (0)