Move trip new-country filtering into stats

This commit is contained in:
Edward Betts 2026-01-15 22:53:17 +00:00
parent ddfa77b152
commit f22772d12d
5 changed files with 27 additions and 15 deletions

View file

@ -32,4 +32,4 @@ This is a personal agenda web application built with Flask that tracks various e
- Personal data directory (`personal-data/`) is excluded from git - Personal data directory (`personal-data/`) is excluded from git
## Notes ## Notes
- Trip stats new-country badges come from `agenda.stats.calculate_yearly_stats` via `year_stats.new_countries` (first-visit year); `templates/trip/stats.html` filters against `PREVIOUSLY_VISITED`. - Trip stats new-country badges come from `agenda.stats.calculate_yearly_stats` via `year_stats.new_countries` (first-visit year, excluding `PREVIOUSLY_VISITED`).

View file

@ -33,10 +33,13 @@ def conferences(trip: Trip, yearly_stats: Mapping[int, StrDict]) -> None:
yearly_stats[c["start"].year]["conferences"] += 1 yearly_stats[c["start"].year]["conferences"] += 1
def calculate_yearly_stats(trips: list[Trip]) -> dict[int, StrDict]: def calculate_yearly_stats(
trips: list[Trip], previously_visited: set[str] | None = None
) -> dict[int, StrDict]:
"""Calculate total distance and distance by transport type grouped by year.""" """Calculate total distance and distance by transport type grouped by year."""
yearly_stats: defaultdict[int, StrDict] = defaultdict(dict) yearly_stats: defaultdict[int, StrDict] = defaultdict(dict)
first_visit_year: dict[str, int] = {} first_visit_year: dict[str, int] = {}
excluded_new: set[str] = previously_visited or set()
for trip in trips: for trip in trips:
year = trip.start.year year = trip.start.year
@ -72,7 +75,10 @@ def calculate_yearly_stats(trips: list[Trip]) -> dict[int, StrDict]:
continue continue
yearly_stats[year].setdefault("countries", set()) yearly_stats[year].setdefault("countries", set())
yearly_stats[year]["countries"].add(country) yearly_stats[year]["countries"].add(country)
if first_visit_year.get(country.alpha_2) == year: if (
first_visit_year.get(country.alpha_2) == year
and country.alpha_2 not in excluded_new
):
yearly_stats[year].setdefault("new_countries", set()) yearly_stats[year].setdefault("new_countries", set())
yearly_stats[year]["new_countries"].add(country) yearly_stats[year]["new_countries"].add(country)

View file

@ -27,19 +27,13 @@
<div>Trips in {{ year }}: {{ year_stats.count }}</div> <div>Trips in {{ year }}: {{ year_stats.count }}</div>
<div>Conferences in {{ year }}: {{ year_stats.conferences }}</div> <div>Conferences in {{ year }}: {{ year_stats.conferences }}</div>
<div>{{ countries | count }} countries visited in {{ year }}: <div>{{ countries | count }} countries visited in {{ year }}:
{% set display_new_countries = [] %} {% if new_countries %}
{% for c in new_countries %} ({{ new_countries | count }} new)
{% if c.alpha_2 not in previously_visited %}
{% set _ = display_new_countries.append(c) %}
{% endif %}
{% endfor %}
{% if display_new_countries %}
({{ display_new_countries | count }} new)
{% endif %} {% endif %}
{% for c in countries %} {% for c in countries %}
<span class="d-inline-block border border-2 p-1 m-1"> <span class="d-inline-block border border-2 p-1 m-1">
{{ c.flag }} {{ c.name }} ({{ c.alpha_2 }}) {{ c.flag }} {{ c.name }} ({{ c.alpha_2 }})
{% if c in display_new_countries %} {% if c in new_countries %}
<span class="badge text-bg-info">new</span> <span class="badge text-bg-info">new</span>
{% endif %} {% endif %}
</span> </span>

View file

@ -27,3 +27,11 @@ def test_new_country_only_first_year() -> None:
assert czechia in yearly_stats[2024]["new_countries"] assert czechia in yearly_stats[2024]["new_countries"]
assert czechia in yearly_stats[2026]["countries"] assert czechia in yearly_stats[2026]["countries"]
assert "new_countries" not in yearly_stats[2026] assert "new_countries" not in yearly_stats[2026]
def test_new_country_respects_previously_visited() -> None:
"""Ensure previously visited countries are excluded from new-country stats."""
trips = [make_trip(date(2024, 5, 1), "CZ")]
yearly_stats = calculate_yearly_stats(trips, {"CZ"})
assert "new_countries" not in yearly_stats[2024]

View file

@ -461,7 +461,9 @@ def accommodation_list() -> str:
items = travel.parse_yaml("accommodation", data_dir) items = travel.parse_yaml("accommodation", data_dir)
# Create a dictionary to hold stats for each year # Create a dictionary to hold stats for each year
year_stats = defaultdict(lambda: {"total_nights": 0, "nights_abroad": 0}) year_stats: defaultdict[int, dict[str, int]] = defaultdict(
lambda: {"total_nights": 0, "nights_abroad": 0}
)
# Calculate stats for each year # Calculate stats for each year
for stay in items: for stay in items:
@ -536,7 +538,7 @@ def calc_total_distance(trips: list[Trip]) -> float:
def calc_total_co2_kg(trips: list[Trip]) -> float: def calc_total_co2_kg(trips: list[Trip]) -> float:
"""Total CO₂ for trips.""" """Total CO₂ for trips."""
return sum(item.total_co2_kg() for item in trips) return sum(item.total_co2_kg() or 0.0 for item in trips)
def sum_distances_by_transport_type(trips: list[Trip]) -> list[tuple[str, float]]: def sum_distances_by_transport_type(trips: list[Trip]) -> list[tuple[str, float]]:
@ -806,7 +808,9 @@ def trip_stats() -> str:
conferences = sum(len(item.conferences) for item in trip_list) conferences = sum(len(item.conferences) for item in trip_list)
yearly_stats = agenda.stats.calculate_yearly_stats(trip_list) yearly_stats = agenda.stats.calculate_yearly_stats(
trip_list, app.config.get("PREVIOUSLY_VISITED")
)
return flask.render_template( return flask.render_template(
"trip/stats.html", "trip/stats.html",