Move functions out of web_view.py

This commit is contained in:
Edward Betts 2024-07-07 11:32:03 +01:00
parent e66945a825
commit a873060949
3 changed files with 77 additions and 75 deletions

View file

@ -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)

View file

@ -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

View file

@ -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",