Skip to content

Commit

Permalink
Add alternative CSV input
Browse files Browse the repository at this point in the history
  • Loading branch information
liskin committed Feb 6, 2024
1 parent 2c72f62 commit 105c774
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 25 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ omit the `[strava]` bit to avoid installing strava-offline twice.
Usage: strava-ical [OPTIONS]

Options:
--csv FILENAME Load activities from CSV instead of the strava-offline database (columns: distance,
elapsed_time, id, moving_time, name, start_date, start_latlng, total_elevation_gain, type)
--strava-database PATH Location of the strava-offline database [default:
/home/user/.local/share/strava_offline/strava.sqlite]
-o, --output FILENAME Output file [default: -]
Expand Down
21 changes: 18 additions & 3 deletions src/strava_ical/cli.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
from itertools import chain
from pathlib import Path
from typing import BinaryIO
from typing import Optional
from typing import TextIO

import click
import platformdirs

from .data import essential_columns
from .data import optional_columns
from .ical import ical
from .strava_offline import read_strava_offline
from .input import read_input_csv
from .input import read_strava_offline


@click.command(context_settings={'max_content_width': 120})
@click.option(
'--csv', type=click.File('r'),
help=f"""
Load activities from CSV instead of the strava-offline database
(columns: {", ".join(sorted(chain(essential_columns, optional_columns)))})
""")
@click.option(
'--strava-database', type=click.Path(path_type=Path), # type: ignore [type-var] # debian typeshed compat
default=platformdirs.user_data_path(appname='strava_offline') / 'strava.sqlite',
Expand All @@ -17,6 +29,9 @@
@click.option(
'-o', '--output', type=click.File('wb'), default='-', show_default=True,
help="Output file")
def cli(strava_database: Path, output: BinaryIO):
activities = read_strava_offline(strava_database)
def cli(csv: Optional[TextIO], strava_database: Path, output: BinaryIO):
if csv:
activities = read_input_csv(csv)
else:
activities = read_strava_offline(strava_database)
output.write(ical(activities))
3 changes: 3 additions & 0 deletions src/strava_ical/data.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
'start_date',
'type',
}
optional_columns = {
'start_latlng',
}


class Activity:
Expand Down
47 changes: 47 additions & 0 deletions src/strava_ical/input.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import csv
import json
from os import PathLike
import sqlite3
from typing import Iterable
from typing import TextIO
from typing import Union

from .data import Activity
from .data import essential_columns


def read_input_csv(inp: TextIO) -> Iterable[Activity]:
"""
Read activities from CSV generated from this command:
sqlite3 ~/.local/share/strava_offline/strava.sqlite \
".mode csv" \
".headers on" \
"SELECT distance, elapsed_time, id, moving_time, name, start_date, \
json_extract(json, '$.start_latlng') AS 'start_latlng', total_elevation_gain, type FROM activity" \
>activities.csv
"""
for r in csv.DictReader(inp):
assert essential_columns <= r.keys()
r['id'] = int(r['id'])

Check failure on line 26 in src/strava_ical/input.py

View workflow job for this annotation

GitHub Actions / check-distro (ubuntu:latest)

Incompatible types in assignment (expression has type "int", target has type "str")
r['distance'] = float(r['distance'])

Check failure on line 27 in src/strava_ical/input.py

View workflow job for this annotation

GitHub Actions / check-distro (ubuntu:latest)

Incompatible types in assignment (expression has type "float", target has type "str")
r['total_elevation_gain'] = float(r['total_elevation_gain'])

Check failure on line 28 in src/strava_ical/input.py

View workflow job for this annotation

GitHub Actions / check-distro (ubuntu:latest)

Incompatible types in assignment (expression has type "float", target has type "str")
r['moving_time'] = int(r['moving_time'])

Check failure on line 29 in src/strava_ical/input.py

View workflow job for this annotation

GitHub Actions / check-distro (ubuntu:latest)

Incompatible types in assignment (expression has type "int", target has type "str")
r['elapsed_time'] = int(r['elapsed_time'])

Check failure on line 30 in src/strava_ical/input.py

View workflow job for this annotation

GitHub Actions / check-distro (ubuntu:latest)

Incompatible types in assignment (expression has type "int", target has type "str")
if 'start_latlng' in r:
r['start_latlng'] = json.loads(r['start_latlng'])
yield Activity(r)


def read_strava_offline(db_filename: Union[str, PathLike]) -> Iterable[Activity]:
"""
Read activities from strava-offline database.
"""
with sqlite3.connect(db_filename) as db:
db.row_factory = sqlite3.Row

for r in db.execute('SELECT * FROM activity'):
r_json = json.loads(r['json'])
r = {**r_json, **r}
assert essential_columns <= set(r.keys())
yield Activity(r)
22 changes: 0 additions & 22 deletions src/strava_ical/strava_offline.py

This file was deleted.

2 changes: 2 additions & 0 deletions tests/readme/help.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
Usage: strava-ical [OPTIONS]

Options:
--csv FILENAME Load activities from CSV instead of the strava-offline database (columns: distance,
elapsed_time, id, moving_time, name, start_date, start_latlng, total_elevation_gain, type)
--strava-database PATH Location of the strava-offline database [default:
/home/user/.local/share/strava_offline/strava.sqlite]
-o, --output FILENAME Output file [default: -]
Expand Down

0 comments on commit 105c774

Please sign in to comment.