Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add CLI as strava_py #1

Merged
merged 2 commits into from
Feb 5, 2022
Merged

Add CLI as strava_py #1

merged 2 commits into from
Feb 5, 2022

Conversation

hugovk
Copy link
Collaborator

@hugovk hugovk commented Feb 5, 2022

This PR adds a CLI for creating visualisations, and puts it into an installable package, ready for distributions via https://pypi.org so it can be installed using pip. (I can help with how to do that later!)

First, I moved the strava_py directory into a src directory. This is commonly used for Python projects. The main benefits is to make sure when you're testing, you're testing against something that has been installed, and not something that happens to have the same directory name in your current dir. Much more:

The way to install from source:

pip install .

Or if you're developing, -e means an editable install, so you can make changes to your local source tree and they're reflected in what's run:

pip install -e .

Then I went for strava_py as the CLI name (this can be changed):

$ strava_py --help
usage: strava_py [-h] [-o OUTPUT_FILE] path

positional arguments:
  path                  Input path to folder with GPX and / or FIT files

options:
  -h, --help            show this help message and exit
  -o OUTPUT_FILE, --output_file OUTPUT_FILE
                        Output PNG file (default: plot.png)

Example run:

$ strava_py /tmp/my_strava_activities
Processing data...
Processing: 6628958226.gpx
Processing: 6626094662.gpx
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 142); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 162); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 203); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 249); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 285); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 326); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 351); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 397); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 443); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 487); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 531); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 575); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 619); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 663); adding dummy dev data...
  warnings.warn(msg)
/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/site-packages/fitdecode/reader.py:909: UserWarning: 'field "native_field_num" (idx #0) not found in message "field_description"' (local_mesg_num: 0; chunk_offset: 707); adding dummy dev data...
  warnings.warn(msg)
Processing: 6678512291.fit
Processing: 6619621876.gpx
Processing: 6604948302.gpx
Processing: 6860888905.fit
Processing: 6678648994.fit
Processing: 6676505895.fit
Processing: 6810573487.fit
Processing: 6623947053.gpx
Processing: 6630963143.gpx
Plotting facets...
Saved to plot.png

I'll also add a bunch of inline comments to explain what some things do, please feel free to ask more about any of this!

@@ -0,0 +1,50 @@
[metadata]
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file contains the metadata for the package.

author = Marcus Volz
license = MIT
license_file = LICENSE
classifiers =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can freely choose classifiers from https://pypi.org/classifiers/. These don't have a huge lot of use, but are listed on a package's PyPI page and can be browsed: https://pypi.org/project/Pillow/

Topic :: Artistic Software
Topic :: Multimedia :: Graphics
Topic :: Scientific/Engineering :: Visualization
keywords =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also used on PyPI. Like classifiers, can be useful for people using the PyPI API.


[options]
packages = find:
install_requires =
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

List the dependent packages here, so they get installed when people install our package.

matplotlib
pandas
seaborn
python_requires = >=3.7
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pick a minimum supported Python version. pip uses this to make sure to install only for the known supported versions.

3.7 is the lowest currently supported: https://endoflife.date/python

@@ -0,0 +1,50 @@
[metadata]
name = strava_py
version = 0.0.1
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(It's possible to use things like https://github.com/pypa/setuptools_scm to make it automatically take the version number from Git tags, but let's start off simple.)


[options.entry_points]
console_scripts =
strava_py = strava_py.cli:main
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this strava_py is the name for the CLI, what the user runs on the command line. Doesn't need to be the same as name= above, but less confusing if they are the same.

@@ -0,0 +1,28 @@
import argparse
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used for parsing (and validating) arguments for us.

)
parser.add_argument("path", help="Input path to folder with GPX and / or FIT files")
parser.add_argument(
"-o", "--output_file", default="plot.png", help="Output PNG file"
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The dashes indicate an optional argument with two forms: short -o and long --output_file. If not specified, the default is used.

parser = argparse.ArgumentParser(
formatter_class=argparse.ArgumentDefaultsHelpFormatter
)
parser.add_argument("path", help="Input path to folder with GPX and / or FIT files")
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No dashes here, so path is a required argument.

@marcusvolz marcusvolz merged commit 1de8b22 into marcusvolz:main Feb 5, 2022
@marcusvolz
Copy link
Owner

@hugovk Amazing, these are exactly the kinds of things I was going to work out later. Thanks so much!

@hugovk hugovk deleted the cli branch February 5, 2022 21:42
@hugovk
Copy link
Collaborator Author

hugovk commented Feb 5, 2022

You're very welcome, happy to help out!

I'll post tomorrow about how to put it on PyPI. Any thoughts for a name? strava_py?

(Side note: PyPI does name normalisation whereby dashes and underscores can be interchanged, and they show dashes by default, but both work. See for example https://github.com/pypa/setuptools_scm/ / https://pypi.org/project/setuptools-scm/ that as a project prefers underscores.)

@marcusvolz
Copy link
Owner

Awesome, thanks again! Having given it a bit more thought, I think "stravavis" is a better name, as the package is focused on visualisation. There was some discussion about moving away from it being strava-centric, but I intend to create new visualizations from other data in the bulk export zip (not just gpx/fit activity files). So the package will be mostly aimed at Strava users, even if some of the visualisations still work with activity files exported from other sources. Let me know what you think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants