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" }}

- - - - - - - - - - - - - - - - - - - - - {% 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 %} - - - - {% endif %} - - - - - - - - + {{ conference_row(item, badge) }} +
+ {% if item.linked_trip %} trip: {{ trip_link(item.linked_trip) }} {% endif %} +
{% endfor %} - -
DatesConferenceTopicLocationCFP endsPrice
{{ month_label }}
- {%- 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 %} -
-{% 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),