Skip to content

Talks

The talks area is the heart of the attendee experience. It lists every talk an attendee may see, lets them search and filter, and links each row to a detail page with the abstract, links, and (once available) the video. Everything is scoped to the events a user has access to, so an attendee never sees a talk from an event they did not register for.

All talk views require authentication. A logged-out visitor is redirected to the login flow before any of these pages render.

Access scoping

Every query that drives these pages runs through Talk.objects.accessible_to(user). Superusers see every talk; everyone else sees only talks whose event they belong to (event__in=user.events.all()). Because Talk.event is required, there is no event-less talk that could slip past the filter.

See TalkQuerySet.accessible_to.

Talk list and dashboard

The talk list (TalkListView, served at /) is the default landing page after login. It renders a single, scrollable column of talk cards sorted by start time. Each card shows the title, speakers, a timing badge, date and room, presentation type and track, and a row of action buttons.

Source: talks/views.py, template templates/talks/talk_list.html.

Timing badges

Each card is styled by the talk's timing relative to now, computed by Talk.get_timing() with a five-minute margin:

  • Happening now (orange): the current time falls within the talk's slot (plus or minus the margin).
  • Upcoming (blue): the talk starts more than five minutes from now.
  • Completed (gray): the talk ended more than five minutes ago.

A colored status bar on the left edge of each card mirrors the badge.

Filters

The filter bar offers, in this order, dropdowns for event, day, room, presentation type, and track, plus a status dropdown and a "Saved only" checkbox. The filter options are not hard-coded: they are derived from the talks actually visible in the selected event and search scope, so an empty room or a track with no talks never appears.

Filter GET param Behavior
Event event Numeric event id, or all for every accessible event
Day date ISO date (YYYY-MM-DD)
Room room Numeric room id
Presentation type presentation_type One of the Talk.PresentationType values
Track track Track name
Status status current, upcoming, or completed
Saved only saved 1 to show only the user's bookmarked talks

Room, day, track, and type are validated against the event-scoped queryset before they are applied, and then combined into a single .filter(). This matters in two ways:

  • A stale param left over from a previous event (for example a room id that belongs to another event) is silently dropped rather than producing a confusing empty list.
  • Intersecting criteria are applied together, so "Room A + April 6" correctly returns nothing when no talk matches both, instead of widening the result.

The status filter reuses the same five-minute margin as the timing badges: current matches talks whose slot overlaps now, upcoming matches talks starting after the margin, and completed matches talks that ended before it.

When no event is selected

An empty or garbage event param (for example ?event=abc) falls back to the resolved default event for the session rather than showing every event. The default event slug for this deployment is pyconde-pydata-2026.

The search box queries across three scopes, toggled by the Title, Description, and Author checkboxes:

  • Title: title__icontains.
  • Description: matches description or abstract (both fields).
  • Author: matches any speaker name (speakers__name__icontains).

When no scope is checked (or the special all scope is used) all three are searched. Matched terms are highlighted in the title and speaker name via the highlight template filter.

Search and filters compose: the search narrows the queryset first, then the filters apply on top.

Dashboard statistics

The dashboard_stats view renders a small per-event summary used on the dashboard: total talks, today's talks, and the number of talks with an available recording. A single event renders as three stat cards (via the stat_card inclusion tag); multiple events render as a per-event table with a totals row. The "available recordings" count walks each talk's get_video_link(), so it reflects both talks with their own video_link and talks covered by a live streaming session.

The response is cached for 60 seconds and varies on the session cookie, so the count stays cheap without leaking one user's event scope to another.

Upcoming talks

The upcoming_talks view returns the next eight talks starting after now, scoped to the user's events, with Today/Tomorrow badges. It is cached for 30 seconds and varies on the cookie.

Talk detail page

The detail page (TalkDetailView, at /<id>/) shows everything about one talk. The template is templates/talks/talk_detail.html.

It includes:

  • Header: title, speaker names, and a bookmark button. The session chair line appears only for moderators, and only when a chair is assigned.
  • Metadata box: date, start and end time, room, presentation type badge, and track badge.
  • Video embed: when get_video_link() returns a URL, the talk is shown in a responsive 16:9 iframe. See Live streaming for where that link comes from.
  • Jump-to-start control: for recordings with a computed start offset, a "Jump to" button seeks the player to the estimated start of the talk. For a still-live stream it shows a manual-skip hint instead. The seek is wired up with the Vimeo or YouTube player API depending on the provider.
  • Transcription: a collapsible iframe plus a link, shown when get_transcription_url() resolves (from the talk or its streaming session).
  • Description: the full description, rendered from Markdown.
  • Rating widget: see Ratings.
  • Rating comments: a superuser-only section listing attendee comments, ordered newest first.
  • Action buttons: "View on Pretalx", "Questions & Answers" (see Questions and answers), and a transcription link when available.

Rating summary visibility

Whether the aggregate star rating shows on the detail and list pages is controlled per event by Event.show_rating_summary, with moderators always allowed to see it. When the "all events" scope is selected there is no single event to read the flag from, so the summary defaults to hidden rather than silently bypassing a per-event opt-out.

ID or Pretalx redirect

talk_redirect_view resolves a path segment that may be either a numeric talk id or a Pretalx submission code, and redirects to the canonical detail URL. This lets a Pretalx link such as /ABCDEF/ land on the right talk. The same pattern exists for the Q&A page.

Saved talks and bookmarks

Any attendee can bookmark a talk. Saved talks are stored in the SavedTalk model, a simple join between a user and a talk with a unique (user, talk) constraint so a talk can only be saved once.

Source: talks/views_saved.py, model talks/models_rating.py.

The toggle_save_talk endpoint flips the bookmark: if the talk is already saved it is removed, otherwise it is created. It accepts only POST. The bookmark icon appears on the detail page, every list card, and every schedule card.

To keep list rendering fast, the views build a set of the current user's saved talk ids once per page (SavedTalk.talk_ids_for) and the template tests membership with the is_in filter rather than querying per row.

Saved-tags helper

The saved_tags template library exposes a single is_in filter used as {{ talk.pk|is_in:saved_talk_ids }}. It returns False (instead of raising) when the container is not iterable, so a template never breaks on an unexpected value.

HTMX-driven updates

The talk pages lean on HTMX so common interactions happen without a full page reload.

  • Search and filters: both forms issue hx-get requests to the list view and swap only the #talks-container. The view detects the HTMX request and returns the #talk-list template fragment instead of the whole page. Typing in the search box debounces 300 ms; changing any dropdown fires immediately.
  • Filter sync: when the event changes, the list fragment re-renders every other dropdown out-of-band (hx-swap-oob) so the day, room, type, track, and status options stay consistent with the new event.
  • URL push: hx-push-url="true" keeps the address bar in sync, so a filtered view is bookmarkable and survives a refresh.
  • Auto-refresh: the container re-fetches every 300 seconds so timing badges (Happening now / Upcoming / Completed) stay current during the event.
  • Bookmark toggle: the save button posts to toggle_save_talk and swaps itself with the updated state. Schedule cards use a compact icon-only partial; list and detail pages use the labeled button. The view picks the partial based on the HX-Target header.

For non-HTMX clients every endpoint degrades gracefully: the bookmark toggle flashes a message and redirects back to the detail page.