Skip to content

Instantly share code, notes, and snippets.

@monperrus
Created March 16, 2025 10:30
Show Gist options
  • Save monperrus/a5eb677edba5963b0a16bb0907d1828c to your computer and use it in GitHub Desktop.
Save monperrus/a5eb677edba5963b0a16bb0907d1828c to your computer and use it in GitHub Desktop.
a python script to merge all activities in a tcx file
import xml.etree.ElementTree as ET
from datetime import datetime, timedelta
import sys
def merge_tcx_activities(input_file, output_file):
# Parse the input TCX file
tree = ET.parse(input_file)
root = tree.getroot()
nspace = root.tag.split('}')[0].strip('{')
# Find the namespace
ns = {'ns': nspace}
ET.register_namespace('',nspace)
# Find all activities
activities = root.findall('.//ns:Activity', ns)
if len(activities) <= 1:
print("No activities to merge or only one activity found.")
return
# Use the first activity as the base
base_activity = activities[0]
# Get the start time of the first activity
start_time = datetime.strptime(
base_activity.find('.//ns:Time', ns).text,
'%Y-%m-%dT%H:%M:%S.%fZ'
)
# for ac in activities:
# print(ac.findall('.//ns:DistanceMeters', ns))
totaldistance = sum([float(ac.findall('.//ns:DistanceMeters', ns)[-1].text) for ac in activities])
base_activity.findall('.//ns:DistanceMeters', ns)[-1].text = str(totaldistance)
totaltime = sum([float(ac.findall('.//ns:TotalTimeSeconds', ns)[-1].text) for ac in activities])
base_activity.findall('.//ns:TotalTimeSeconds', ns)[-1].text = str(totaltime)
totalcalories = sum([float(ac.findall('.//ns:Calories', ns)[-1].text) for ac in activities])
base_activity.findall('.//ns:Calories', ns)[-1].text = str(totalcalories)
# Process each subsequent activity
for activity in activities[1:]:
# Get all trackpoints from the current activity
trackpoints = activity.findall('.//ns:Trackpoint', ns)
# Get the time of the first trackpoint in the current activity
first_tp_time = datetime.strptime(
trackpoints[0].find('ns:Time', ns).text,
'%Y-%m-%dT%H:%M:%S.%fZ'
)
# Calculate time difference
time_diff = first_tp_time - start_time
# Add trackpoints to the base activity
main_lap = base_activity.find('ns:Lap', ns)
track = main_lap.find('ns:Track', ns)
for tp in trackpoints:
# Add the adjusted trackpoint to the base activity
track.append(tp)
# Remove the processed activity
root.find('.//ns:Activities', ns).remove(activity)
# Update the total time and distance in the base lap
base_lap = base_activity.find('ns:Lap', ns)
trackpoints = base_activity.findall('.//ns:Trackpoint', ns)
if trackpoints:
# first_time = datetime.strptime(trackpoints[0].find('ns:Time', ns).text, '%Y-%m-%dT%H:%M:%S.%fZ')
# last_time = datetime.strptime(trackpoints[-1].find('ns:Time', ns).text, '%Y-%m-%dT%H:%M:%S.%fZ')
# total_time = (last_time - first_time).total_seconds()
#
# # Update TotalTimeSeconds
# total_time_elem = base_lap.find('ns:TotalTimeSeconds', ns)
# if total_time_elem is not None:
# total_time_elem.text = str(total_time)
# Calculate and update DistanceMeters if available
# remove all DistanceMeters
for tp in trackpoints:
distance_elem = tp.find('ns:DistanceMeters', ns)
if distance_elem is not None:
# tp.remove(distance_elem)
pass
# distance_points = [float(tp.find('ns:DistanceMeters', ns).text) for tp in trackpoints if track.find('ns:DistanceMeters', ns) is not None]
# if distance_points:
# total_distance = distance_points[-1] - distance_points[0]
# distance_elem = base_lap.find('ns:DistanceMeters', ns)
# if distance_elem is not None:
# distance_elem.text = str(total_distance)
# Save the merged file
tree.write(output_file, encoding='UTF-8', xml_declaration=True)
print(f"Activities merged successfully. Output saved to {output_file}")
# Example usage
if __name__ == "__main__":
input_file = sys.argv[1]
output_file = "merged_"+sys.argv[1]
merge_tcx_activities(input_file, output_file)
# Example usage
# shutil.copy(sys.arvg[1],sys.arvg[1]+".bak")
# merge_laps_in_tcx(input_file, output_file)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment