-
-
Save hutattedonmyarm/70ce9d690c47b43c4ea79c38df298826 to your computer and use it in GitHub Desktop.
import xml.etree.cElementTree as ElementTree | |
import json, pytz, zipfile, unicodedata, re | |
from datetime import datetime | |
from os import listdir | |
from os.path import isfile, join | |
import glob | |
def slugify(value): | |
""" | |
Normalizes string, converts to lowercase, removes non-alpha characters. | |
Slightly modified from https://github.com/django/django/blob/master/django/utils/text.py | |
""" | |
value = str(value) | |
try: | |
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii') | |
value = re.sub(r'[^\w\s-]', '', value).strip().lower() | |
value = re.sub(r'[-\s]+', '-', value) | |
except: | |
print('Could not slugify filename, will use default one instead') | |
value = 'output' | |
return value | |
creator = "GapX" | |
version = "1.1" | |
zip_files = glob.glob("*.zip") | |
for zip_file in zip_files: | |
print("Unzipping " + zip_file) | |
zip_ref = zipfile.ZipFile(zip_file, 'r') | |
for info in zip_ref.infolist(): | |
print(info.filename) | |
if info.filename.endswith("metadata.json") or info.filename.endswith("rungap.json") or info.filename.endswith("nike.json"): | |
zip_ref.extract(info) | |
zip_ref.close() | |
metadata_files = glob.glob("*metadata.json") | |
data_files = glob.glob("*rungap.json") | |
data_files += glob.glob("*nike.json") | |
print("Found metadata files") | |
print(metadata_files) | |
print("Found data files") | |
print(data_files) | |
if len(metadata_files) != len(data_files): | |
print("Error, number of metadata files does not match number of datafiles!") | |
exit(1) | |
for idx, metadata_file in enumerate(metadata_files): | |
print("=========") | |
print("Parsing " + metadata_file + " and " + data_files[idx]) | |
metadata_data = json.load(open(metadata_file)) | |
data = json.load(open(data_files[idx])) | |
root = ElementTree.Element("gpx") | |
root.set("xmlns","http://www.topografix.com/GPX/1/1") | |
root.set("creator", creator) | |
root.set("version", version) | |
metadata = ElementTree.SubElement(root, "metadata") | |
name = metadata_data["title"] | |
desc = metadata_data["description"] | |
print("Route name: " + name) | |
print("Route description: " + desc) | |
ElementTree.SubElement(metadata, "name").text = name | |
ElementTree.SubElement(metadata, "desc").text = desc | |
ElementTree.SubElement(metadata, "time").text = metadata_data["startTime"]["time"] | |
timezone = pytz.timezone(metadata_data["startTime"].get("timeZone", "UTC")) | |
source = metadata_data["source"] + " exported by Rungap for iOS, version " + metadata_data["appversion"] | |
track = ElementTree.SubElement(root, "trk") | |
ElementTree.SubElement(track, "name").text = name | |
ElementTree.SubElement(track, "desc").text = desc | |
ElementTree.SubElement(track, "src").text = source | |
segment = ElementTree.SubElement(track, "trkseg") | |
if "laps" in data: | |
print("Found " + str(len(data["laps"][0]["points"])) + " track points") | |
for point in data["laps"][0]["points"]: | |
if ("lat" in point and "lon" in point and "ele" in point and "time" in point): | |
trkpt = ElementTree.SubElement(segment, "trkpt", lat=str(point["lat"]), lon=str(point["lon"])) | |
ElementTree.SubElement(trkpt, "ele").text = str(point["ele"]) | |
ElementTree.SubElement(trkpt, "time").text = datetime.fromtimestamp(point["time"], timezone).isoformat() | |
elif "laps" in metadata_data: | |
print("Found " + str(len(metadata_data["laps"])) + " track points") | |
for point in metadata_data["laps"]: | |
p = point.get("startLocation", {}) | |
if ("lat" in p and "lon" in p and "startTime" in point): | |
#dt = datetime.datetime.strptime(d, "%Y-%m-%dT%H:%M:%SZ") | |
trkpt = ElementTree.SubElement(segment, "trkpt", lat=str(p["lat"]), lon=str(p["lon"])) | |
ElementTree.SubElement(trkpt, "time").text = point["startTime"] | |
gpx_filename = slugify(name + " - " + datetime.now().isoformat()) + ".gpx" | |
print("Writing " + gpx_filename) | |
tree = ElementTree.ElementTree(root) | |
tree.write(gpx_filename, "UTF-8", True) |
The other file is called "2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.nike.json"
Oh, try renaming that to "2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.rungap.json" (replace the "nike" with "rungap"). I did not anticipate the field being named differently than rung's usual file names!
Thanks. That works better!
Using your original script I get
Found metadata files
['2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.metadata.json']
Found data files
['2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.rungap.json']Parsing 2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.metadata.json and 2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.rungap.json
Route name: Friday Morning Run
Route description: Running 8.06 km in 47:37 min (5:55 min/km)
Traceback (most recent call last):
File "RungabGPX.py", line 63, in
timezone = pytz.timezone(metadata_data["startTime"]["timeZone"])
KeyError: 'timeZone'
I then tried the modification from @curlyjordi and got
Found metadata files
['2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.metadata.json']
Found data files
['2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.rungap.json']Parsing 2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.metadata.json and 2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.rungap.json
Route name: Friday Morning Run
Route description: Running 8.06 km in 47:37 min (5:55 min/km)
Traceback (most recent call last):
File "RungabGPX.py", line 71, in
print("Found " + str(len(data["laps"][0]["points"])) + " track points")
KeyError: 'laps'
Absolutely appreciate your help here!
John
Looks like rungap has changed their file format, give me a coupe minutes!
Looks like rungap has changed their file format, give me a coupe minutes!
Thank you! Would you like me to send you the files?
Looks like rungap has changed their file format, give me a coupe minutes!
Thank you! Would you like me to send you the files?
@jcbigears Yeah, that would help! The regular one still works, might be a difference with the nike files
@jcbigears Alright, I updated the gist. Turns out the .nike files have a different format
I tried.
I failed.
Sad face emoji.
I changed line 13: value = unicode(value)
and it worked perfectly!!!!!!
Thank you @huattedonmyarm!
Hi! It's very nice script. Thank you for that. I've noticed that it doesn't pass heart rate data to .gpx. I've tried to make some changes on my own but got stuck. Problem is that number of gps (lat|lon|ele) points does not match number of heart rate points. And I don't know how to properly map it to each other. Can you add it to your script or give me some ideas how to do that?
I'm a noob with this programming stuff.
The script first says it cannot find module pytz, if i remove that from the code:
When I run the script the newly created "__MACOSX" folder contains only blank folders with no content.
The script first says it cannot find module pytz, if i remove that from the code
you'll need to install the dependencies. Run pip install pytz
and try again with the original file
Hello
I have installed pytz but the newly created __MACOSX folder contains empty folders
Unzipped it manually then placed every file into one big folder, worked perfectly, thanks
Ah, I think I know what the issue was. Should work with the zip file now too
Hi.
I've tried both the original file as well as the modifications from curlyjordi. I have three files in a zip folder from my exported nike run via rungap. Two are JSON files and the other is a png of the map. I've unzipped the folder and put the script in the same folder. Running the script I get the error:
The other file is called "2019-12-20_05-17-13_nk_d0301bdd-5c63-4466-97f3-db5fa6bdfcce.nike.json"
Can anyone enlighten me please?
Thanks so much,
John