diff --git a/templates/conference_list.html b/templates/conference_list.html
index 7ac5ddc..a900f7d 100644
--- a/templates/conference_list.html
+++ b/templates/conference_list.html
@@ -1,250 +1,59 @@
{% extends "base.html" %}
-{% from "macros.html" import trip_link with context %}
+{% from "macros.html" import trip_link, conference_row with context %}
{% block title %}Conferences - Edward Betts{% endblock %}
{% block style %}
+{% set column_count = 9 %}
{% endblock %}
-{% set tl_colors = ["#0d6efd","#198754","#dc3545","#fd7e14","#6f42c1","#20c997","#0dcaf0","#d63384"] %}
+{% macro section(heading, item_list, badge) %}
+ {% if item_list %}
+
-{% macro render_timeline(timeline) %}
-{% if timeline %}
-{% set row_h = 32 %}
-{% set header_h = 22 %}
-{% set total_h = timeline.lane_count * row_h + header_h %}
-
-
Next 90 days
-
+
{{ heading }}
- {% for m in timeline.months %}
-
- {{ m.label }}
-
- {% endfor %}
+
+ {% set item_count = item_list|length %}
+ {% if item_count == 1 %}{{ item_count }} conference{% else %}{{ item_count }} conferences{% endif %}
+
+
-
-
- {% for conf in timeline.confs %}
- {% set color = tl_colors[conf.lane % tl_colors | length] %}
- {% set top_px = conf.lane * row_h + header_h %}
-
- {% if conf.url %}
{{ conf.name }}
- {% else %}
{{ conf.name }}{% endif %}
-
- {% endfor %}
-
-
-
-{% endif %}
-{% endmacro %}
-
-{% macro conf_table(heading, item_list, badge) %}
-{% if item_list %}
-{% set count = item_list | length %}
-{{ heading }} {{ count }} conference{{ "" if count == 1 else "s" }}
-
-
-
-
-
-
-
-
-
-
-
- | Dates |
- Conference |
- Topic |
- Location |
- CFP ends |
- Price |
-
-
-
- {% set ns = namespace(prev_month="") %}
{% for item in item_list %}
- {% set month_label = item.start_date.strftime("%B %Y") %}
- {% if month_label != ns.prev_month %}
- {% set ns.prev_month = month_label %}
-
- | {{ month_label }} |
-
- {% endif %}
-
- |
- {%- if item.start_date == item.end_date -%}
- {{ item.start_date.strftime("%-d %b %Y") }}
- {%- elif item.start_date.year == item.end_date.year and item.start_date.month == item.end_date.month -%}
- {{ item.start_date.strftime("%-d") }}–{{ item.end_date.strftime("%-d %b %Y") }}
- {%- else -%}
- {{ item.start_date.strftime("%-d %b") }}–{{ item.end_date.strftime("%-d %b %Y") }}
- {%- endif -%}
- |
-
- {% if item.url %}{{ item.name }}
- {% else %}{{ item.name }}{% endif %}
- {% if item.going and not (item.accommodation_booked or item.travel_booked) %}
- {{ badge }}
- {% endif %}
- {% if item.accommodation_booked %}
- accommodation
- {% endif %}
- {% if item.transport_booked %}
- transport
- {% endif %}
- {% if item.linked_trip %}
- {% set trip = item.linked_trip %}
-
- 🧳{% if trip.title != item.name %} {{ trip.title }}{% endif %}
-
- {% endif %}
- |
- {{ item.topic }} |
-
- {% set country = get_country(item.country) if item.country else None %}
- {% if country %}{{ country.flag }} {{ item.location }}
- {% elif item.online %}💻 Online
- {% else %}{{ item.location }}{% endif %}
- |
-
- {% if item.cfp_end %}{{ item.cfp_end.strftime("%-d %b %Y") }}{% endif %}
- |
-
- {% if item.price and item.currency %}
- {{ "{:,d}".format(item.price | int) }} {{ item.currency }}
- {% if item.currency != "GBP" and item.currency in fx_rate %}
- {{ "{:,.0f}".format(item.price / fx_rate[item.currency]) }} GBP
- {% endif %}
- {% elif item.free %}
- free
- {% endif %}
- |
-
+ {{ conference_row(item, badge) }}
+
+ {% if item.linked_trip %} trip: {{ trip_link(item.linked_trip) }} {% endif %}
+
{% endfor %}
-
-
-{% endif %}
+ {% endif %}
{% endmacro %}
{% block content %}
Conferences
-
- {{ render_timeline(timeline) }}
-
- {{ conf_table("Current", current, "attending") }}
- {{ conf_table("Future", future, "going") }}
- {{ conf_table("Past", past|reverse|list, "went") }}
+
+ {{ section("Current", current, "attending") }}
+ {{ section("Future", future, "going") }}
+ {{ section("Past", past|reverse|list, "went") }}
+
-
-
{% endblock %}
diff --git a/update.py b/update.py
index efa4bd9..00460c2 100755
--- a/update.py
+++ b/update.py
@@ -29,9 +29,6 @@ from agenda.types import StrDict
from web_view import app
-from datetime import date, timedelta
-
-
async def update_bank_holidays(config: flask.config.Config) -> None:
"""Update cached copy of UK Bank holidays."""
t0 = time()
@@ -321,7 +318,8 @@ def update_thespacedevs(config: flask.config.Config) -> None:
"""
rocket_dir = os.path.join(config["DATA_DIR"], "thespacedevs")
- existing_data = agenda.thespacedevs.load_cached_launches(rocket_dir) or {}
+ existing_data = agenda.thespacedevs.load_cached_launches(rocket_dir)
+ assert existing_data
if agenda.thespacedevs.is_launches_cache_fresh(rocket_dir):
return
@@ -413,13 +411,16 @@ def update_gandi(config: flask.config.Config) -> None:
def update_weather(config: flask.config.Config) -> None:
"""Refresh weather cache for home and all upcoming trips."""
+ from datetime import date, timedelta
today = date.today()
forecast_window = today + timedelta(days=8)
trips = agenda.trip.build_trip_list()
upcoming = [
- t for t in trips if (t.end or t.start) >= today and t.start <= forecast_window
+ t
+ for t in trips
+ if (t.end or t.start) >= today and t.start <= forecast_window
]
seen: set[tuple[float, float]] = set()
diff --git a/web_view.py b/web_view.py
index 8ba8b25..4bd5203 100755
--- a/web_view.py
+++ b/web_view.py
@@ -440,74 +440,6 @@ def _conference_description(conf: StrDict) -> str:
return "\n".join(lines) if lines else "Conference"
-def build_conference_timeline(
- current: list[StrDict], future: list[StrDict], today: date, days: int = 90
-) -> dict | None:
- """Build data for a Gantt-style timeline of upcoming conferences."""
- timeline_start = today
- timeline_end = today + timedelta(days=days)
-
- visible = [
- c
- for c in (current + future)
- if c["start_date"] <= timeline_end and c["end_date"] >= today
- ]
- if not visible:
- return None
-
- visible.sort(key=lambda c: c["start_date"])
-
- # Greedy interval-coloring: assign each conference a lane (row)
- lane_ends: list[date] = []
- conf_data = []
- for conf in visible:
- lane = next(
- (i for i, end in enumerate(lane_ends) if end < conf["start_date"]),
- len(lane_ends),
- )
- if lane == len(lane_ends):
- lane_ends.append(conf["end_date"])
- else:
- lane_ends[lane] = conf["end_date"]
-
- start_off = max((conf["start_date"] - timeline_start).days, 0)
- end_off = min((conf["end_date"] - timeline_start).days + 1, days)
- left_pct = round(start_off / days * 100, 2)
- width_pct = max(round((end_off - start_off) / days * 100, 2), 0.5)
-
- conf_data.append(
- {
- "name": conf["name"],
- "url": conf.get("url"),
- "lane": lane,
- "left_pct": left_pct,
- "width_pct": width_pct,
- "key": f"{conf['start_date'].isoformat()}|{conf['name']}",
- "label": (
- f"{conf['name']}"
- f" ({conf['start_date'].strftime('%-d %b')}–"
- f"{conf['end_date'].strftime('%-d %b')})"
- ),
- }
- )
-
- # Month markers for x-axis labels
- months = []
- d = today.replace(day=1)
- while d <= timeline_end:
- off = max((d - timeline_start).days, 0)
- months.append({"label": d.strftime("%b %Y"), "left_pct": round(off / days * 100, 2)})
- # advance to next month
- d = (d.replace(day=28) + timedelta(days=4)).replace(day=1)
-
- return {
- "confs": conf_data,
- "lane_count": len(lane_ends),
- "months": months,
- "days": days,
- }
-
-
def build_conference_ical(items: list[StrDict]) -> bytes:
"""Build iCalendar feed for all conferences."""
lines = [
@@ -556,13 +488,10 @@ def conference_list() -> str:
]
future = [conf for conf in items if conf["start_date"] > today]
- timeline = build_conference_timeline(current, future, today)
-
return flask.render_template(
"conference_list.html",
current=current,
future=future,
- timeline=timeline,
today=today,
get_country=agenda.get_country,
fx_rate=agenda.fx.get_rates(app.config),