Show end date for trips

This commit is contained in:
Edward Betts 2024-01-05 09:35:56 +00:00
parent ea33722f69
commit 21b67bdc64
4 changed files with 67 additions and 45 deletions

View file

@ -1,8 +1,8 @@
"""Types.""" """Types."""
import dataclasses
import datetime
import typing import typing
from dataclasses import dataclass, field
from datetime import date, datetime, timezone
from pycountry.db import Country from pycountry.db import Country
@ -12,23 +12,47 @@ from agenda import format_list_with_ampersand
StrDict = dict[str, typing.Any] StrDict = dict[str, typing.Any]
@dataclasses.dataclass def as_date(d: datetime | date) -> date:
"""Convert datetime to date."""
if isinstance(d, datetime):
return d.date()
assert isinstance(d, date)
return d
@dataclass
class Trip: class Trip:
"""Trip.""" """Trip."""
date: datetime.date start: date
travel: list[StrDict] = dataclasses.field(default_factory=list) travel: list[StrDict] = field(default_factory=list)
accommodation: list[StrDict] = dataclasses.field(default_factory=list) accommodation: list[StrDict] = field(default_factory=list)
conferences: list[StrDict] = dataclasses.field(default_factory=list) conferences: list[StrDict] = field(default_factory=list)
@property @property
def title(self) -> str: def title(self) -> str:
"""Trip title.""" """Trip title."""
names = ( return (
format_list_with_ampersand([conf["name"] for conf in self.conferences]) format_list_with_ampersand([conf["name"] for conf in self.conferences])
or "[no conferences in trip]" or "[no conference]"
) )
return f'{names} ({self.date.strftime("%b %Y")})'
@property
def end(self) -> date | None:
"""End date for trip."""
max_conference_end = (
max(as_date(item["end"]) for item in self.conferences)
if self.conferences
else date.min
)
assert isinstance(max_conference_end, date)
arrive = [item["arrive"].date() for item in self.travel if "arrive" in item]
travel_end = max(arrive) if arrive else date.min
assert isinstance(travel_end, date)
max_date = max(max_conference_end, travel_end)
return max_date if max_date != date.min else None
@property @property
def countries(self) -> set[Country]: def countries(self) -> set[Country]:
@ -51,56 +75,54 @@ class Trip:
) )
@dataclasses.dataclass @dataclass
class Holiday: class Holiday:
"""Holiay.""" """Holiay."""
name: str name: str
country: str country: str
date: datetime.date date: date
@dataclasses.dataclass @dataclass
class Event: class Event:
"""Event.""" """Event."""
name: str name: str
date: datetime.date | datetime.datetime date: date | datetime
end_date: datetime.date | datetime.datetime | None = None end_date: date | datetime | None = None
title: str | None = None title: str | None = None
url: str | None = None url: str | None = None
going: bool | None = None going: bool | None = None
@property @property
def as_datetime(self) -> datetime.datetime: def as_datetime(self) -> datetime:
"""Date/time of event.""" """Date/time of event."""
d = self.date d = self.date
t0 = datetime.datetime.min.time() t0 = datetime.min.time()
return ( return (
d d
if isinstance(d, datetime.datetime) if isinstance(d, datetime)
else datetime.datetime.combine(d, t0).replace(tzinfo=datetime.timezone.utc) else datetime.combine(d, t0).replace(tzinfo=timezone.utc)
) )
@property @property
def has_time(self) -> bool: def has_time(self) -> bool:
"""Event has a time associated with it.""" """Event has a time associated with it."""
return isinstance(self.date, datetime.datetime) return isinstance(self.date, datetime)
@property @property
def as_date(self) -> datetime.date: def as_date(self) -> date:
"""Date of event.""" """Date of event."""
return ( return self.date.date() if isinstance(self.date, datetime) else self.date
self.date.date() if isinstance(self.date, datetime.datetime) else self.date
)
@property @property
def end_as_date(self) -> datetime.date: def end_as_date(self) -> date:
"""Date of event.""" """Date of event."""
return ( return (
( (
self.end_date.date() self.end_date.date()
if isinstance(self.end_date, datetime.datetime) if isinstance(self.end_date, datetime)
else self.end_date else self.end_date
) )
if self.end_date if self.end_date
@ -110,22 +132,14 @@ class Event:
@property @property
def display_time(self) -> str | None: def display_time(self) -> str | None:
"""Time for display on web page.""" """Time for display on web page."""
return ( return self.date.strftime("%H:%M") if isinstance(self.date, datetime) else None
self.date.strftime("%H:%M")
if isinstance(self.date, datetime.datetime)
else None
)
@property @property
def display_timezone(self) -> str | None: def display_timezone(self) -> str | None:
"""Timezone for display on web page.""" """Timezone for display on web page."""
return ( return self.date.strftime("%z") if isinstance(self.date, datetime) else None
self.date.strftime("%z")
if isinstance(self.date, datetime.datetime)
else None
)
def delta_days(self, today: datetime.date) -> str: def delta_days(self, today: date) -> str:
"""Return number of days from today as a string.""" """Return number of days from today as a string."""
delta = (self.as_date - today).days delta = (self.as_date - today).days
@ -140,7 +154,7 @@ class Event:
@property @property
def display_date(self) -> str: def display_date(self) -> str:
"""Date for display on web page.""" """Date for display on web page."""
if isinstance(self.date, datetime.datetime): if isinstance(self.date, datetime):
return self.date.strftime("%a, %d, %b %Y %H:%M %z") return self.date.strftime("%a, %d, %b %Y %H:%M %z")
else: else:
return self.date.strftime("%a, %d, %b %Y") return self.date.strftime("%a, %d, %b %Y")

View file

@ -1,5 +1,7 @@
{% macro display_datetime(dt) %}{{ dt.strftime("%a, %d, %b %Y %H:%M %z") }}{% endmacro %} {% macro display_datetime(dt) %}{{ dt.strftime("%a, %d, %b %Y %H:%M %z") }}{% endmacro %}
{% macro display_time(dt) %}{{ dt.strftime("%H:%M %z") }}{% endmacro %} {% macro display_time(dt) %}{{ dt.strftime("%H:%M %z") }}{% endmacro %}
{% macro display_date(dt) %}{{ dt.strftime("%a, %d, %b %Y") }}{% endmacro %}
{% macro display_date_no_year(dt) %}{{ dt.strftime("%a, %d, %b") }}{% endmacro %}
{% macro conference_row(item, badge) %} {% macro conference_row(item, badge) %}
{% set country = get_country(item.country) if item.country else None %} {% set country = get_country(item.country) if item.country else None %}

View file

@ -1,6 +1,6 @@
{% extends "base.html" %} {% extends "base.html" %}
{% from "macros.html" import conference_row, accommodation_row, flight_row, train_row with context %} {% from "macros.html" import display_date_no_year, conference_row, accommodation_row, flight_row, train_row with context %}
{% block style %} {% block style %}
{% set conference_column_count = 6 %} {% set conference_column_count = 6 %}
@ -41,9 +41,15 @@
<h1>Trips</h1> <h1>Trips</h1>
{% for trip in trips %} {% for trip in trips %}
{% set end = trip.end %}
<div class="border border-2 rounded mb-2 p-2"> <div class="border border-2 rounded mb-2 p-2">
<h4>{{ trip.title }}</h4> <h3>{{ trip.title }} <small class="text-muted">({{ trip.start.strftime("%b %Y") }})</small></h3>
<p>Countries: {{ trip.countries_str }}</p> <div>Countries: {{ trip.countries_str }}</div>
{% if end %}
<div>Dates: {{ display_date_no_year(trip.start) }} to {{ display_date_no_year(end) }}</div>
{% else %}
<div>Start: {{ display_date_no_year(trip.start) }} (end date missing)</div>
{% endif %}
<div class="conferences"> <div class="conferences">
{% for conf in trip.conferences %} {% for conf in trip.conferences %}
{{ conference_row(conf, "going") }} {{ conference_row(conf, "going") }}

View file

@ -184,11 +184,11 @@ def trip_list() -> str:
for key, item_list in data.items(): for key, item_list in data.items():
assert isinstance(item_list, list) assert isinstance(item_list, list)
for item in item_list: for item in item_list:
if not (trip_id := item.get("trip")): if not (start := item.get("trip")):
continue continue
if trip_id not in trips: if start not in trips:
trips[trip_id] = Trip(date=trip_id) trips[start] = Trip(start=start)
getattr(trips[trip_id], key).append(item) getattr(trips[start], key).append(item)
trip_list = [trip for _, trip in sorted(trips.items(), reverse=True)] trip_list = [trip for _, trip in sorted(trips.items(), reverse=True)]