Show end date for trips
This commit is contained in:
parent
ea33722f69
commit
21b67bdc64
|
@ -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")
|
||||||
|
|
|
@ -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 %}
|
||||||
|
|
|
@ -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") }}
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue