Move functions out of web_view.py
This commit is contained in:
parent
e66945a825
commit
a873060949
|
@ -3,6 +3,7 @@
|
||||||
import decimal
|
import decimal
|
||||||
import os
|
import os
|
||||||
import typing
|
import typing
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import date, datetime, time
|
from datetime import date, datetime, time
|
||||||
from zoneinfo import ZoneInfo
|
from zoneinfo import ZoneInfo
|
||||||
|
|
||||||
|
@ -377,3 +378,34 @@ def get_coordinates_and_routes(
|
||||||
route["geojson"] = read_geojson(data_dir, route.pop("geojson_filename"))
|
route["geojson"] = read_geojson(data_dir, route.pop("geojson_filename"))
|
||||||
|
|
||||||
return (coordinates, routes)
|
return (coordinates, routes)
|
||||||
|
|
||||||
|
|
||||||
|
def calculate_yearly_stats(trips: list[Trip]) -> dict[int, StrDict]:
|
||||||
|
"""Calculate total distance and distance by transport type grouped by year."""
|
||||||
|
yearly_stats: defaultdict[int, StrDict] = defaultdict(dict)
|
||||||
|
for trip in trips:
|
||||||
|
year = trip.start.year
|
||||||
|
dist = trip.total_distance()
|
||||||
|
yearly_stats[year].setdefault("count", 0)
|
||||||
|
yearly_stats[year]["count"] += 1
|
||||||
|
if dist:
|
||||||
|
yearly_stats[year]["total_distance"] = (
|
||||||
|
yearly_stats[year].get("total_distance", 0) + trip.total_distance()
|
||||||
|
)
|
||||||
|
for transport_type, distance in trip.distances_by_transport_type():
|
||||||
|
yearly_stats[year].setdefault("distances_by_transport_type", {})
|
||||||
|
yearly_stats[year]["distances_by_transport_type"][transport_type] = (
|
||||||
|
yearly_stats[year]["distances_by_transport_type"].get(transport_type, 0)
|
||||||
|
+ distance
|
||||||
|
)
|
||||||
|
for country in trip.countries:
|
||||||
|
yearly_stats[year].setdefault("countries", set())
|
||||||
|
yearly_stats[year]["countries"].add(country)
|
||||||
|
for leg in trip.travel:
|
||||||
|
if leg["type"] == "flight":
|
||||||
|
yearly_stats[year].setdefault("flight_count", 0)
|
||||||
|
yearly_stats[year]["flight_count"] += 1
|
||||||
|
if leg["type"] == "train":
|
||||||
|
yearly_stats[year].setdefault("train_count", 0)
|
||||||
|
yearly_stats[year]["train_count"] += 1
|
||||||
|
return dict(yearly_stats)
|
||||||
|
|
|
@ -1,23 +1,56 @@
|
||||||
"""Utility functions."""
|
"""Utility functions."""
|
||||||
|
|
||||||
import datetime
|
from datetime import date, datetime, timezone
|
||||||
|
|
||||||
DateOrDateTime = datetime.datetime | datetime.date
|
DateOrDateTime = datetime | date
|
||||||
|
|
||||||
|
|
||||||
def as_date(d: DateOrDateTime) -> datetime.date:
|
def as_date(d: DateOrDateTime) -> date:
|
||||||
"""Convert datetime to date."""
|
"""Convert datetime to date."""
|
||||||
if isinstance(d, datetime.datetime):
|
if isinstance(d, datetime):
|
||||||
return d.date()
|
return d.date()
|
||||||
assert isinstance(d, datetime.date)
|
assert isinstance(d, date)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
||||||
def as_datetime(d: DateOrDateTime) -> datetime.datetime:
|
def as_datetime(d: DateOrDateTime) -> datetime:
|
||||||
"""Date/time of event."""
|
"""Date/time of event."""
|
||||||
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)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def human_readable_delta(future_date: date) -> str | None:
|
||||||
|
"""
|
||||||
|
Calculate the human-readable time delta for a given future date.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
future_date (date): The future date as a datetime.date object.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: Human-readable time delta.
|
||||||
|
"""
|
||||||
|
# Ensure the input is a future date
|
||||||
|
if future_date <= date.today():
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Calculate the delta
|
||||||
|
delta = future_date - date.today()
|
||||||
|
|
||||||
|
# Convert delta to a more human-readable format
|
||||||
|
months, days = divmod(delta.days, 30)
|
||||||
|
weeks, days = divmod(days, 7)
|
||||||
|
|
||||||
|
# Formatting the output
|
||||||
|
parts = []
|
||||||
|
if months > 0:
|
||||||
|
parts.append(f"{months} months")
|
||||||
|
if weeks > 0:
|
||||||
|
parts.append(f"{weeks} weeks")
|
||||||
|
if days > 0:
|
||||||
|
parts.append(f"{days} days")
|
||||||
|
|
||||||
|
return " ".join(parts) if parts else None
|
||||||
|
|
69
web_view.py
69
web_view.py
|
@ -23,6 +23,7 @@ import agenda.fx
|
||||||
import agenda.holidays
|
import agenda.holidays
|
||||||
import agenda.thespacedevs
|
import agenda.thespacedevs
|
||||||
import agenda.trip
|
import agenda.trip
|
||||||
|
import agenda.utils
|
||||||
from agenda import calendar, format_list_with_ampersand, travel, uk_tz
|
from agenda import calendar, format_list_with_ampersand, travel, uk_tz
|
||||||
from agenda.types import StrDict, Trip
|
from agenda.types import StrDict, Trip
|
||||||
|
|
||||||
|
@ -487,39 +488,6 @@ def trip_list_text() -> str:
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def human_readable_delta(future_date: date) -> str | None:
|
|
||||||
"""
|
|
||||||
Calculate the human-readable time delta for a given future date.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
future_date (date): The future date as a datetime.date object.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
str: Human-readable time delta.
|
|
||||||
"""
|
|
||||||
# Ensure the input is a future date
|
|
||||||
if future_date <= date.today():
|
|
||||||
return None
|
|
||||||
|
|
||||||
# Calculate the delta
|
|
||||||
delta = future_date - date.today()
|
|
||||||
|
|
||||||
# Convert delta to a more human-readable format
|
|
||||||
months, days = divmod(delta.days, 30)
|
|
||||||
weeks, days = divmod(days, 7)
|
|
||||||
|
|
||||||
# Formatting the output
|
|
||||||
parts = []
|
|
||||||
if months > 0:
|
|
||||||
parts.append(f"{months} months")
|
|
||||||
if weeks > 0:
|
|
||||||
parts.append(f"{weeks} weeks")
|
|
||||||
if days > 0:
|
|
||||||
parts.append(f"{days} days")
|
|
||||||
|
|
||||||
return " ".join(parts) if parts else None
|
|
||||||
|
|
||||||
|
|
||||||
def get_prev_current_and_next_trip(
|
def get_prev_current_and_next_trip(
|
||||||
start: str, trip_list: list[Trip]
|
start: str, trip_list: list[Trip]
|
||||||
) -> tuple[Trip | None, Trip | None, Trip | None]:
|
) -> tuple[Trip | None, Trip | None, Trip | None]:
|
||||||
|
@ -582,7 +550,7 @@ def trip_page(start: str) -> str:
|
||||||
get_country=agenda.get_country,
|
get_country=agenda.get_country,
|
||||||
format_list_with_ampersand=format_list_with_ampersand,
|
format_list_with_ampersand=format_list_with_ampersand,
|
||||||
holidays=holidays,
|
holidays=holidays,
|
||||||
human_readable_delta=human_readable_delta,
|
euman_readable_delta=agenda.utils.human_readable_delta,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -614,37 +582,6 @@ def birthday_list() -> str:
|
||||||
return flask.render_template("birthday_list.html", items=items, today=today)
|
return flask.render_template("birthday_list.html", items=items, today=today)
|
||||||
|
|
||||||
|
|
||||||
def calculate_yearly_stats(trips: list[Trip]) -> dict[int, StrDict]:
|
|
||||||
"""Calculate total distance and distance by transport type grouped by year."""
|
|
||||||
yearly_stats: defaultdict[int, StrDict] = defaultdict(dict)
|
|
||||||
for trip in trips:
|
|
||||||
year = trip.start.year
|
|
||||||
dist = trip.total_distance()
|
|
||||||
yearly_stats[year].setdefault("count", 0)
|
|
||||||
yearly_stats[year]["count"] += 1
|
|
||||||
if dist:
|
|
||||||
yearly_stats[year]["total_distance"] = (
|
|
||||||
yearly_stats[year].get("total_distance", 0) + trip.total_distance()
|
|
||||||
)
|
|
||||||
for transport_type, distance in trip.distances_by_transport_type():
|
|
||||||
yearly_stats[year].setdefault("distances_by_transport_type", {})
|
|
||||||
yearly_stats[year]["distances_by_transport_type"][transport_type] = (
|
|
||||||
yearly_stats[year]["distances_by_transport_type"].get(transport_type, 0)
|
|
||||||
+ distance
|
|
||||||
)
|
|
||||||
for country in trip.countries:
|
|
||||||
yearly_stats[year].setdefault("countries", set())
|
|
||||||
yearly_stats[year]["countries"].add(country)
|
|
||||||
for leg in trip.travel:
|
|
||||||
if leg["type"] == "flight":
|
|
||||||
yearly_stats[year].setdefault("flight_count", 0)
|
|
||||||
yearly_stats[year]["flight_count"] += 1
|
|
||||||
if leg["type"] == "train":
|
|
||||||
yearly_stats[year].setdefault("train_count", 0)
|
|
||||||
yearly_stats[year]["train_count"] += 1
|
|
||||||
return dict(yearly_stats)
|
|
||||||
|
|
||||||
|
|
||||||
@app.route("/trip/stats")
|
@app.route("/trip/stats")
|
||||||
def trip_stats() -> str:
|
def trip_stats() -> str:
|
||||||
"""Travel stats: distance and price by year and travel type."""
|
"""Travel stats: distance and price by year and travel type."""
|
||||||
|
@ -654,7 +591,7 @@ def trip_stats() -> str:
|
||||||
|
|
||||||
past = [item for item in trip_list if (item.end or item.start) < today]
|
past = [item for item in trip_list if (item.end or item.start) < today]
|
||||||
|
|
||||||
yearly_stats = calculate_yearly_stats(past)
|
yearly_stats = agenda.trip.calculate_yearly_stats(past)
|
||||||
|
|
||||||
return flask.render_template(
|
return flask.render_template(
|
||||||
"trip/stats.html",
|
"trip/stats.html",
|
||||||
|
|
Loading…
Reference in a new issue