Create artistic visualisations with your exercise data (Python version)

Overview

strava_py

Create artistic visualisations with your exercise data (Python version).

This is a port of the R strava package to Python.

Examples

Facets

A plot of activities as small multiples. The concept behind this plot was originally inspired by Sisu.

facets

Map

A map of activities viewed in plan.

map

How to use

Bulk export from Strava

The process for downloading data is described on the Strava website here: [https://support.strava.com/hc/en-us/articles/216918437-Exporting-your-Data-and-Bulk-Export#Bulk], but in essence, do the following:

  1. Log in to Strava
  2. Select "Settings" from the main drop-down menu at top right of the screen
  3. Select "My Account" from the navigation menu to the left of the screen.
  4. Under the "Download or Delete Your Account" heading, click the "Get Started" button.
  5. Under the "Download Request", heading, click the "Request Your Archive" button. Don't click anything else on that page, i.e. particularly not the "Request Account Deletion" button.
  6. Wait for an email to be sent
  7. Click the link in email to download zipped folder containing activities
  8. Unzip files

Process the data

The main function for importing and processing activity files expects a path to a directory of unzipped GPX and / or FIT files. If required, the fit2gpx package provides useful tools for pre-processing bulk files exported from Strava, e.g. unzipping activity files (see Use Case 3: Strava Bulk Export Tools).

df = process_data(<path to folder with GPX and / or FIT files>)

Plot activities as small multiples

plot_facets(df, output_file = 'plot.png')

Plot activity map

plot_map(df, lon_min=None, lon_max= None, lat_min=None, lat_max=None,
             alpha=0.3, linewidth=0.3, output_file="map.png")
Comments
  • Getting:

    Getting: "gpxpy.gpx.GPXException: latitude is mandatory in None (got None)"

    Hey,

    I tried using the CLI tool but I'm getting this error: gpxpy.gpx.GPXException: latitude is mandatory in None (got None)

    I tried setting --lon_max, --lat_max to 180,90 and --lat_min, --lon_min to 0,0 but still getting the same error.

    Is this a problem with a specific .gpx file? And if so how can I find that one because the error doesn't show which file.

    Thanks

    opened by Alpha249 7
  • Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Starting cli.py gives error: ModuleNotFoundError: No module named 'stravavis'

    Thanks for the app, it looks quite nice. But as I'm a bit new to Python, I'm having trouble to get it started.

    I cloned the repo and tried from its root directory: [email protected] strava_py % python3 src/stravavis/cli.py /Users/jmizv/Downloads/gpx

    But I just got that error message:

    Traceback (most recent call last):
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 91, in <module>
        main()
      File "/Users/jmizv/IdeaProjects/strava_py/src/stravavis/cli.py", line 41, in main
        from stravavis.plot_calendar import plot_calendar
    ModuleNotFoundError: No module named 'stravavis'
    [email protected] strava_py % 
    

    I'm probably missing something obvious but I don't see it. Could you, @marcusvolz, please give an advice?

    opened by jmizv 7
  • Add CLI as strava_py

    Add CLI as strava_py

    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:

    • https://packaging.python.org/en/latest/tutorials/packaging-projects/#a-simple-project
    • https://blog.ionelmc.ro/2014/05/25/python-packaging/
    • https://hynek.me/articles/testing-packaging/

    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!

    opened by hugovk 3
  • Elevations

    Elevations

    @hugovk Thanks for reviewing these and for the recent PRs. This one just adds the elevations small multiples plot.

    I think I might have missed the suggested commits in your previous PRs, could you let me know if everything went through ok? Thanks!

    opened by marcusvolz 2
  • ValueError: All objects passed were None

    ValueError: All objects passed were None

    Hi, I have been trying to apply this package on my data pull from Strava but in the first step of processing data I'm getting this error of "ValueError: All objects passed were None" attaching a screenshot for reference. I tried to give address of either the activities or routes folder which has gpx files but still the error remains constant Screenshot 2022-12-25 at 16 03 22

    opened by shu3hamiitkgp 1
  • Fix squashed maps

    Fix squashed maps

    This is similar to https://github.com/marcusvolz/strava/issues/5.

    Here's the same GPX using main:

    strava-map

    Like https://stackoverflow.com/a/14457180/724176, rather than using latitude and longitude values, this uses a Mercator projection calculation to come up with x and y values instead.

    Dummy, unit map width and height values are used, as we don't know and don't need to know the final size as matplotlib will take care of that.

    And with this PR:

    strava-map

    Measuring performance:

    With main, on 2,946 GPX files (but commenting out some bits of cli.py to only run "Processing data..." and "Plotting map..."), it takes 11m45s on a Mac M1 16GB.

    With the PR it takes a comparable 11m43s, which is really good because the transform is done by Pandas so presumably in C.

    opened by hugovk 1
  • UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>

    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to

    When I execute stravavis against this single attached file (5670595833.gpx.txt, you need to remove the txt extension) it fails with the below error. I tend to add emojis to my strava activity titles so this is in my opinion the problem. Not sure if you could exclude this easily? The files are certainly not encoded with Cp1252.

    C:\Users\...>stravavis C:\Users\...
    Processing data...
    Processing ----------------------------------------   0% -:--:--
    multiprocessing.pool.RemoteTraceback:
    """
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 125, in worker
        result = (True, func(*args, **kwds))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 13, in process_file
        return process_gpx(fpath)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 21, in process_gpx
        activity = gpxpy.parse(open(gpxfile))
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\__init__.py", line 37, in parse
        parser = mod_parser.GPXParser(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 70, in __init__
        self.init(xml_or_file)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\gpxpy\parser.py", line 82, in init
        text = xml_or_file.read() if hasattr(xml_or_file, 'read') else xml_or_file # type: ignore
      File "C:\Program Files\Python310\lib\encodings\cp1252.py", line 23, in decode
        return codecs.charmap_decode(input,self.errors,decoding_table)[0]
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    """
    
    The above exception was the direct cause of the following exception:
    
    Traceback (most recent call last):
      File "C:\Program Files\Python310\lib\runpy.py", line 196, in _run_module_as_main
        return _run_code(code, main_globals, None,
      File "C:\Program Files\Python310\lib\runpy.py", line 86, in _run_code
        exec(code, run_globals)
      File "C:\Users\...\AppData\Roaming\Python\Python310\Scripts\stravavis.exe\__main__.py", line 7, in <module>
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\cli.py", line 51, in main
        df = process_data(args.path)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\stravavis\process_data.py", line 108, in process_data
        processed = list(it)
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 168, in track
        yield from progress.track(
      File "C:\Users\...\AppData\Roaming\Python\Python310\site-packages\rich\progress.py", line 1211, in track
        for value in sequence:
      File "C:\Program Files\Python310\lib\multiprocessing\pool.py", line 870, in next
        raise value
    UnicodeDecodeError: 'charmap' codec can't decode byte 0x8d in position 379: character maps to <undefined>
    

    5670595833.gpx.txt

    bug 
    opened by jmizv 1
  • Add --bbox CLI param as a shortcut

    Add --bbox CLI param as a shortcut

    It's a bit tedious to enter four params and values on the command line when creating a map:

    stravavis activities --lon_min 23.516665 --lat_min 60.000467 --lon_max 26.390877 --lat_max 61.360291

    This PR adds a --bbox shortcut for those, that takes a bounding box of comma-separated values of lon_min, lat_min, lon_max, lat_max:

    stravavis activities --bbox 23.516665,60.000467,26.390877,61.360291

    It's quite easy to find bounding boxes online, for example: http://bboxfinder.com/#60.000467,23.516665,61.360291,26.390877


    I also noticed that these lat/lon values are not actually passed from the CLI to the plotting function, and neither are alpha or linewidth, so let's fix that too :)


    Finally, bump some GitHub Actions versions for the CI.

    enhancement 
    opened by hugovk 1
  • Updated README: Add installation instructions and link directly to images

    Updated README: Add installation instructions and link directly to images

    Images are broken on PyPI:

    image

    Instead of linking to things like https://github.com/marcusvolz/strava_py/blob/main/plots/facets001.png, which is an HTML page showing the image, and which GitHub can apparently handle redirects to for the README, link directly to the image file, like https://raw.githubusercontent.com/marcusvolz/strava_py/main/plots/facets001.png

    This also adds installation instructions to the README.

    opened by hugovk 1
  • More progress bars for slow loops, and refactor

    More progress bars for slow loops, and refactor

    Also in Python we generally prefer to iterate over objects in a list (or tuple, or other so-called "iterable") rather than using an index of range(len(thing)).

    So for example, instead of:

    n = len(activities)
    for i in range(n):
        print(activities[i])
    

    This is more direct and readable:

    for activity in activities:
        print(activity)
    

    If we do need an index as well, enumerate is often used:

    for i, activity in enumerate(activities):
        print(i, activity)
    
    opened by hugovk 1
  • Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files

    Use multiprocessing to speed up processing of input files, using the number of processes which matches the CPU count.

    On my old dual-core Mac, the processing step speeds up:

    • 601 files (all of 2021): 2m7s -> 1m20s
    • 2,629 files (my full Strava archive): 10m29s -> 5m9s

    Will be a larger gain for machines with more CPUs.

    multiprocessing needs a single worker function, this is the new process_file which decides which of process_gpx and process_fit to call. They also need to be top-level functions to work with multiprocessing.

    I also formatted the file using the popular Black autoformatter:

    python -m pip install -U black
    black . 
    

    I also fixed a bug when using an input path like ~/dir, it needs to expand ~ before checking it's a dir (and appending *).

    opened by hugovk 1
Releases(v0.1.0)
  • v0.1.0(Dec 30, 2022)

    What's Changed

    • Add --bbox CLI param as a shortcut by @hugovk in https://github.com/marcusvolz/strava_py/pull/18
    • Add support for Python 3.11 by @hugovk in https://github.com/marcusvolz/strava_py/pull/26
    • Catch exception to skip invalid file by @hugovk in https://github.com/marcusvolz/strava_py/pull/27
    • Fix encoding on Windows by @hugovk in https://github.com/marcusvolz/strava_py/pull/20
    • Fix squashed maps by @hugovk in https://github.com/marcusvolz/strava_py/pull/21
    • Fix README typo by @hugovk in https://github.com/marcusvolz/strava_py/pull/24

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.2...v0.1.0

    Source code(tar.gz)
    Source code(zip)
  • v0.0.2(Sep 1, 2022)

    What's Changed

    • Updated README: Add installation instructions and link directly to images by @hugovk in https://github.com/marcusvolz/strava_py/pull/17

    Full Changelog: https://github.com/marcusvolz/strava_py/compare/v0.0.1...v0.0.2

    Source code(tar.gz)
    Source code(zip)
  • v0.0.1(Sep 1, 2022)

    What's Changed

    • Add CLI as strava_py by @hugovk in https://github.com/marcusvolz/strava_py/pull/1
    • added plot_map by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/2
    • Rename package and CLI program to stravavis by @hugovk in https://github.com/marcusvolz/strava_py/pull/3
    • Allow input path specification and show progress bar by @hugovk in https://github.com/marcusvolz/strava_py/pull/4
    • Elevations by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/5
    • Test run on GitHub Actions by @hugovk in https://github.com/marcusvolz/strava_py/pull/6
    • Landscape by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/8
    • Use multiprocessing to speed up processing of input files by @hugovk in https://github.com/marcusvolz/strava_py/pull/9
    • Calendar by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/10
    • Fix local variable 'activities' referenced before assignment by @hugovk in https://github.com/marcusvolz/strava_py/pull/11
    • Fix ValueError with --activities_path by @hugovk in https://github.com/marcusvolz/strava_py/pull/12
    • Dumbbell by @marcusvolz in https://github.com/marcusvolz/strava_py/pull/13
    • Allow --activities_path to point to file or directory by @hugovk in https://github.com/marcusvolz/strava_py/pull/14
    • More progress bars for slow loops, and refactor by @hugovk in https://github.com/marcusvolz/strava_py/pull/15

    New Contributors

    • @hugovk made their first contribution in https://github.com/marcusvolz/strava_py/pull/1
    • @marcusvolz made their first contribution in https://github.com/marcusvolz/strava_py/pull/2

    Full Changelog: https://github.com/marcusvolz/strava_py/commits/v0.0.1

    Source code(tar.gz)
    Source code(zip)
Owner
Marcus Volz
Data visualisation | mathematical visualisation | 3D visualisation
Marcus Volz
Create charts with Python in a very similar way to creating charts using Chart.js

Create charts with Python in a very similar way to creating charts using Chart.js. The charts created are fully configurable, interactive and modular and are displayed directly in the output of the t

Nicolas H 68 Dec 08, 2022
The Metabolomics Integrator (MINT) is a post-processing tool for liquid chromatography-mass spectrometry (LCMS) based metabolomics.

MINT (Metabolomics Integrator) The Metabolomics Integrator (MINT) is a post-processing tool for liquid chromatography-mass spectrometry (LCMS) based m

Sören Wacker 0 May 04, 2022
Curvipy - The Python package for visualizing curves and linear transformations in a super simple way

Curvipy - The Python package for visualizing curves and linear transformations in a super simple way

Dylan Tintenfich 55 Dec 28, 2022
This component provides a wrapper to display SHAP plots in Streamlit.

streamlit-shap This component provides a wrapper to display SHAP plots in Streamlit.

Snehan Kekre 30 Dec 10, 2022
A dashboard built using Plotly-Dash for interactive visualization of Dex-connected individuals across the country.

Dashboard For The DexConnect Platform of Dexterity Global Working prototype submission for internship at Dexterity Global Group. Dashboard for real ti

Yashasvi Misra 2 Jun 15, 2021
Pretty Confusion Matrix

Pretty Confusion Matrix Why pretty confusion matrix? We can make confusion matrix by using matplotlib. However it is not so pretty. I want to make con

Junseo Ko 5 Nov 22, 2022
Datapane is the easiest way to create data science reports from Python.

Datapane Teams | Documentation | API Docs | Changelog | Twitter | Blog Share interactive plots and data in 3 lines of Python. Datapane is a Python lib

Datapane 744 Jan 06, 2023
High-level geospatial data visualization library for Python.

geoplot: geospatial data visualization geoplot is a high-level Python geospatial plotting library. It's an extension to cartopy and matplotlib which m

Aleksey Bilogur 1k Jan 01, 2023
DataVisualization - The evolution of my arduino and python journey. New level of competence achieved

DataVisualization - The evolution of my arduino and python journey. New level of competence achieved

1 Jan 03, 2022
Sky attention heatmap of submissions to astrometry.net

astroheat Installation Requires Python 3.6+, Tested with Python 3.9.5 Install library dependencies pip install -r requirements.txt The program require

4 Jun 20, 2022
This is a super simple visualization toolbox (script) for transformer attention visualization ✌

Trans_attention_vis This is a super simple visualization toolbox (script) for transformer attention visualization ✌ 1. How to prepare your attention m

Mingyu Wang 3 Jul 09, 2022
Data visualization electromagnetic spectrum

Datenvisualisierung-Elektromagnetischen-Spektrum Anhand des Moduls matplotlib sollen die Daten des elektromagnetischen Spektrums dargestellt werden. D

Pulsar 1 Sep 01, 2022
阴阳师后台全平台(使用网易 MuMu 模拟器)辅助。支持御魂,觉醒,御灵,结界突破,秘闻副本,地域鬼王。

阴阳师后台全平台辅助 Python 版本:Python 3.8.3 模拟器:网易 MuMu | 雷电模拟器 模拟器分辨率:1024*576 显卡渲染模式:兼容(OpenGL) 兼容 Windows 系统和 MacOS 系统 思路: 利用 adb 截图后,使用 opencv 找图找色,模拟点击。使用

简讯 27 Jul 09, 2022
Plot-configurations for scientific publications, purely based on matplotlib

TUEplots Plot-configurations for scientific publications, purely based on matplotlib. Usage Please have a look at the examples in the example/ directo

Nicholas Krämer 487 Jan 08, 2023
A Simple Flask-Plotly Example for NTU 110-1 DSSI Class

A Simple Flask-Plotly Example for NTU 110-1 DSSI Class Live Demo Prerequisites We will use Flask and Ploty to build a Flask application. If you haven'

Ting Ni Wu 1 Dec 11, 2021
Simple implementation of Self Organizing Maps (SOMs) with rectangular and hexagonal grid topologies

py-self-organizing-map Simple implementation of Self Organizing Maps (SOMs) with rectangular and hexagonal grid topologies. A SOM is a simple unsuperv

Jonas Grebe 1 Feb 10, 2022
Displaying plot of death rates from past years in Poland. Data source from these years is in readme

Average-Death-Rate Displaying plot of death rates from past years in Poland The goal collect the data from a CSV file count the ADR (Average Death Rat

Oliwier Szymański 0 Sep 12, 2021
Automatically Visualize any dataset, any size with a single line of code. Created by Ram Seshadri. Collaborators Welcome. Permission Granted upon Request.

AutoViz Automatically Visualize any dataset, any size with a single line of code. AutoViz performs automatic visualization of any dataset with one lin

AutoViz and Auto_ViML 1k Jan 02, 2023
Certificate generating and sending system written in Python.

Certificate Generator & Sender How to use git clone https://github.com/saadhaxxan/Certificate-Generator-Sender.git cd Certificate-Generator-Sender Add

Saad Hassan 11 Dec 01, 2022
Schema validation for Xarray objects

xarray-schema Schema validation for Xarray installation This package is in the early stages of development. Install it from source: pip install git+gi

carbonplan 22 Oct 31, 2022