diff --git a/README.md b/README.md index 0b45959..4590b60 100644 --- a/README.md +++ b/README.md @@ -293,6 +293,8 @@ For a real life example, take a look at [my own rules.yaml](https://github.com/l --show-first-last / --hide-first-last Show first/last usage of components [default: show-first-last] --show-vert / --hide-vert Show vertical (elevation gain) [default: hide-vert] + --show-retired / --hide-retired + Show retired bikes (on Strava) [default: hide-retired] --units [metric|imperial] Show data in metric or imperial [default: metric] --date-start ISO8601 Filter activities: start at or after the specified date(time) --date-end ISO8601 Filter activities: start before the specified date(time) diff --git a/src/strava_gear/cli.py b/src/strava_gear/cli.py index 7ebb174..614de5b 100644 --- a/src/strava_gear/cli.py +++ b/src/strava_gear/cli.py @@ -71,6 +71,9 @@ def get_metavar(self, _param): @click.option( '--show-vert/--hide-vert', default=False, show_default=True, help="Show vertical (elevation gain)") +@click.option( + '--show-retired/--hide-retired', default=False, show_default=True, + help="Show retired bikes (on Strava)") @click.option( '--units', type=click.Choice([u.name.lower() for u in Units]), default=Units.METRIC.name.lower(), show_default=True, callback=lambda _ctx, _param, v: Units[v.upper()], # TODO: drop when Python 3.11 is the oldest supported @@ -91,6 +94,7 @@ def cli( show_name: bool, show_first_last: bool, show_vert: bool, + show_retired: bool, units: Units, date_start: Optional[datetime], date_end: Optional[datetime] @@ -108,6 +112,7 @@ def cli( show_name=show_name, show_first_last=show_first_last, show_vert=show_vert, + show_bike=lambda b: show_retired or not input.bike_retired(b), units=units, ) warn_unknown_bikes(rules, activities) diff --git a/src/strava_gear/data.py b/src/strava_gear/data.py index 14d0c9f..8a80db9 100644 --- a/src/strava_gear/data.py +++ b/src/strava_gear/data.py @@ -8,6 +8,7 @@ from datetime import timezone from functools import total_ordering from itertools import chain +from typing import Callable from typing import Dict from typing import Iterable from typing import Iterator @@ -38,6 +39,7 @@ class Input: activities: List[Dict] aliases: Dict[BikeName, BikeId] = field(default_factory=dict) + bike_retired: Callable[[BikeId], bool] = lambda _: False @total_ordering diff --git a/src/strava_gear/input/activities.py b/src/strava_gear/input/activities.py index 58e77c4..885b340 100644 --- a/src/strava_gear/input/activities.py +++ b/src/strava_gear/input/activities.py @@ -62,6 +62,13 @@ def read_strava_offline(db_filename: Union[str, PathLike]) -> Input: assert essential_columns <= set(r.keys()) activities.append({**r, 'start_date': parse_datetime(r['start_date'])}) + # Strava doesn't return any retired bikes in /api/v3/athlete, + # which is where strava-offline gets its list of bikes from + # (if it ever does, there's an (undocumented) "retired" field + # in the SummaryGear record) + known_bike_ids = set(aliases.values()) + bike_retired = lambda b: b not in known_bike_ids # noqa: E731 + return Input( activities=activities, aliases=aliases, diff --git a/src/strava_gear/report.py b/src/strava_gear/report.py index 16c1aac..c76f286 100644 --- a/src/strava_gear/report.py +++ b/src/strava_gear/report.py @@ -3,6 +3,7 @@ from enum import Enum from enum import auto from functools import partial +from typing import Callable from typing import Dict from typing import Final from typing import Iterator @@ -31,6 +32,7 @@ def report( show_name: bool, show_first_last: bool, show_vert: bool, + show_bike: Callable[[BikeId], bool], units: Units, ): def cols(d: Dict) -> Dict: @@ -50,7 +52,7 @@ def cols(d: Dict) -> Dict: del d["vert m"] return d - table = [cols(d) for d in f(res)] + table = [cols(d) for d in f(res, show_bike=show_bike)] if not table: return @@ -62,7 +64,7 @@ def cols(d: Dict) -> Dict: print(tabulate(table, headers="keys", floatfmt=".1f", tablefmt=tablefmt), file=output) -def report_components(res: Result) -> Iterator[Dict]: +def report_components(res: Result, **_kwargs) -> Iterator[Dict]: for c in sorted(res.components, key=lambda c: (c.firstlast, c.ident,)): yield { "id": c.ident, @@ -76,14 +78,21 @@ def report_components(res: Result) -> Iterator[Dict]: } -def report_bikes(res: Result) -> Iterator[Dict]: +def report_bikes(res: Result, show_bike: Callable[[BikeId], bool]) -> Iterator[Dict]: bikes_firstlasts = bikes_firstlast(res) def sort_key(c: Component): assert c.assignment return bikes_firstlasts[c.assignment.bike], c.assignment - for c in sorted((c for c in res.components if c.assignment), key=sort_key): + for c in sorted(( + c + for c in res.components + if c.assignment + if show_bike(c.assignment.bike) + ), + key=sort_key + ): assert c.assignment yield { "bike": res.bike_names.get(c.assignment.bike, c.assignment.bike), diff --git a/tests/readme/help.md b/tests/readme/help.md index dea5a0a..03cb05b 100644 --- a/tests/readme/help.md +++ b/tests/readme/help.md @@ -21,6 +21,8 @@ --show-first-last / --hide-first-last Show first/last usage of components [default: show-first-last] --show-vert / --hide-vert Show vertical (elevation gain) [default: hide-vert] + --show-retired / --hide-retired + Show retired bikes (on Strava) [default: hide-retired] --units [metric|imperial] Show data in metric or imperial [default: metric] --date-start ISO8601 Filter activities: start at or after the specified date(time) --date-end ISO8601 Filter activities: start before the specified date(time)