From abe1c46359d1c31bf669055a776c4a1fcb208c4d Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 29 Oct 2023 23:32:09 +0800 Subject: [PATCH 01/14] feat: elevation gain - strava/garmin(gpx/tcx/fit) --- run_page/generator/db.py | 4 ++++ run_page/gpxtrackposter/track.py | 15 +++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/run_page/generator/db.py b/run_page/generator/db.py index bd8ab04f46a..4f1ce9d6615 100644 --- a/run_page/generator/db.py +++ b/run_page/generator/db.py @@ -35,6 +35,7 @@ def randomword(): "summary_polyline", "average_heartrate", "average_speed", + "total_elevation_gain", ] @@ -53,6 +54,7 @@ class Activity(Base): summary_polyline = Column(String) average_heartrate = Column(Float) average_speed = Column(Float) + total_elevation_gain = Column(Float) streak = None def to_dict(self): @@ -106,6 +108,7 @@ def update_or_create_activity(session, run_activity): location_country=location_country, average_heartrate=run_activity.average_heartrate, average_speed=float(run_activity.average_speed), + total_elevation_gain=float(run_activity.total_elevation_gain), summary_polyline=( run_activity.map and run_activity.map.summary_polyline or "" ), @@ -120,6 +123,7 @@ def update_or_create_activity(session, run_activity): activity.type = run_activity.type activity.average_heartrate = run_activity.average_heartrate activity.average_speed = float(run_activity.average_speed) + activity.total_elevation_gain = float(run_activity.total_elevation_gain) activity.summary_polyline = ( run_activity.map and run_activity.map.summary_polyline or "" ) diff --git a/run_page/gpxtrackposter/track.py b/run_page/gpxtrackposter/track.py index 3eac70c52bd..c37f75a00df 100644 --- a/run_page/gpxtrackposter/track.py +++ b/run_page/gpxtrackposter/track.py @@ -45,6 +45,7 @@ def __init__(self): self.length = 0 self.special = False self.average_heartrate = None + self.total_elevation_gain = None self.moving_dict = {} self.run_id = 0 self.start_latlng = [] @@ -157,6 +158,7 @@ def _load_tcx_data(self, tcx, file_name): except: pass self.polyline_str = polyline.encode(polyline_container) + self.total_elevation_gain = tcx.ascent self.moving_dict = { "distance": self.length, "moving_time": datetime.timedelta(seconds=moving_time), @@ -220,6 +222,9 @@ def _load_gpx_data(self, gpx): sum(heart_rate_list) / len(heart_rate_list) if heart_rate_list else None ) self.moving_dict = self._get_moving_data(gpx) + self.total_elevation_gain = ( + gpx.get_uphill_downhill().uphill if gpx.has_elevations() else None + ) def _load_fit_data(self, fit: FitFile): _polylines = [] @@ -250,6 +255,9 @@ def _load_fit_data(self, fit: FitFile): self.average_heartrate = ( message.avg_heart_rate if message.avg_heart_rate != 0 else None ) + self.total_elevation_gain = ( + message.total_ascent if message.total_ascent != 0 else None + ) self.type = Sport(message.sport).name.lower() # moving_dict @@ -292,6 +300,10 @@ def append(self, other): ) self.file_names.extend(other.file_names) self.special = self.special or other.special + self.average_heartrate = self.average_heartrate or other.average_heartrate + self.total_elevation_gain = ( + self.total_elevation_gain if self.total_elevation_gain else 0 + ) + (other.total_elevation_gain if other.total_elevation_gain else 0) except: print( f"something wrong append this {self.end_time},in files {str(self.file_names)}" @@ -325,6 +337,9 @@ def to_namedtuple(self): "average_heartrate": int(self.average_heartrate) if self.average_heartrate else None, + "total_elevation_gain": int(self.total_elevation_gain) + if self.total_elevation_gain + else None, "map": run_map(self.polyline_str), "start_latlng": self.start_latlng, } From 81fc61446777364650b16e50331d7357952d2b2a Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 29 Oct 2023 23:34:54 +0800 Subject: [PATCH 02/14] feat: elevation gain - keep: elevation & gpx type --- run_page/keep_sync.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/run_page/keep_sync.py b/run_page/keep_sync.py index 80323c9b9e9..7f45f47fcc8 100755 --- a/run_page/keep_sync.py +++ b/run_page/keep_sync.py @@ -148,6 +148,7 @@ def parse_raw_data_to_nametuple( "end_local": datetime.strftime(end_local, "%Y-%m-%d %H:%M:%S"), "length": run_data["distance"], "average_heartrate": int(avg_heart_rate) if avg_heart_rate else None, + "total_elevation_gain": run_data["accumulativeUpliftedHeight"], "map": run_map(polyline_str), "start_latlng": start_latlng, "distance": run_data["distance"], @@ -211,7 +212,7 @@ def parse_points_to_gpx(run_points_data, start_time): (point["timestamp"] * 100 + start_time) / 1000 # note that the timestamp of a point is decisecond(分秒) ), - "elevation": point.get("verticalAccuracy"), + "elevation": point.get("altitude"), "hr": point.get("hr"), } points_dict_list.append(points_dict) @@ -219,6 +220,7 @@ def parse_points_to_gpx(run_points_data, start_time): gpx.nsmap["gpxtpx"] = "http://www.garmin.com/xmlschemas/TrackPointExtension/v1" gpx_track = gpxpy.gpx.GPXTrack() gpx_track.name = "gpx from keep" + gpx_track.type = "running" gpx.tracks.append(gpx_track) # Create first segment in our GPX track: From 12b4e5cc8dbf1b6a9ce7d13a642d5c179053475a Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 26 May 2024 14:39:20 +0800 Subject: [PATCH 03/14] feat: elevation gain - keep: elevation & gpx type --- run_page/codoon_sync.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/run_page/codoon_sync.py b/run_page/codoon_sync.py index c91a69e6169..51c278ccfaa 100755 --- a/run_page/codoon_sync.py +++ b/run_page/codoon_sync.py @@ -477,7 +477,7 @@ def parse_points_to_gpx(self, run_points_data): for p in points_dict_list: point = gpxpy.gpx.GPXTrackPoint(**p) gpx_segment.points.append(point) - return gpx.to_xml() + return gpx def get_single_run_record(self, route_id): print(f"Get single run for codoon id {route_id}") @@ -528,11 +528,18 @@ def parse_raw_data_to_namedtuple( p["latitude"] = latlng_data[i][0] p["longitude"] = latlng_data[i][1] - if with_gpx: - # pass the track no points - if str(log_id) not in old_gpx_ids and run_points_data: - gpx_data = self.parse_points_to_gpx(run_points_data) - download_codoon_gpx(gpx_data, str(log_id)) + total_elevation_gain = None + if run_points_data: + gpx_data = self.parse_points_to_gpx(run_points_data) + total_elevation_gain = ( + gpx_data.get_uphill_downhill().uphill + if gpx_data.has_elevations() + else None + ) + if with_gpx: + # pass the track no points + if str(log_id) not in old_gpx_ids: + download_codoon_gpx(gpx_data.to_xml(), str(log_id)) heart_rate_dict = run_data.get("heart_rate") heart_rate = None if heart_rate_dict: @@ -569,6 +576,7 @@ def parse_raw_data_to_namedtuple( seconds=int((end_date.timestamp() - start_date.timestamp())) ), "average_speed": run_data["total_length"] / run_data["total_time"], + "total_elevation_gain": total_elevation_gain, "location_country": location_country, "source": "Codoon", } From 5ac7f694f3001db885e469b17106ad81ce487326 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 26 May 2024 15:26:06 +0800 Subject: [PATCH 04/14] feat: elevation gain - ui --- src/components/RunTable/RunRow.tsx | 2 ++ src/components/RunTable/index.tsx | 3 +++ src/components/RunTable/style.module.css | 16 +++++++++++++--- src/components/YearStat/index.tsx | 4 ++++ src/utils/utils.ts | 1 + 5 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/components/RunTable/RunRow.tsx b/src/components/RunTable/RunRow.tsx index bcb554ebd58..6e5d93e8a67 100644 --- a/src/components/RunTable/RunRow.tsx +++ b/src/components/RunTable/RunRow.tsx @@ -11,6 +11,7 @@ interface IRunRowProperties { const RunRow = ({ elementIndex, locateActivity, run, runIndex, setRunIndex }: IRunRowProperties) => { const distance = (run.distance / 1000.0).toFixed(2); + const elevation_gain = run.total_elevation_gain?.toFixed(0); const paceParts = run.average_speed ? formatPace(run.average_speed) : null; const heartRate = run.average_heartrate; const runTime = formatRunTime(run.moving_time); @@ -32,6 +33,7 @@ const RunRow = ({ elementIndex, locateActivity, run, runIndex, setRunIndex }: IR > {titleForRun(run)} {distance} + {elevation_gain} {paceParts && {paceParts}} {heartRate && heartRate.toFixed(0)} {runTime} diff --git a/src/components/RunTable/index.tsx b/src/components/RunTable/index.tsx index 4c8bfa42abe..6c337f2186f 100644 --- a/src/components/RunTable/index.tsx +++ b/src/components/RunTable/index.tsx @@ -30,6 +30,8 @@ const RunTable = ({ // TODO refactor? const sortKMFunc: SortFunc = (a, b) => sortFuncInfo === 'KM' ? a.distance - b.distance : b.distance - a.distance; + const sortElevationGainFunc: SortFunc = (a, b) => + sortFuncInfo === 'Elevation Gain' ? a.total_elevation_gain - b.total_elevation_gain : b.total_elevation_gain - a.total_elevation_gain; const sortPaceFunc: SortFunc = (a, b) => sortFuncInfo === 'Pace' ? a.average_speed - b.average_speed @@ -50,6 +52,7 @@ const RunTable = ({ sortFuncInfo === 'Date' ? sortDateFunc : sortDateFuncReverse; const sortFuncMap = new Map([ ['KM', sortKMFunc], + ['Elevation Gain', sortElevationGainFunc], ['Pace', sortPaceFunc], ['BPM', sortBPMFunc], ['Time', sortRunTimeFunc], diff --git a/src/components/RunTable/style.module.css b/src/components/RunTable/style.module.css index d5ac56f4838..d4e4ca40797 100644 --- a/src/components/RunTable/style.module.css +++ b/src/components/RunTable/style.module.css @@ -1,10 +1,20 @@ +@media only screen and (max-width: 800px) { + + /* 当屏幕宽度小于 800px 时 */ + .runTable th:nth-child(3), + .runTable td:nth-child(3) { + display: none; + /* 隐藏第3列 */ + } +} + @media only screen and (max-width: 700px) { /* 当屏幕宽度小于 700px 时 */ - .runTable th:nth-child(4), - .runTable td:nth-child(4) { + .runTable th:nth-child(5), + .runTable td:nth-child(5) { display: none; - /* 隐藏第四列 */ + /* 隐藏第5列 */ } } diff --git a/src/components/YearStat/index.tsx b/src/components/YearStat/index.tsx index f7294d35856..78998b81889 100644 --- a/src/components/YearStat/index.tsx +++ b/src/components/YearStat/index.tsx @@ -18,6 +18,7 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) => } let sumDistance = 0; let streak = 0; + let sumElevationGain = 0; let pace = 0; // eslint-disable-line no-unused-vars let paceNullCount = 0; // eslint-disable-line no-unused-vars let heartRate = 0; @@ -26,6 +27,7 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) => let totalSecondsAvail = 0; runs.forEach((run) => { sumDistance += run.distance || 0; + sumElevationGain += run.total_elevation_gain || 0; if (run.average_speed) { pace += run.average_speed; totalMetersAvail += run.distance || 0; @@ -43,6 +45,7 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) => } }); sumDistance = parseFloat((sumDistance / 1000.0).toFixed(1)); + sumElevationGain = (sumElevationGain).toFixed(0); const avgPace = formatPace(totalMetersAvail / totalSecondsAvail); const hasHeartRate = !(heartRate === 0); const avgHeartRate = (heartRate / (runs.length - heartRateNullCount)).toFixed( @@ -58,6 +61,7 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) => + {hasHeartRate && ( diff --git a/src/utils/utils.ts b/src/utils/utils.ts index ce6e7f03adc..d12e561e042 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -22,6 +22,7 @@ export interface Activity { location_country?: string | null; summary_polyline?: string | null; average_heartrate?: number | null; + total_elevation_gain?: number | null; average_speed: number; streak: number; } From 834db8dda1a227c20e0209722d9497f2c8788ba5 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 26 May 2024 22:30:17 +0800 Subject: [PATCH 05/14] feat: joyrun gpx: elevation and heart_rate --- run_page/joyrun_sync.py | 68 ++++++++++++++++++++++++++++++----------- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/run_page/joyrun_sync.py b/run_page/joyrun_sync.py index 6441f198f62..49a6b01e6be 100755 --- a/run_page/joyrun_sync.py +++ b/run_page/joyrun_sync.py @@ -7,6 +7,7 @@ from datetime import datetime, timedelta from hashlib import md5 from urllib.parse import quote +from xml.etree import ElementTree import gpxpy import polyline @@ -187,13 +188,21 @@ def parse_content_to_ponits(content): @staticmethod def parse_points_to_gpx( - run_points_data, start_time, end_time, pause_list, interval=5 + run_points_data, + start_time, + end_time, + heart_rate_list=None, + altitude_list=None, + pause_list=[], + interval=5, ): """ parse run_data content to gpx object TODO for now kind of same as `keep` maybe refactor later :param run_points_data: [[latitude, longitude],...] + :param heart_rate_list: [heart_rate, ...] + :param altitude_list: [altitude, ...] :param pause_list: [[interval_index, pause_seconds],...] :param interval: time interval between each point, in seconds """ @@ -209,6 +218,8 @@ def parse_points_to_gpx( "longitude": point[1], "time": datetime.utcfromtimestamp(current_time), } + if altitude_list and len(altitude_list) > index: + points_dict["elevation"] = altitude_list[index] points_dict_list.append(points_dict) current_time += interval @@ -218,13 +229,14 @@ def parse_points_to_gpx( current_time += int(pause_list[0][1]) pause_list.pop(0) - points_dict_list.append( - { - "latitude": run_points_data[-1][0], - "longitude": run_points_data[-1][1], - "time": datetime.utcfromtimestamp(end_time), - } - ) + last = { + "latitude": run_points_data[-1][0], + "longitude": run_points_data[-1][1], + "time": datetime.utcfromtimestamp(end_time), + } + if altitude_list and len(altitude_list) > len(run_points_data): + last["elevation"] = altitude_list[len(run_points_data) - 1] + points_dict_list.append(last) segment_list.append(points_dict_list) # gpx part @@ -235,14 +247,23 @@ def parse_points_to_gpx( gpx.tracks.append(gpx_track) # add segment list to our GPX track: + i = 0 for point_list in segment_list: gpx_segment = gpxpy.gpx.GPXTrackSegment() gpx_track.segments.append(gpx_segment) for p in point_list: point = gpxpy.gpx.GPXTrackPoint(**p) + if heart_rate_list and len(heart_rate_list) > i: + gpx_extension_hr = ElementTree.fromstring( + f""" + {heart_rate_list[i]} + + """) + i += 1 + point.extensions.append(gpx_extension_hr) gpx_segment.points.append(point) - return gpx.to_xml() + return gpx def get_single_run_record(self, fid): payload = { @@ -265,25 +286,38 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): end_time = run_data["endtime"] pause_list = run_data["pause"] run_points_data = self.parse_content_to_ponits(run_data["content"]) - if with_gpx: - # pass the track no points - if run_points_data: - gpx_data = self.parse_points_to_gpx( - run_points_data, start_time, end_time, pause_list - ) - download_joyrun_gpx(gpx_data, str(joyrun_id)) + altitude_list = run_data["altitude"] + try: heart_rate_list = ( eval(run_data["heartrate"]) if run_data["heartrate"] else None ) except: - print(f"Heart Rate: can not eval for {str(heart_rate_list)}") + print(f"Heart Rate: can not eval for {str(run_data['heartrate'''])}") + try: + altitude_list = eval(altitude_list) if altitude_list else None + except: + print(f"Altitude: can not eval for {str(altitude_list)}") heart_rate = None if heart_rate_list: heart_rate = int(sum(heart_rate_list) / len(heart_rate_list)) # fix #66 if heart_rate < 0: heart_rate = None + # pass the track no points + if run_points_data: + gpx_data = self.parse_points_to_gpx( + run_points_data, + start_time, + end_time, + heart_rate_list, + altitude_list, + pause_list, + ) + if with_gpx: + # pass the track no points + if str(joyrun_id) not in old_gpx_ids: + download_joyrun_gpx(gpx_data.to_xml(), str(joyrun_id)) polyline_str = polyline.encode(run_points_data) if run_points_data else "" start_latlng = start_point(*run_points_data[0]) if run_points_data else None From 67aab8306a940766e1579039756d31e51bfe2519 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Sun, 26 May 2024 22:31:38 +0800 Subject: [PATCH 06/14] feat: elevation gain - joyrun --- run_page/endomondo_sync.py | 1 + run_page/gpxtrackposter/track.py | 2 +- run_page/joyrun_sync.py | 10 +++++++++- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/run_page/endomondo_sync.py b/run_page/endomondo_sync.py index bdecf438d68..00f4bed2358 100644 --- a/run_page/endomondo_sync.py +++ b/run_page/endomondo_sync.py @@ -68,6 +68,7 @@ def parse_run_endomondo_to_nametuple(en_dict): "average_speed": en_dict.get("distance_km", 0) / en_dict.get("duration_s", 1) * 1000, + "total_elevation_gain": None, "location_country": "", } return namedtuple("x", d.keys())(*d.values()) diff --git a/run_page/gpxtrackposter/track.py b/run_page/gpxtrackposter/track.py index 88daacd1e8d..bdeebd6f66b 100644 --- a/run_page/gpxtrackposter/track.py +++ b/run_page/gpxtrackposter/track.py @@ -345,7 +345,7 @@ def to_namedtuple(self): int(self.average_heartrate) if self.average_heartrate else None ), "total_elevation_gain": ( - int(self.total_elevation_gain) if self.total_elevation_gain else None + int(self.total_elevation_gain) if self.total_elevation_gain else None ), "map": run_map(self.polyline_str), "start_latlng": self.start_latlng, diff --git a/run_page/joyrun_sync.py b/run_page/joyrun_sync.py index 49a6b01e6be..a2e4e894196 100755 --- a/run_page/joyrun_sync.py +++ b/run_page/joyrun_sync.py @@ -258,7 +258,8 @@ def parse_points_to_gpx( f""" {heart_rate_list[i]} - """) + """ + ) i += 1 point.extensions.append(gpx_extension_hr) gpx_segment.points.append(point) @@ -304,6 +305,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): # fix #66 if heart_rate < 0: heart_rate = None + total_elevation_gain = None # pass the track no points if run_points_data: gpx_data = self.parse_points_to_gpx( @@ -314,6 +316,11 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): altitude_list, pause_list, ) + total_elevation_gain = ( + gpx_data.get_uphill_downhill().uphill + if gpx_data.has_elevations() + else None + ) if with_gpx: # pass the track no points if str(joyrun_id) not in old_gpx_ids: @@ -351,6 +358,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): seconds=int((run_data["endtime"] - run_data["starttime"])) ), "average_speed": run_data["meter"] / run_data["second"], + "total_elevation_gain": total_elevation_gain, "location_country": location_country, } return namedtuple("x", d.keys())(*d.values()) From 5b576595caa39df6aac8d43ff79a25ac15a0b15d Mon Sep 17 00:00:00 2001 From: ben_29 Date: Wed, 29 May 2024 17:18:25 +0800 Subject: [PATCH 07/14] feat: elevation gain - keep --- run_page/keep_sync.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/run_page/keep_sync.py b/run_page/keep_sync.py index c4c85ced5b8..b53c362199f 100755 --- a/run_page/keep_sync.py +++ b/run_page/keep_sync.py @@ -108,6 +108,7 @@ def parse_raw_data_to_nametuple( start_time = run_data["startTime"] avg_heart_rate = None + total_elevation_gain = None decoded_hr_data = [] if run_data["heartRate"]: avg_heart_rate = run_data["heartRate"].get("averageHeartRate", None) @@ -134,14 +135,17 @@ def parse_raw_data_to_nametuple( p_hr = find_nearest_hr(decoded_hr_data, int(p["timestamp"]), start_time) if p_hr: p["hr"] = p_hr - if with_download_gpx: - if str(keep_id) not in old_gpx_ids and run_data["dataType"].startswith( - "outdoor" - ): - gpx_data = parse_points_to_gpx( - run_points_data_gpx, start_time, KEEP2STRAVA[run_data["dataType"]] - ) - download_keep_gpx(gpx_data, str(keep_id)) + if run_data["dataType"].startswith("outdoor"): + gpx_data = parse_points_to_gpx( + run_points_data_gpx, start_time, KEEP2STRAVA[run_data["dataType"]] + ) + total_elevation_gain = ( + gpx_data.get_uphill_downhill().uphill + if gpx_data.has_elevations() + else None + ) + if with_download_gpx and str(keep_id) not in old_gpx_ids: + download_keep_gpx(gpx_data.to_xml(), str(keep_id)) else: print(f"ID {keep_id} no gps data") polyline_str = polyline.encode(run_points_data) if run_points_data else "" @@ -174,6 +178,7 @@ def parse_raw_data_to_nametuple( seconds=int((run_data["endTime"] - run_data["startTime"]) / 1000) ), "average_speed": run_data["distance"] / run_data["duration"], + "total_elevation_gain": total_elevation_gain, "location_country": str(run_data.get("region", "")), } return namedtuple("x", d.keys())(*d.values()) @@ -262,7 +267,7 @@ def parse_points_to_gpx(run_points_data, start_time, sport_type): ) point.extensions.append(gpx_extension_hr) gpx_segment.points.append(point) - return gpx.to_xml() + return gpx def find_nearest_hr( From 77660b04caae4c8cbeb93b302d6e0d4dfd0931ca Mon Sep 17 00:00:00 2001 From: ben_29 Date: Wed, 29 May 2024 17:43:40 +0800 Subject: [PATCH 08/14] feat: elevation gain - gpx --- run_page/codoon_sync.py | 6 +----- run_page/gpxtrackposter/track.py | 6 ++---- run_page/joyrun_sync.py | 6 +----- run_page/keep_sync.py | 6 +----- 4 files changed, 5 insertions(+), 19 deletions(-) diff --git a/run_page/codoon_sync.py b/run_page/codoon_sync.py index 51c278ccfaa..e3719eaf376 100755 --- a/run_page/codoon_sync.py +++ b/run_page/codoon_sync.py @@ -531,11 +531,7 @@ def parse_raw_data_to_namedtuple( total_elevation_gain = None if run_points_data: gpx_data = self.parse_points_to_gpx(run_points_data) - total_elevation_gain = ( - gpx_data.get_uphill_downhill().uphill - if gpx_data.has_elevations() - else None - ) + total_elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx: # pass the track no points if str(log_id) not in old_gpx_ids: diff --git a/run_page/gpxtrackposter/track.py b/run_page/gpxtrackposter/track.py index bdeebd6f66b..8a4ec4e8762 100644 --- a/run_page/gpxtrackposter/track.py +++ b/run_page/gpxtrackposter/track.py @@ -229,9 +229,7 @@ def _load_gpx_data(self, gpx): sum(heart_rate_list) / len(heart_rate_list) if heart_rate_list else None ) self.moving_dict = self._get_moving_data(gpx) - self.total_elevation_gain = ( - gpx.get_uphill_downhill().uphill if gpx.has_elevations() else None - ) + self.total_elevation_gain = gpx.get_uphill_downhill().uphill def _load_fit_data(self, fit: dict): _polylines = [] @@ -345,7 +343,7 @@ def to_namedtuple(self): int(self.average_heartrate) if self.average_heartrate else None ), "total_elevation_gain": ( - int(self.total_elevation_gain) if self.total_elevation_gain else None + int(self.total_elevation_gain) if self.total_elevation_gain else 0 ), "map": run_map(self.polyline_str), "start_latlng": self.start_latlng, diff --git a/run_page/joyrun_sync.py b/run_page/joyrun_sync.py index a2e4e894196..e6f1a98f3c0 100755 --- a/run_page/joyrun_sync.py +++ b/run_page/joyrun_sync.py @@ -316,11 +316,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): altitude_list, pause_list, ) - total_elevation_gain = ( - gpx_data.get_uphill_downhill().uphill - if gpx_data.has_elevations() - else None - ) + total_elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx: # pass the track no points if str(joyrun_id) not in old_gpx_ids: diff --git a/run_page/keep_sync.py b/run_page/keep_sync.py index b53c362199f..9b3c1d7d732 100755 --- a/run_page/keep_sync.py +++ b/run_page/keep_sync.py @@ -139,11 +139,7 @@ def parse_raw_data_to_nametuple( gpx_data = parse_points_to_gpx( run_points_data_gpx, start_time, KEEP2STRAVA[run_data["dataType"]] ) - total_elevation_gain = ( - gpx_data.get_uphill_downhill().uphill - if gpx_data.has_elevations() - else None - ) + total_elevation_gain = gpx_data.get_uphill_downhill().uphill if with_download_gpx and str(keep_id) not in old_gpx_ids: download_keep_gpx(gpx_data.to_xml(), str(keep_id)) else: From 3ea88fddd9f1fdb6b1878517df495362ec5e815b Mon Sep 17 00:00:00 2001 From: ben_29 Date: Wed, 29 May 2024 17:48:09 +0800 Subject: [PATCH 09/14] feat: elevation gain - oppo --- run_page/oppo_sync.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/run_page/oppo_sync.py b/run_page/oppo_sync.py index cf5bf803f6b..e72a58d28cb 100644 --- a/run_page/oppo_sync.py +++ b/run_page/oppo_sync.py @@ -189,6 +189,7 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): start_time = sport_data["startTime"] other_data = sport_data["otherSportData"] avg_heart_rate = None + total_elevation_gain = None if other_data: avg_heart_rate = other_data.get("avgHeartRate", None) # fix #66 @@ -206,9 +207,10 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): point_dict = prepare_track_points(sport_data, with_gpx) + gpx_data = parse_points_to_gpx(sport_data, point_dict) + total_elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx is True: - gpx_data = parse_points_to_gpx(sport_data, point_dict) - download_keep_gpx(gpx_data, str(oppo_id)) + download_keep_gpx(gpx_data.to_xml(), str(oppo_id)) if with_tcx is True: parse_points_to_tcx(sport_data, point_dict) @@ -247,6 +249,7 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): seconds=int((sport_data["endTime"] - sport_data["startTime"]) / 1000) ), "average_speed": other_data["totalDistance"] / other_data["totalTime"] * 1000, + "total_elevation_gain": total_elevation_gain, "location_country": location_country, "source": sport_data["deviceName"], } @@ -372,7 +375,7 @@ def parse_points_to_gpx(sport_data, points_dict_list): ) point.extensions.append(gpx_extension) gpx_segment.points.append(point) - return gpx.to_xml() + return gpx def download_keep_gpx(gpx_data, keep_id): From b809e61515bb54256cb6a0ec4ac630bb37b63632 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Wed, 29 May 2024 18:04:51 +0800 Subject: [PATCH 10/14] feat: elevation gain - tulipsport --- run_page/tulipsport_sync.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/run_page/tulipsport_sync.py b/run_page/tulipsport_sync.py index a99c12c91ad..a2904eb58dc 100755 --- a/run_page/tulipsport_sync.py +++ b/run_page/tulipsport_sync.py @@ -98,6 +98,7 @@ def merge_summary_and_detail_to_nametuple(summary, detail): # end_date = datetime.strftime(summary["end_date"], "%Y-%m-%d %H:%M:%S") # end_date_local = datetime.strftime(summary["end_date_local"], "%Y-%m-%d %H:%M:%S") average_heartrate = int(detail["avg_hr"]) + total_elevation_gain = None map = run_map("") start_latlng = None distance = summary["distance"] @@ -121,6 +122,9 @@ def merge_summary_and_detail_to_nametuple(summary, detail): latlng_list = [[float(point[0]), float(point[1])] for point in point_list] map = run_map(polyline.encode(latlng_list)) + altitude_list = [point[2] for point in detail["map_data_list"]] + total_elevation_gain = compute_elevation_gain(altitude_list) + activity_db_instance = { "id": id, "name": name, @@ -134,6 +138,7 @@ def merge_summary_and_detail_to_nametuple(summary, detail): "moving_time": moving_time, "elapsed_time": elapsed_time, "average_speed": average_speed, + "total_elevation_gain": total_elevation_gain, "location_country": location_country, } return namedtuple("activity_db_instance", activity_db_instance.keys())( @@ -141,6 +146,14 @@ def merge_summary_and_detail_to_nametuple(summary, detail): ) +def compute_elevation_gain(altitudes): + total_gain = 0 + for i in range(1, len(altitudes)): + if altitudes[i] > altitudes[i - 1]: + total_gain += altitudes[i] - altitudes[i - 1] + return total_gain + + def find_last_tulipsport_start_time(track_ids): start_time = None tulipsport_ids = [ From 673861e89eb58aee3f907a2179daebbbc4742d6d Mon Sep 17 00:00:00 2001 From: ben_29 Date: Wed, 29 May 2024 18:30:14 +0800 Subject: [PATCH 11/14] feat: elevation gain - field name --- run_page/codoon_sync.py | 6 +++--- run_page/endomondo_sync.py | 2 +- run_page/generator/db.py | 8 ++++---- run_page/gpxtrackposter/track.py | 18 +++++++++--------- run_page/joyrun_sync.py | 6 +++--- run_page/keep_sync.py | 8 ++++---- run_page/nike_sync.py | 1 + run_page/oppo_sync.py | 6 +++--- run_page/tulipsport_sync.py | 6 +++--- src/components/RunTable/RunRow.tsx | 2 +- src/components/RunTable/index.tsx | 2 +- src/components/YearStat/index.tsx | 2 +- src/utils/utils.ts | 2 +- 13 files changed, 35 insertions(+), 34 deletions(-) diff --git a/run_page/codoon_sync.py b/run_page/codoon_sync.py index e3719eaf376..584225f8879 100755 --- a/run_page/codoon_sync.py +++ b/run_page/codoon_sync.py @@ -528,10 +528,10 @@ def parse_raw_data_to_namedtuple( p["latitude"] = latlng_data[i][0] p["longitude"] = latlng_data[i][1] - total_elevation_gain = None + elevation_gain = None if run_points_data: gpx_data = self.parse_points_to_gpx(run_points_data) - total_elevation_gain = gpx_data.get_uphill_downhill().uphill + elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx: # pass the track no points if str(log_id) not in old_gpx_ids: @@ -572,7 +572,7 @@ def parse_raw_data_to_namedtuple( seconds=int((end_date.timestamp() - start_date.timestamp())) ), "average_speed": run_data["total_length"] / run_data["total_time"], - "total_elevation_gain": total_elevation_gain, + "elevation_gain": elevation_gain, "location_country": location_country, "source": "Codoon", } diff --git a/run_page/endomondo_sync.py b/run_page/endomondo_sync.py index 00f4bed2358..c10f3ba02df 100644 --- a/run_page/endomondo_sync.py +++ b/run_page/endomondo_sync.py @@ -68,7 +68,7 @@ def parse_run_endomondo_to_nametuple(en_dict): "average_speed": en_dict.get("distance_km", 0) / en_dict.get("duration_s", 1) * 1000, - "total_elevation_gain": None, + "elevation_gain": None, "location_country": "", } return namedtuple("x", d.keys())(*d.values()) diff --git a/run_page/generator/db.py b/run_page/generator/db.py index 7ee681d7491..3b7c87c0005 100644 --- a/run_page/generator/db.py +++ b/run_page/generator/db.py @@ -35,7 +35,7 @@ def randomword(): "summary_polyline", "average_heartrate", "average_speed", - "total_elevation_gain", + "elevation_gain", ] @@ -54,7 +54,7 @@ class Activity(Base): summary_polyline = Column(String) average_heartrate = Column(Float) average_speed = Column(Float) - total_elevation_gain = Column(Float) + elevation_gain = Column(Float) streak = None def to_dict(self): @@ -113,7 +113,7 @@ def update_or_create_activity(session, run_activity): location_country=location_country, average_heartrate=run_activity.average_heartrate, average_speed=float(run_activity.average_speed), - total_elevation_gain=float(run_activity.total_elevation_gain), + elevation_gain=float(run_activity.elevation_gain), summary_polyline=( run_activity.map and run_activity.map.summary_polyline or "" ), @@ -128,7 +128,7 @@ def update_or_create_activity(session, run_activity): activity.type = run_activity.type activity.average_heartrate = run_activity.average_heartrate activity.average_speed = float(run_activity.average_speed) - activity.total_elevation_gain = float(run_activity.total_elevation_gain) + activity.elevation_gain = float(run_activity.elevation_gain) activity.summary_polyline = ( run_activity.map and run_activity.map.summary_polyline or "" ) diff --git a/run_page/gpxtrackposter/track.py b/run_page/gpxtrackposter/track.py index 8a4ec4e8762..c4a01cf6631 100644 --- a/run_page/gpxtrackposter/track.py +++ b/run_page/gpxtrackposter/track.py @@ -47,7 +47,7 @@ def __init__(self): self.length = 0 self.special = False self.average_heartrate = None - self.total_elevation_gain = None + self.elevation_gain = None self.moving_dict = {} self.run_id = 0 self.start_latlng = [] @@ -165,7 +165,7 @@ def _load_tcx_data(self, tcx, file_name): except: pass self.polyline_str = polyline.encode(polyline_container) - self.total_elevation_gain = tcx.ascent + self.elevation_gain = tcx.ascent self.moving_dict = { "distance": self.length, "moving_time": datetime.timedelta(seconds=moving_time), @@ -229,7 +229,7 @@ def _load_gpx_data(self, gpx): sum(heart_rate_list) / len(heart_rate_list) if heart_rate_list else None ) self.moving_dict = self._get_moving_data(gpx) - self.total_elevation_gain = gpx.get_uphill_downhill().uphill + self.elevation_gain = gpx.get_uphill_downhill().uphill def _load_fit_data(self, fit: dict): _polylines = [] @@ -246,7 +246,7 @@ def _load_fit_data(self, fit: dict): self.average_heartrate = ( message["avg_heart_rate"] if "avg_heart_rate" in message else None ) - self.total_elevation_gain = ( + self.elevation_gain = ( message["total_ascent"] if "total_ascent" in message else None ) self.type = message["sport"].lower() @@ -304,9 +304,9 @@ def append(self, other): self.file_names.extend(other.file_names) self.special = self.special or other.special self.average_heartrate = self.average_heartrate or other.average_heartrate - self.total_elevation_gain = ( - self.total_elevation_gain if self.total_elevation_gain else 0 - ) + (other.total_elevation_gain if other.total_elevation_gain else 0) + self.elevation_gain = ( + self.elevation_gain if self.elevation_gain else 0 + ) + (other.elevation_gain if other.elevation_gain else 0) except: print( f"something wrong append this {self.end_time},in files {str(self.file_names)}" @@ -342,8 +342,8 @@ def to_namedtuple(self): "average_heartrate": ( int(self.average_heartrate) if self.average_heartrate else None ), - "total_elevation_gain": ( - int(self.total_elevation_gain) if self.total_elevation_gain else 0 + "elevation_gain": ( + int(self.elevation_gain) if self.elevation_gain else 0 ), "map": run_map(self.polyline_str), "start_latlng": self.start_latlng, diff --git a/run_page/joyrun_sync.py b/run_page/joyrun_sync.py index e6f1a98f3c0..4ee5c3cecf4 100755 --- a/run_page/joyrun_sync.py +++ b/run_page/joyrun_sync.py @@ -305,7 +305,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): # fix #66 if heart_rate < 0: heart_rate = None - total_elevation_gain = None + elevation_gain = None # pass the track no points if run_points_data: gpx_data = self.parse_points_to_gpx( @@ -316,7 +316,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): altitude_list, pause_list, ) - total_elevation_gain = gpx_data.get_uphill_downhill().uphill + elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx: # pass the track no points if str(joyrun_id) not in old_gpx_ids: @@ -354,7 +354,7 @@ def parse_raw_data_to_nametuple(self, run_data, old_gpx_ids, with_gpx=False): seconds=int((run_data["endtime"] - run_data["starttime"])) ), "average_speed": run_data["meter"] / run_data["second"], - "total_elevation_gain": total_elevation_gain, + "elevation_gain": elevation_gain, "location_country": location_country, } return namedtuple("x", d.keys())(*d.values()) diff --git a/run_page/keep_sync.py b/run_page/keep_sync.py index 9b3c1d7d732..1edff5a050b 100755 --- a/run_page/keep_sync.py +++ b/run_page/keep_sync.py @@ -108,7 +108,7 @@ def parse_raw_data_to_nametuple( start_time = run_data["startTime"] avg_heart_rate = None - total_elevation_gain = None + elevation_gain = None decoded_hr_data = [] if run_data["heartRate"]: avg_heart_rate = run_data["heartRate"].get("averageHeartRate", None) @@ -139,7 +139,7 @@ def parse_raw_data_to_nametuple( gpx_data = parse_points_to_gpx( run_points_data_gpx, start_time, KEEP2STRAVA[run_data["dataType"]] ) - total_elevation_gain = gpx_data.get_uphill_downhill().uphill + elevation_gain = gpx_data.get_uphill_downhill().uphill if with_download_gpx and str(keep_id) not in old_gpx_ids: download_keep_gpx(gpx_data.to_xml(), str(keep_id)) else: @@ -165,7 +165,7 @@ def parse_raw_data_to_nametuple( "end_local": datetime.strftime(end_local, "%Y-%m-%d %H:%M:%S"), "length": run_data["distance"], "average_heartrate": int(avg_heart_rate) if avg_heart_rate else None, - "total_elevation_gain": run_data["accumulativeUpliftedHeight"], + "elevation_gain": run_data["accumulativeUpliftedHeight"], "map": run_map(polyline_str), "start_latlng": start_latlng, "distance": run_data["distance"], @@ -174,7 +174,7 @@ def parse_raw_data_to_nametuple( seconds=int((run_data["endTime"] - run_data["startTime"]) / 1000) ), "average_speed": run_data["distance"] / run_data["duration"], - "total_elevation_gain": total_elevation_gain, + "elevation_gain": elevation_gain, "location_country": str(run_data.get("region", "")), } return namedtuple("x", d.keys())(*d.values()) diff --git a/run_page/nike_sync.py b/run_page/nike_sync.py index 04c72f55e3d..11356a909a8 100644 --- a/run_page/nike_sync.py +++ b/run_page/nike_sync.py @@ -372,6 +372,7 @@ def parse_no_gpx_data(activity): "moving_time": moving_time, "elapsed_time": elapsed_time, "average_speed": distance / int(activity["active_duration_ms"] / 1000), + "elevation_gain": 0, "location_country": "", } return namedtuple("x", d.keys())(*d.values()) diff --git a/run_page/oppo_sync.py b/run_page/oppo_sync.py index e72a58d28cb..1af9169eabf 100644 --- a/run_page/oppo_sync.py +++ b/run_page/oppo_sync.py @@ -189,7 +189,7 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): start_time = sport_data["startTime"] other_data = sport_data["otherSportData"] avg_heart_rate = None - total_elevation_gain = None + elevation_gain = None if other_data: avg_heart_rate = other_data.get("avgHeartRate", None) # fix #66 @@ -208,7 +208,7 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): point_dict = prepare_track_points(sport_data, with_gpx) gpx_data = parse_points_to_gpx(sport_data, point_dict) - total_elevation_gain = gpx_data.get_uphill_downhill().uphill + elevation_gain = gpx_data.get_uphill_downhill().uphill if with_gpx is True: download_keep_gpx(gpx_data.to_xml(), str(oppo_id)) if with_tcx is True: @@ -249,7 +249,7 @@ def parse_raw_data_to_name_tuple(sport_data, with_gpx, with_tcx): seconds=int((sport_data["endTime"] - sport_data["startTime"]) / 1000) ), "average_speed": other_data["totalDistance"] / other_data["totalTime"] * 1000, - "total_elevation_gain": total_elevation_gain, + "elevation_gain": elevation_gain, "location_country": location_country, "source": sport_data["deviceName"], } diff --git a/run_page/tulipsport_sync.py b/run_page/tulipsport_sync.py index a2904eb58dc..6e1966765eb 100755 --- a/run_page/tulipsport_sync.py +++ b/run_page/tulipsport_sync.py @@ -98,7 +98,7 @@ def merge_summary_and_detail_to_nametuple(summary, detail): # end_date = datetime.strftime(summary["end_date"], "%Y-%m-%d %H:%M:%S") # end_date_local = datetime.strftime(summary["end_date_local"], "%Y-%m-%d %H:%M:%S") average_heartrate = int(detail["avg_hr"]) - total_elevation_gain = None + elevation_gain = None map = run_map("") start_latlng = None distance = summary["distance"] @@ -123,7 +123,7 @@ def merge_summary_and_detail_to_nametuple(summary, detail): map = run_map(polyline.encode(latlng_list)) altitude_list = [point[2] for point in detail["map_data_list"]] - total_elevation_gain = compute_elevation_gain(altitude_list) + elevation_gain = compute_elevation_gain(altitude_list) activity_db_instance = { "id": id, @@ -138,7 +138,7 @@ def merge_summary_and_detail_to_nametuple(summary, detail): "moving_time": moving_time, "elapsed_time": elapsed_time, "average_speed": average_speed, - "total_elevation_gain": total_elevation_gain, + "elevation_gain": elevation_gain, "location_country": location_country, } return namedtuple("activity_db_instance", activity_db_instance.keys())( diff --git a/src/components/RunTable/RunRow.tsx b/src/components/RunTable/RunRow.tsx index 6e5d93e8a67..77d69fef9ac 100644 --- a/src/components/RunTable/RunRow.tsx +++ b/src/components/RunTable/RunRow.tsx @@ -11,7 +11,7 @@ interface IRunRowProperties { const RunRow = ({ elementIndex, locateActivity, run, runIndex, setRunIndex }: IRunRowProperties) => { const distance = (run.distance / 1000.0).toFixed(2); - const elevation_gain = run.total_elevation_gain?.toFixed(0); + const elevation_gain = run.elevation_gain.toFixed(0); const paceParts = run.average_speed ? formatPace(run.average_speed) : null; const heartRate = run.average_heartrate; const runTime = formatRunTime(run.moving_time); diff --git a/src/components/RunTable/index.tsx b/src/components/RunTable/index.tsx index 6c337f2186f..1929c921a34 100644 --- a/src/components/RunTable/index.tsx +++ b/src/components/RunTable/index.tsx @@ -31,7 +31,7 @@ const RunTable = ({ const sortKMFunc: SortFunc = (a, b) => sortFuncInfo === 'KM' ? a.distance - b.distance : b.distance - a.distance; const sortElevationGainFunc: SortFunc = (a, b) => - sortFuncInfo === 'Elevation Gain' ? a.total_elevation_gain - b.total_elevation_gain : b.total_elevation_gain - a.total_elevation_gain; + sortFuncInfo === 'Elevation Gain' ? a.elevation_gain - b.elevation_gain : b.elevation_gain - a.elevation_gain; const sortPaceFunc: SortFunc = (a, b) => sortFuncInfo === 'Pace' ? a.average_speed - b.average_speed diff --git a/src/components/YearStat/index.tsx b/src/components/YearStat/index.tsx index 78998b81889..cc9fa638f79 100644 --- a/src/components/YearStat/index.tsx +++ b/src/components/YearStat/index.tsx @@ -27,7 +27,7 @@ const YearStat = ({ year, onClick }: { year: string, onClick: (_year: string) => let totalSecondsAvail = 0; runs.forEach((run) => { sumDistance += run.distance || 0; - sumElevationGain += run.total_elevation_gain || 0; + sumElevationGain += run.elevation_gain || 0; if (run.average_speed) { pace += run.average_speed; totalMetersAvail += run.distance || 0; diff --git a/src/utils/utils.ts b/src/utils/utils.ts index d12e561e042..3237189643a 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -22,7 +22,7 @@ export interface Activity { location_country?: string | null; summary_polyline?: string | null; average_heartrate?: number | null; - total_elevation_gain?: number | null; + elevation_gain: number; average_speed: number; streak: number; } From 6cd112d83234159e979f86b3e1cc1667f5c88211 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Thu, 30 May 2024 18:58:46 +0800 Subject: [PATCH 12/14] feat: elevation gain - db updator --- run_page/db_updater.py | 20 ++++++++++++++++++++ run_page/gpxtrackposter/track.py | 4 +--- 2 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 run_page/db_updater.py diff --git a/run_page/db_updater.py b/run_page/db_updater.py new file mode 100644 index 00000000000..570ed5aea7d --- /dev/null +++ b/run_page/db_updater.py @@ -0,0 +1,20 @@ +from generator.db import init_db, Activity +from config import SQL_FILE +import sqlalchemy + + +def add_column_elevation_gain(session): + # check if column elevation_gain is already added + # if not add it to the db + try: + session.query(Activity).first() + print("column elevation_gain already added, skipping") + except sqlalchemy.exc.OperationalError: + sql_statement = 'alter TABLE "activities" add column elevation_gain Float after average_heartrate' + session.execute(statement=sql_statement) + print("column elevation_gain added successfully") + + +if __name__ == "__main__": + session = init_db(SQL_FILE) + add_column_elevation_gain(session) diff --git a/run_page/gpxtrackposter/track.py b/run_page/gpxtrackposter/track.py index c4a01cf6631..bbbe21147b4 100644 --- a/run_page/gpxtrackposter/track.py +++ b/run_page/gpxtrackposter/track.py @@ -342,9 +342,7 @@ def to_namedtuple(self): "average_heartrate": ( int(self.average_heartrate) if self.average_heartrate else None ), - "elevation_gain": ( - int(self.elevation_gain) if self.elevation_gain else 0 - ), + "elevation_gain": (int(self.elevation_gain) if self.elevation_gain else 0), "map": run_map(self.polyline_str), "start_latlng": self.start_latlng, } From 16b96130c73885287ab6d88db27111f5ab732351 Mon Sep 17 00:00:00 2001 From: ben_29 Date: Mon, 3 Jun 2024 21:38:33 +0800 Subject: [PATCH 13/14] feat: elevation gain - fix --- run_page/db_updater.py | 7 ++++++- run_page/generator/__init__.py | 2 ++ src/components/RunTable/RunRow.tsx | 2 +- src/components/RunTable/index.tsx | 4 +++- src/utils/utils.ts | 2 +- 5 files changed, 13 insertions(+), 4 deletions(-) diff --git a/run_page/db_updater.py b/run_page/db_updater.py index 570ed5aea7d..9777be486d2 100644 --- a/run_page/db_updater.py +++ b/run_page/db_updater.py @@ -1,6 +1,9 @@ from generator.db import init_db, Activity from config import SQL_FILE import sqlalchemy +from sqlalchemy import text +from config import GPX_FOLDER, JSON_FILE, SQL_FILE +from utils import make_activities_file def add_column_elevation_gain(session): @@ -11,10 +14,12 @@ def add_column_elevation_gain(session): print("column elevation_gain already added, skipping") except sqlalchemy.exc.OperationalError: sql_statement = 'alter TABLE "activities" add column elevation_gain Float after average_heartrate' - session.execute(statement=sql_statement) + session.execute(text(sql_statement)) print("column elevation_gain added successfully") if __name__ == "__main__": session = init_db(SQL_FILE) add_column_elevation_gain(session) + # regenerate activities + make_activities_file(SQL_FILE, GPX_FOLDER, JSON_FILE) diff --git a/run_page/generator/__init__.py b/run_page/generator/__init__.py index 9c798718a6f..fcc9e1b63c9 100644 --- a/run_page/generator/__init__.py +++ b/run_page/generator/__init__.py @@ -65,6 +65,8 @@ def sync(self, force): continue if IGNORE_BEFORE_SAVING: activity.summary_polyline = filter_out(activity.summary_polyline) + # strava use total_elevation_gain as elevation_gain + activity.elevation_gain = activity.total_elevation_gain created = update_or_create_activity(self.session, activity) if created: sys.stdout.write("+") diff --git a/src/components/RunTable/RunRow.tsx b/src/components/RunTable/RunRow.tsx index 77d69fef9ac..5f05ea2ebf0 100644 --- a/src/components/RunTable/RunRow.tsx +++ b/src/components/RunTable/RunRow.tsx @@ -11,7 +11,7 @@ interface IRunRowProperties { const RunRow = ({ elementIndex, locateActivity, run, runIndex, setRunIndex }: IRunRowProperties) => { const distance = (run.distance / 1000.0).toFixed(2); - const elevation_gain = run.elevation_gain.toFixed(0); + const elevation_gain = run.elevation_gain?.toFixed(0); const paceParts = run.average_speed ? formatPace(run.average_speed) : null; const heartRate = run.average_heartrate; const runTime = formatRunTime(run.moving_time); diff --git a/src/components/RunTable/index.tsx b/src/components/RunTable/index.tsx index 1929c921a34..2dc83413345 100644 --- a/src/components/RunTable/index.tsx +++ b/src/components/RunTable/index.tsx @@ -31,7 +31,9 @@ const RunTable = ({ const sortKMFunc: SortFunc = (a, b) => sortFuncInfo === 'KM' ? a.distance - b.distance : b.distance - a.distance; const sortElevationGainFunc: SortFunc = (a, b) => - sortFuncInfo === 'Elevation Gain' ? a.elevation_gain - b.elevation_gain : b.elevation_gain - a.elevation_gain; + sortFuncInfo === 'Elevation Gain' + ? (a.elevation_gain ?? 0) - (b.elevation_gain ?? 0) + : (b.elevation_gain ?? 0) - (a.elevation_gain ?? 0); const sortPaceFunc: SortFunc = (a, b) => sortFuncInfo === 'Pace' ? a.average_speed - b.average_speed diff --git a/src/utils/utils.ts b/src/utils/utils.ts index 3237189643a..503e66ac7ab 100644 --- a/src/utils/utils.ts +++ b/src/utils/utils.ts @@ -22,7 +22,7 @@ export interface Activity { location_country?: string | null; summary_polyline?: string | null; average_heartrate?: number | null; - elevation_gain: number; + elevation_gain: number | null; average_speed: number; streak: number; } From 93c36f8386dfa444d931202077dc5415a8a352df Mon Sep 17 00:00:00 2001 From: ben_29 Date: Mon, 19 Aug 2024 18:54:41 +0800 Subject: [PATCH 14/14] doc: update notice --- .github/workflows/run_data_sync.yml | 7 ++++++- README-CN.md | 19 ++++++++++++++----- README.md | 22 +++++++++++++++------- 3 files changed, 35 insertions(+), 13 deletions(-) diff --git a/.github/workflows/run_data_sync.yml b/.github/workflows/run_data_sync.yml index 065a175262f..05fb75a046c 100644 --- a/.github/workflows/run_data_sync.yml +++ b/.github/workflows/run_data_sync.yml @@ -25,7 +25,7 @@ on: env: # please change to your own config. - RUN_TYPE: pass # support strava/nike/garmin/coros/garmin_cn/garmin_sync_cn_global/keep/only_gpx/only_fit/nike_to_strava/strava_to_garmin/tcx_to_garmin/strava_to_garmin_cn/garmin_to_strava/garmin_to_strava_cn/codoon/oppo, Please change the 'pass' it to your own + RUN_TYPE: pass # support strava/nike/garmin/coros/garmin_cn/garmin_sync_cn_global/keep/only_gpx/only_fit/nike_to_strava/strava_to_garmin/tcx_to_garmin/strava_to_garmin_cn/garmin_to_strava/garmin_to_strava_cn/codoon/oppo/db_updater, Please change the 'pass' it to your own ATHLETE: yihong0618 TITLE: Yihong0618 Running MIN_GRID_DISTANCE: 10 # change min distance here @@ -202,6 +202,11 @@ jobs: # If you want to sync fit activity in gpx format, please consider the following script: # python run_page/oppo_sync.py ${{ secrets.OPPO_ID }} ${{ secrets.OPPO_CLIENT_SECRET }} ${{ secrets.OPPO_CLIENT_REFRESH_TOKEN }} --with-gpx + - name: Run db updater script to add "Elevation Gain" field to db + if: env.RUN_TYPE == 'db_updater' + run: | + python run_page/db_updater.py + - name: Make svg GitHub profile if: env.RUN_TYPE != 'pass' run: | diff --git a/README-CN.md b/README-CN.md index b07320a61fe..1c0406f99b2 100644 --- a/README-CN.md +++ b/README-CN.md @@ -4,12 +4,21 @@ 2. python3(python) in README means python3 python 3. use v2.0 need change vercel setting from gatsby to vite 4. 2023.09.26 garmin need secret_string(and in Actions) get + + ```bash + python run_page/get_garmin_secret.py ${email} ${password} + # if cn + python run_page/get_garmin_secret.py ${email} ${password} --is-cn + ``` -```bash - python run_page/get_garmin_secret.py ${email} ${password} - # if cn - python run_page/get_garmin_secret.py ${email} ${password} --is-cn -``` +5. 2024.08.19: Added `Elevation Gain` field, If you forked the project before this update, please run the following command: + - To resolve errors: `sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: activities.elevation_gain` + - For old data: To include `Elevation Gain` for past activities, perform a full reimport. + - If you don't have a local environment, set `RUN_TYPE` to `db_updater` in the `.github/workflows/run_data_sync.yml` file once then change back. + + ```bash + python run_page/db_updater.py + ``` ![running_page](https://socialify.git.ci/yihong0618/running_page/image?description=1&font=Inter&forks=1&issues=1&language=1&logo=https%3A%2F%2Fraw.githubusercontent.com%2Fshaonianche%2Fgallery%2Fmaster%2Frunning_page%2Frunning_page_logo_150*150.jpg&owner=1&pulls=1&stargazers=1&theme=Light) diff --git a/README.md b/README.md index 26ef5b37ed0..30cc19d9e13 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,21 @@ 1. clone or Fork before vercel 404 need to pull the latest code 2. python3(python) in README means python3 python 3. use v2.0 need change vercel setting from gatsby to vite -4. 2023.09.26 garmin need secret_string(and in Actions) get - -```bash - python run_page/get_garmin_secret.py ${email} ${password} - # if cn - python run_page/get_garmin_secret.py ${email} ${password} --is-cn -``` +4. 2023.09.26 garmin need secret_string(and in Actions) get + ```bash + python run_page/get_garmin_secret.py ${email} ${password} + # if cn + python run_page/get_garmin_secret.py ${email} ${password} --is-cn + ``` + +5. 2024.08.19: Added `Elevation Gain` field, If you forked the project before this update, please run the following command: + - To resolve errors: `sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: activities.elevation_gain` + - For old data: To include `Elevation Gain` for past activities, perform a full reimport. + - If you don't have a local environment, set `RUN_TYPE` to `db_updater` in the `.github/workflows/run_data_sync.yml` file once then change back. + + ```bash + python run_page/db_updater.py + ```