Skip to content

Commit

Permalink
Merge pull request #583 from NaturezzZ/sync_garmin_cn_global
Browse files Browse the repository at this point in the history
New feature: Sync garmin cn to garmin global
  • Loading branch information
NaturezzZ authored Jan 5, 2024
2 parents 7180aa2 + 634b25f commit 3a35e45
Show file tree
Hide file tree
Showing 8 changed files with 250 additions and 4 deletions.
11 changes: 10 additions & 1 deletion .github/workflows/run_data_sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ on:

env:
# please change to your own config.
RUN_TYPE: pass # support strava/nike/garmin/garmin_cn/keep/only_gpx/only_fit/nike_to_strava/strava_to_garmin/strava_to_garmin_cn/garmin_to_strava/garmin_to_strava_cn/codoon, Please change the 'pass' it to your own
RUN_TYPE: pass # support strava/nike/garmin/garmin_cn/garmin_sync_cn_global/keep/only_gpx/only_fit/nike_to_strava/strava_to_garmin/strava_to_garmin_cn/garmin_to_strava/garmin_to_strava_cn/codoon, Please change the 'pass' it to your own
ATHLETE: yihong0618
TITLE: Yihong0618 Running
MIN_GRID_DISTANCE: 10 # change min distance here
Expand Down Expand Up @@ -125,6 +125,15 @@ jobs:
# If you only want to sync `type running` add args --only-run, default script is to sync all data (rides and runs).
# python run_page/garmin_sync.py ${{ secrets.GARMIN_SECRET_STRING_CN }} --only-run --is-cn

- name: Run sync Garmin CN to Garmin script
if: env.RUN_TYPE == 'garmin_sync_cn_global'
run: |
# make garimin secret string `python run_page/garmin_sync_cn_global.py ${email} ${password} --is-cn
python run_page/garmin_sync_cn_global.py ${{ secrets.GARMIN_SECRET_STRING_CN }} ${{ secrets.GARMIN_SECRET_STRING }}
# If you only want to sync `type running` add args --only-run, default script is to sync all data (rides and runs).
# python run_page/garmin_sync_cn_global.py ${{ secrets.GARMIN_SECRET_STRING_CN }} ${{ secrets.GARMIN_SECRET_STRING }} --only-run


- name: Run sync Only GPX script
if: env.RUN_TYPE == 'only_gpx'
run: |
Expand Down
38 changes: 38 additions & 0 deletions README-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ R.I.P. 希望大家都能健康顺利的跑过终点,逝者安息。
- **[GPX](#gpx)**
- **[TCX](#tcx)**
- **[FIT](#fit)**
- **[佳明国内同步国际](#Garmin-CN-to-Garmin)**
- **[Tcx+Strava(upload all tcx data to strava)](#tcx_to_strava)**
- **[Gpx+Strava(upload all tcx data to strava)](#gpx_to_strava)**
- **[Nike+Strava(Using NRC Run, Strava backup data)](#nikestrava)**
Expand Down Expand Up @@ -570,6 +571,43 @@ python3(python) run_page/garmin_sync.py xxxxxxxxxx --is-cn --only-run

</details>

### Garmin-CN to Garmin

<details>
<summary> 同步佳明 CN 数据到 佳明国际区</summary>

<br>

- 如果你只想同步 `type running` 使用参数 --only-run
**The Python version must be >=3.10**

#### 获取佳明 CN 的密钥

在终端中输入以下命令

```bash
python3(python) run_page/get_garmin_secret.py ${your email} ${your password} --is-cn
```

#### 获取佳明全球的密钥

在终端中输入以下命令

```bash
python3(python) run_page/get_garmin_secret.py ${your email} ${your password}
```

#### 同步 佳明 CN 到 佳明全球

在终端中输入以下命令

```bash
python3(python) run_page/garmin_sync_cn_global.py ${garmin_cn_secret_string} ${garmin_secret_string}
```

</details>


### Nike Run Club

<details>
Expand Down
40 changes: 40 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ English | [简体中文](https://github.com/yihong0618/running_page/blob/master/
- **[GPX](#gpx)**
- **[TCX](#tcx)**
- **[FIT](#fit)**
- **[Garmin-CN_to_Garmin(Sync Garmin-CN activities to Garmin Global)](#garmin-cn-to-garmin)**
- **[Nike_to_Strava(Using NRC Run, Strava backup data)](#nike_to_strava)**
- **[Tcx_to_Strava(upload all tcx data to strava)](#tcx_to_strava)**
- **[Gpx_to_Strava(upload all gpx data to strava)](#gpx_to_strava)**
Expand Down Expand Up @@ -379,6 +380,45 @@ python3(python) run_page/garmin_sync.py xxxxxxxxxxxxxx(secret_string) --is-cn -

</details>

### Garmin-CN to Garmin

<details>
<summary> Sync your <code>Garmin-CN</code> data to <code>Garmin</code></summary>

<br>

- If you only want to sync `type running` add args --only-run
**The Python version must be >=3.10**

#### Get Garmin CN Secret

Enter the following command in the terminal

```bash
# to get secret_string
python3(python) run_page/get_garmin_secret.py ${your email} ${your password} --is-cn
```

#### Get Garmin Secret

Enter the following command in the terminal

```bash
# to get secret_string
python3(python) run_page/get_garmin_secret.py ${your email} ${your password}
```

#### Sync Garmin CN to Garmin

Enter the following command in the terminal

```bash
# to sync garmin-cn to garmin-global
python3(python) run_page/garmin_sync_cn_global.py ${garmin_cn_secret_string} ${garmin_secret_string}
```

</details>

### Nike Run Club

<details>
Expand Down
1 change: 1 addition & 0 deletions run_page/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
SQL_FILE = os.path.join(parent, "run_page", "data.db")
JSON_FILE = os.path.join(parent, "src", "static", "activities.json")
SYNCED_FILE = os.path.join(parent, "imported.json")
SYNCED_ACTIVITY_FILE = os.path.join(parent, "synced_activity.json")

# TODO: Move into nike_sync NRC THINGS

Expand Down
36 changes: 35 additions & 1 deletion run_page/garmin_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ async def download_activity(self, activity_id, file_type="gpx"):
response.raise_for_status()
return response.read()

async def upload_activities_original(self, datas, use_fake_garmin_device=False):
async def upload_activities_original_from_strava(
self, datas, use_fake_garmin_device=False
):
print(
"start upload activities to garmin!, use_fake_garmin_device:",
use_fake_garmin_device,
Expand Down Expand Up @@ -152,6 +154,38 @@ async def upload_activities_original(self, datas, use_fake_garmin_device=False):
print("garmin upload failed: ", e)
await self.req.aclose()

async def upload_activity_from_file(self, file):
print("Uploading " + str(file))
f = open(file, "rb")

file_body = BytesIO(f.read())
files = {"file": (file, file_body)}

try:
res = await self.req.post(
self.upload_url, files=files, headers=self.headers
)
f.close()
except Exception as e:
print(str(e))
# just pass for now
return
try:
resp = res.json()["detailedImportResult"]
print("garmin upload success: ", resp)
except Exception as e:
print("garmin upload failed: ", e)

async def upload_activities_files(self, files):
print("start upload activities to garmin!")

await gather_with_concurrency(
10,
[self.upload_activity_from_file(file=f) for f in files],
)

await self.req.aclose()


class GarminConnectHttpError(Exception):
def __init__(self, status):
Expand Down
105 changes: 105 additions & 0 deletions run_page/garmin_sync_cn_global.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""
Python 3 API wrapper for Garmin Connect to get your statistics.
Copy most code from https://github.com/cyberjunky/python-garminconnect
"""

import argparse
import asyncio
import logging
import os
import sys
import time
import traceback
import zipfile
from io import BytesIO

import aiofiles
import cloudscraper
import garth
import httpx
from config import FIT_FOLDER, GPX_FOLDER, JSON_FILE, SQL_FILE, config
from garmin_device_adaptor import wrap_device_info
from garmin_sync import Garmin, get_downloaded_ids
from garmin_sync import download_new_activities, gather_with_concurrency
from synced_data_file_logger import load_synced_activity_list, save_synced_activity_list
from utils import make_activities_file

if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument(
"cn_secret_string", nargs="?", help="secret_string fro get_garmin_secret.py"
)
parser.add_argument(
"global_secret_string", nargs="?", help="secret_string fro get_garmin_secret.py"
)
parser.add_argument(
"--only-run",
dest="only_run",
action="store_true",
help="if is only for running",
)

options = parser.parse_args()
secret_string_cn = options.cn_secret_string
secret_string_global = options.global_secret_string
auth_domain = "CN"
is_only_running = options.only_run
if secret_string_cn is None or secret_string_global is None:
print("Missing argument nor valid configuration file")
sys.exit(1)

# Step 1:
# Sync all activities from Garmin CN to Garmin Global in FIT format
# If the activity is manually imported with a GPX, the GPX file will be synced

# load synced activity list
synced_activity = load_synced_activity_list()

folder = FIT_FOLDER
# make gpx or tcx dir
if not os.path.exists(folder):
os.mkdir(folder)

loop = asyncio.get_event_loop()
future = asyncio.ensure_future(
download_new_activities(
secret_string_cn,
auth_domain,
synced_activity,
is_only_running,
folder,
"fit",
)
)
loop.run_until_complete(future)
new_ids = future.result()

to_upload_files = []
for i in new_ids:
if os.path.exists(os.path.join(FIT_FOLDER, f"{i}.fit")):
# upload fit files
to_upload_files.append(os.path.join(FIT_FOLDER, f"{i}.fit"))
elif os.path.exists(os.path.join(GPX_FOLDER, f"{i}.gpx")):
# upload gpx files which are manually uploaded to garmin connect
to_upload_files.append(os.path.join(GPX_FOLDER, f"{i}.gpx"))

print("Files to sync:" + " ".join(to_upload_files))
garmin_global_client = Garmin(
secret_string_global,
config("sync", "garmin", "authentication_domain"),
is_only_running,
)
loop = asyncio.get_event_loop()
future = asyncio.ensure_future(
garmin_global_client.upload_activities_files(to_upload_files)
)
loop.run_until_complete(future)

# Save synced activity list for speeding up
synced_activity.extend(new_ids)
save_synced_activity_list(synced_activity)

# Step 2:
# Generate track from fit/gpx file
make_activities_file(SQL_FILE, GPX_FOLDER, JSON_FILE, file_suffix="gpx")
make_activities_file(SQL_FILE, FIT_FOLDER, JSON_FILE, file_suffix="fit")
4 changes: 3 additions & 1 deletion run_page/strava_to_garmin_sync.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@ async def upload_to_activities(
files_list.append(data)
except Exception as ex:
print("get strava data error: ", ex)
await garmin_client.upload_activities_original(files_list, use_fake_garmin_device)
await garmin_client.upload_activities_original_from_strava(
files_list, use_fake_garmin_device
)
return files_list


Expand Down
19 changes: 18 additions & 1 deletion run_page/synced_data_file_logger.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import os
from config import SYNCED_FILE
from config import SYNCED_FILE, SYNCED_ACTIVITY_FILE
import json


Expand All @@ -12,6 +12,11 @@ def save_synced_data_file_list(file_list: list):
json.dump(file_list, f)


def save_synced_activity_list(activity_list: list):
with open(SYNCED_ACTIVITY_FILE, "w") as f:
json.dump(activity_list, f)


def load_synced_file_list():
if os.path.exists(SYNCED_FILE):
with open(SYNCED_FILE, "r") as f:
Expand All @@ -22,3 +27,15 @@ def load_synced_file_list():
pass

return []


def load_synced_activity_list():
if os.path.exists(SYNCED_ACTIVITY_FILE):
with open(SYNCED_ACTIVITY_FILE, "r") as f:
try:
return json.load(f)
except Exception as e:
print(f"json load {SYNCED_ACTIVITY_FILE} \nerror {e}")
pass

return []

1 comment on commit 3a35e45

@vercel
Copy link

@vercel vercel bot commented on 3a35e45 Jan 5, 2024

Choose a reason for hiding this comment

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

Successfully deployed to the following URLs:

running-page – ./

running-page-git-master-yihong0618.vercel.app
running-page.vercel.app
running-page-yihong0618.vercel.app

Please sign in to comment.