diff --git a/Makefile b/Makefile index 7ee58e582b68323035707fa6c46fd61118ce71d2..e3c7d9694c41101ecc397e2d9b06d701954a931c 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,10 @@ help: # Print help on Makefile expand -t20 open_all: # Open all projects files - ${EDITOR} ${VIRTUAL_ENV}/bin/activate + test -n ${VIRTUAL_ENV} && ${EDITOR} ${VIRTUAL_ENV}/bin/activate ${EDITOR} .gitignore .gitlab-ci.yml Makefile README.md requirements.txt requirements-dev.txt setup.cfg ${EDITOR} .git/hooks/p*-commit - ${EDITOR} cli/run.py + ${EDITOR} cli/*.py pre_commit: # Run the pre-commit hook .git/hooks/pre-commit diff --git a/README.md b/README.md index bd2d6dc4f9574f0dda5ebc57e240f479091a85af..37626ddb4ee579ab62ce9fd38f9881d5cfc42896 100644 --- a/README.md +++ b/README.md @@ -17,14 +17,14 @@ A python CLI to share data related to physical activities over an API (geo-local These are imagined with _[Strava](https://developers.strava.com/docs/reference/) in mind_, but any other service will be nice too : -* [Create manual activity](https://lab.frogg.it/fcode/geostrapy/-/issues/1) -* From a GPX track: [get characteristics](https://lab.frogg.it/fcode/geostrapy/-/issues/2) - * and [create an activity](https://lab.frogg.it/fcode/geostrapy/-/issues/3) +* [ ] [Create manual activity](https://lab.frogg.it/fcode/geostrapy/-/issues/1) +* [ ] From a GPX track: [get characteristics](https://lab.frogg.it/fcode/geostrapy/-/issues/2) + * [ ] and [create an activity](https://lab.frogg.it/fcode/geostrapy/-/issues/3) * Sanitise a GPX track: - * [crop start, end & stop points](https://lab.frogg.it/fcode/geostrapy/-/issues/4) - * [change start-time](https://lab.frogg.it/fcode/geostrapy/-/issues/5) -* [Post GPX file as activity](https://lab.frogg.it/fcode/geostrapy/-/issues/6) -* [Process GPX multi-track file](https://lab.frogg.it/fcode/geostrapy/-/issues/7) + * [ ] [crop start, end & stop points](https://lab.frogg.it/fcode/geostrapy/-/issues/4) + * [ ] [change start-time](https://lab.frogg.it/fcode/geostrapy/-/issues/5) +* [ ] [Post GPX file as activity](https://lab.frogg.it/fcode/geostrapy/-/issues/6) +* [ ] [Process GPX multi-track file](https://lab.frogg.it/fcode/geostrapy/-/issues/7) * and [maybe more](https://lab.frogg.it/fcode/geostrapy/-/boards)… diff --git a/cli/api.py b/cli/api.py new file mode 100644 index 0000000000000000000000000000000000000000..22b11f137dbfc891e99e75a9f2e90d43566c3246 --- /dev/null +++ b/cli/api.py @@ -0,0 +1,17 @@ +""" +API utilities +""" + +from requests import HTTPError + +from strava.api._helpers import client, url, json + + +def post_activity(xargs): + """API call to create an activity""" + + response = client.post(url=url("/activities"), data=xargs) + try: + return json(response) + except HTTPError: + return response diff --git a/cli/commands.py b/cli/commands.py new file mode 100644 index 0000000000000000000000000000000000000000..b4451e9266fb5838e236875648dfb8e8fdafbc0f --- /dev/null +++ b/cli/commands.py @@ -0,0 +1,131 @@ +""" +CLI commands +""" + +import datetime + +import click +from strava.decorators import ( + output_option, + login_required, + format_result, + TableFormat, + OutputType, +) +from strava.formatters import ( + humanize, + apply_formatters, +) + +from api import post_activity + +_ACTIVITY_COLUMNS = ("key", "value") + + +@click.option( + "-n", + "--name", + prompt=True, + type=str, + required=True, + default="Activity", + help="The name of the activity", +) +@click.option( + "-a", + "--activity_type", + prompt=True, + type=str, + required=True, + default="run", + help="Type of activity", +) +@click.option( + "-s", + "--start", + prompt=True, + type=str, + required=True, + default=datetime.datetime.now().isoformat(), + help="ISO 8601 formatted date time", +) +@click.option("-t", "--time", prompt=True, type=int, required=True, help="In seconds") +@click.option("-d", "--desc", prompt=True, type=str, help="Description of the activity") +@click.option("-D", "--dist", prompt=True, type=int, help="In meters") +@click.option( + "-c", + "--commute", + prompt=True, + type=int, + default="0", + prompt_required=False, + help="Set to 1 to mark as commute", +) +@click.option( + "-T", + "--trainer", + prompt=True, + type=int, + default="0", + prompt_required=False, + help="Set to 1 to mark as a trainer activity", +) +@click.option( + "-h", + "--hide", + prompt=True, + type=int, + default="0", + prompt_required=False, + help="Set to true to mute activity", +) +@click.command("create") +@output_option() +@login_required +def post_create(**kwargs): + """Create an activity""" + xargs = { + "name": kwargs["name"], + "type": kwargs["activity_type"], + "trainer": kwargs["trainer"], + "commute": kwargs["commute"], + "distance": kwargs["dist"], + "description": kwargs["desc"], + "elapsed_time": kwargs["time"], + "hide_from_home": kwargs["hide"], + "start_date_local": kwargs["start"], + } + result = post_activity(xargs) + _format_upload(result, output=kwargs["output"]) + + +@format_result( + table_columns=_ACTIVITY_COLUMNS, + show_table_headers=False, + table_format=TableFormat.PLAIN, +) +def _format_upload(result, output=None): + return result if output == OutputType.JSON.value else _as_table(result) + + +def _as_table(upload_result): + def format_id(upload_id): + return f"{upload_id}" + + def format_error(upload_error): + return f"{upload_error}" + + def format_property(name): + return click.style(f"{humanize(name)}:", bold=True) + + formatters = { + "id": format_id, + "errors": format_error, + } + + basic_data = [ + {"key": format_property(k), "value": v} + for k, v in apply_formatters(upload_result, formatters).items() + ] + + return [*basic_data] diff --git a/cli/run.py b/cli/main.py similarity index 89% rename from cli/run.py rename to cli/main.py index 60778ff3500aa5c7aae40fca36c6419d52339c8b..9a98d1d0d888109d15459c90e3e626dd25388964 100644 --- a/cli/run.py +++ b/cli/main.py @@ -15,6 +15,8 @@ from strava.commands import ( set_config, ) +from commands import post_create + @click.group() def cli(): @@ -26,6 +28,7 @@ def cli(): cli.add_command(login) cli.add_command(logout) +cli.add_command(post_create) cli.add_command(set_config) if __name__ == "__main__":