Skip to content

Commit

Permalink
fix: 584 change fit-tool to garmin-fit-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
ben-29 committed Jan 3, 2024
1 parent f32d6d0 commit 3f5a22a
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 71 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ stravaweblib
tenacity
numpy
tzlocal
fit-tool
garmin-fit-sdk
haversine==2.8.0
garth
pycryptodome
Expand Down
114 changes: 54 additions & 60 deletions run_page/gpxtrackposter/track.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,8 @@
import lxml
import polyline
import s2sphere as s2
from fit_tool.fit_file import FitFile
from fit_tool.profile.messages.activity_message import ActivityMessage
from fit_tool.profile.messages.device_info_message import DeviceInfoMessage
from fit_tool.profile.messages.file_id_message import FileIdMessage
from fit_tool.profile.messages.record_message import RecordMessage
from fit_tool.profile.messages.session_message import SessionMessage
from fit_tool.profile.messages.software_message import SoftwareMessage
from fit_tool.profile.profile_type import Sport
from garmin_fit_sdk import Decoder, Stream
from garmin_fit_sdk.util import FIT_EPOCH_S
from polyline_processor import filter_out
from rich import print
from tcxreader.tcxreader import TCXReader
Expand All @@ -31,6 +25,7 @@
run_map = namedtuple("polyline", "summary_polyline")

IGNORE_BEFORE_SAVING = os.getenv("IGNORE_BEFORE_SAVING", False)
SEMICIRCLE = 11930465


class Track:
Expand Down Expand Up @@ -91,9 +86,12 @@ def load_fit(self, file_name):
# (for example, treadmill runs pulled via garmin-connect-export)
if os.path.getsize(file_name) == 0:
raise TrackLoadError("Empty FIT file")

fit = FitFile.from_file(file_name)
self._load_fit_data(fit)
stream = Stream.from_file(file_name)
decoder = Decoder(stream)
messages, errors = decoder.read(convert_datetimes_to_dates=False)
if len(errors) > 0:
print(f"FIT file read fail: {errors}")
self._load_fit_data(messages)
except Exception as e:
print(
f"Something went wrong when loading FIT. for file {self.file_names[0]}, we just ignore this file and continue"
Expand Down Expand Up @@ -223,59 +221,55 @@ def _load_gpx_data(self, gpx):
)
self.moving_dict = self._get_moving_data(gpx)

def _load_fit_data(self, fit: FitFile):
def _load_fit_data(self, fit: dict):
_polylines = []
self.polyline_container = []
message = fit["session_mesgs"][0]
self.start_time = datetime.datetime.utcfromtimestamp(
(message["start_time"] + FIT_EPOCH_S)
)
self.run_id = self.__make_run_id(self.start_time)
self.end_time = datetime.datetime.utcfromtimestamp(
(message["start_time"] + FIT_EPOCH_S + message["total_elapsed_time"])
)
self.length = message["total_distance"]
self.average_heartrate = (
message["avg_heart_rate"] if "avg_heart_rate" in message else None
)
self.type = message["sport"].lower()

for record in fit.records:
message = record.message

if isinstance(message, RecordMessage):
if message.position_lat and message.position_long:
_polylines.append(
s2.LatLng.from_degrees(
message.position_lat, message.position_long
)
)
self.polyline_container.append(
[message.position_lat, message.position_long]
)
elif isinstance(message, SessionMessage):
self.start_time = datetime.datetime.utcfromtimestamp(
message.start_time / 1000
)
self.run_id = message.start_time
self.end_time = datetime.datetime.utcfromtimestamp(
(message.start_time + message.total_elapsed_time * 1000) / 1000
)
self.length = message.total_distance
self.average_heartrate = (
message.avg_heart_rate if message.avg_heart_rate != 0 else None
)
self.type = Sport(message.sport).name.lower()

# moving_dict
self.moving_dict["distance"] = message.total_distance
self.moving_dict["moving_time"] = datetime.timedelta(
seconds=message.total_moving_time
if message.total_moving_time
else message.total_timer_time
)
self.moving_dict["elapsed_time"] = datetime.timedelta(
seconds=message.total_elapsed_time
)
self.moving_dict["average_speed"] = (
message.enhanced_avg_speed
if message.enhanced_avg_speed
else message.avg_speed
)

self.start_time_local, self.end_time_local = parse_datetime_to_local(
self.start_time, self.end_time, self.polyline_container[0]
# moving_dict
self.moving_dict["distance"] = message["total_distance"]
self.moving_dict["moving_time"] = datetime.timedelta(
seconds=message["total_moving_time"]
if "total_moving_time" in message
else message["total_timer_time"]
)
self.moving_dict["elapsed_time"] = datetime.timedelta(
seconds=message["total_elapsed_time"]
)
self.start_latlng = start_point(*self.polyline_container[0])
self.polylines.append(_polylines)
self.polyline_str = polyline.encode(self.polyline_container)
self.moving_dict["average_speed"] = (
message["enhanced_avg_speed"]
if message["enhanced_avg_speed"]
else message["avg_speed"]
)
for record in fit["record_mesgs"]:
if "position_lat" in record and "position_long" in record:
lat = record["position_lat"] / SEMICIRCLE
lng = record["position_long"] / SEMICIRCLE
_polylines.append(s2.LatLng.from_degrees(lat, lng))
self.polyline_container.append([lat, lng])
if len(self.polyline_container) > 0:
self.start_time_local, self.end_time_local = parse_datetime_to_local(
self.start_time, self.end_time, self.polyline_container[0]
)
self.start_latlng = start_point(*self.polyline_container[0])
self.polylines.append(_polylines)
self.polyline_str = polyline.encode(self.polyline_container)
else:
self.start_time_local, self.end_time_local = parse_datetime_to_local(
self.start_time, self.end_time, None
)

def append(self, other):
"""Append other track to self."""
Expand Down
23 changes: 13 additions & 10 deletions run_page/gpxtrackposter/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -129,16 +129,19 @@ def format_float(f):


def parse_datetime_to_local(start_time, end_time, point):
# just parse the start time, because start/end maybe different
offset = start_time.utcoffset()
if offset:
return start_time + offset, end_time + offset
lat, lng = point
try:
timezone = get_tz(lng=lng, lat=lat)
except:
# just a little trick when tzfpy support windows will delete this
if not point:
timezone = "Asia/Shanghai"
else:
# just parse the start time, because start/end maybe different
offset = start_time.utcoffset()
if offset:
return start_time + offset, end_time + offset
lat, lng = point
timezone = tf.timezone_at(lng=lng, lat=lat)
try:
timezone = get_tz(lng=lng, lat=lat)
except:
# just a little trick when tzfpy support windows will delete this
lat, lng = point
timezone = tf.timezone_at(lng=lng, lat=lat)
tc_offset = datetime.now(pytz.timezone(timezone)).utcoffset()
return start_time + tc_offset, end_time + tc_offset

0 comments on commit 3f5a22a

Please sign in to comment.