Fix trip stats new-country counting
This commit is contained in:
parent
91a74fd887
commit
8a0df48a73
5 changed files with 61 additions and 18 deletions
|
|
@ -30,3 +30,6 @@ This is a personal agenda web application built with Flask that tracks various e
|
|||
- Avoid committing unrelated untracked files (e.g., `node_modules/`, build artifacts)
|
||||
- Only commit relevant project files
|
||||
- Personal data directory (`personal-data/`) is excluded from git
|
||||
|
||||
## 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`.
|
||||
|
|
|
|||
|
|
@ -36,6 +36,17 @@ def conferences(trip: Trip, yearly_stats: Mapping[int, StrDict]) -> None:
|
|||
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)
|
||||
first_visit_year: dict[str, int] = {}
|
||||
|
||||
for trip in trips:
|
||||
year = trip.start.year
|
||||
for country in trip.countries:
|
||||
if country.alpha_2 == "GB":
|
||||
continue
|
||||
alpha_2 = country.alpha_2
|
||||
if alpha_2 not in first_visit_year or year < first_visit_year[alpha_2]:
|
||||
first_visit_year[alpha_2] = year
|
||||
|
||||
for trip in trips:
|
||||
year = trip.start.year
|
||||
dist = trip.total_distance()
|
||||
|
|
@ -61,6 +72,9 @@ def calculate_yearly_stats(trips: list[Trip]) -> dict[int, StrDict]:
|
|||
continue
|
||||
yearly_stats[year].setdefault("countries", set())
|
||||
yearly_stats[year]["countries"].add(country)
|
||||
if first_visit_year.get(country.alpha_2) == year:
|
||||
yearly_stats[year].setdefault("new_countries", set())
|
||||
yearly_stats[year]["new_countries"].add(country)
|
||||
|
||||
travel_legs(trip, yearly_stats[year])
|
||||
|
||||
|
|
|
|||
|
|
@ -221,22 +221,18 @@ class Trip:
|
|||
@property
|
||||
def countries_str(self) -> str:
|
||||
"""List of countries visited on this trip."""
|
||||
return typing.cast(
|
||||
str,
|
||||
format_list_with_ampersand([f"{c.name} {c.flag}" for c in self.countries]),
|
||||
return format_list_with_ampersand(
|
||||
[f"{c.name} {c.flag}" for c in self.countries]
|
||||
)
|
||||
|
||||
@property
|
||||
def locations_str(self) -> str:
|
||||
"""List of countries visited on this trip."""
|
||||
return typing.cast(
|
||||
str,
|
||||
format_list_with_ampersand(
|
||||
[
|
||||
f"{location} ({c.name})" + (f" {c.flag}" if self.show_flags else "")
|
||||
for location, c in self.locations()
|
||||
]
|
||||
),
|
||||
return format_list_with_ampersand(
|
||||
[
|
||||
f"{location} ({c.name})" + (f" {c.flag}" if self.show_flags else "")
|
||||
for location, c in self.locations()
|
||||
]
|
||||
)
|
||||
|
||||
@property
|
||||
|
|
|
|||
|
|
@ -21,24 +21,25 @@
|
|||
{% endfor %}
|
||||
|
||||
{% for year, year_stats in yearly_stats | dictsort(reverse=True) %}
|
||||
{% set countries = year_stats.countries | sort(attribute="name") %}
|
||||
{% set countries = year_stats.countries | default([]) | sort(attribute="name") %}
|
||||
{% set new_countries = year_stats.new_countries | default([]) %}
|
||||
<h4>{{ year }}</h4>
|
||||
<div>Trips in {{ year }}: {{ year_stats.count }}</div>
|
||||
<div>Conferences in {{ year }}: {{ year_stats.conferences }}</div>
|
||||
<div>{{ countries | count }} countries visited in {{ year }}:
|
||||
{% set new_countries = [] %}
|
||||
{% for c in countries %}
|
||||
{% set display_new_countries = [] %}
|
||||
{% for c in new_countries %}
|
||||
{% if c.alpha_2 not in previously_visited %}
|
||||
{% set _ = new_countries.append(c) %}
|
||||
{% set _ = display_new_countries.append(c) %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% if new_countries %}
|
||||
({{ new_countries | count }} new)
|
||||
{% if display_new_countries %}
|
||||
({{ display_new_countries | count }} new)
|
||||
{% endif %}
|
||||
{% for c in countries %}
|
||||
<span class="d-inline-block border border-2 p-1 m-1">
|
||||
{{ c.flag }} {{ c.name }} ({{ c.alpha_2 }})
|
||||
{% if c.alpha_2 not in previously_visited %}
|
||||
{% if c in display_new_countries %}
|
||||
<span class="badge text-bg-info">new</span>
|
||||
{% endif %}
|
||||
</span>
|
||||
|
|
|
|||
29
tests/test_trip_stats.py
Normal file
29
tests/test_trip_stats.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
"""Tests for trip stats."""
|
||||
|
||||
from datetime import date
|
||||
|
||||
import agenda
|
||||
from agenda.stats import calculate_yearly_stats
|
||||
from agenda.types import Trip
|
||||
|
||||
|
||||
def make_trip(start: date, country_code: str) -> Trip:
|
||||
"""Build a trip with a single country visit."""
|
||||
return Trip(start=start, accommodation=[{"country": country_code}])
|
||||
|
||||
|
||||
def test_new_country_only_first_year() -> None:
|
||||
"""Ensure new countries are only counted on their first visit year."""
|
||||
trips = [
|
||||
make_trip(date(2024, 5, 1), "CZ"),
|
||||
make_trip(date(2026, 6, 1), "CZ"),
|
||||
]
|
||||
|
||||
yearly_stats = calculate_yearly_stats(trips)
|
||||
czechia = agenda.get_country("CZ")
|
||||
assert czechia is not None
|
||||
|
||||
assert czechia in yearly_stats[2024]["countries"]
|
||||
assert czechia in yearly_stats[2024]["new_countries"]
|
||||
assert czechia in yearly_stats[2026]["countries"]
|
||||
assert "new_countries" not in yearly_stats[2026]
|
||||
Loading…
Add table
Add a link
Reference in a new issue