jovandeginste / workout-tracker Goto Github PK
View Code? Open in Web Editor NEWA workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based activities
License: Other
A workout tracking web application for personal use (or family, friends), geared towards running and other GPX-based activities
License: Other
Workout-tracker version: [branch master (5ccfa42)]
I set up the Fitotrack (app version 15.4) API to export tracks direct into workout-tracker. It worked great, except there are some oddities in the data after importing.
The splits don't match what is displayed in Fitotrack:
...though the overall distance and time are mostly correct. The graph in workout-tracker is very wonky too, with strange overlapping artefacts for the elevation and average speed. It looks like the data points are being plotted out of order. Two points close together on the X axis are 8 minutes apart in time:
The track as downloaded from workout-tracker: track.zip.
I deleted the workout in workout tracker and re-imported the exported track. That was corrupt in the same way. I deleted the workout, then I exported the GPX from fitotrack and imported it into workout-tracker via the web interface. It imported perfectly, with proper splits and graphing:
Here's that track: fitotrack_export.zip
Would it be possible to move the following to the admin page?
WT_REGISTRATION_DISABLED="false"
WT_SOCIALS_DISABLED="false"
This would save having to restart the Docker container.
It might also be worth having an option for each user if WT_SOCIALS_DISABLED
is false globally, they can still disable if they wish.
This way the admin can determine if socials are allowed or not at a server level.
If socials are allowed, then a user can still remove the option to do so if they wish.
I'm testing workout-tracker , I love that app which runs fast on Docker image 👍
I'd like to import past workouts from csv file which is formatted like that (I don't have gpx files any more) :
id,uuid,user_id,sport_id,title,gpx,creation_date,modification_date,workout_date,duration,pauses,moving,distance,min_alt,max_alt,descent,ascent,max_speed,ave_speed,bounds,map,map_id,weather_start,weather_end,notes
39,3958a0ec-f780-4579-b7a4-28deffdd8fe5,3,14,Mont Mc,,2018-08-13,,2018-08-13,7:30:00,,7:30:00,20,,3169,,,,,,,,,,-Salomon
I've search database.db but it seems to only get stats on gpx source.
I sync GPX files from fitotrack to my server. I would love to drop them into a directory and have them automatically import into workout-tracker.
It might be less work overall to write a crontask to curl the GPX files to the workouts endpoint, provided I can get the auth sorted out.
In any case: Love this project! I just imported 401 workouts into my instance in one go. It took a few minutes but worked perfectly. Thanks hugely!
@jovandeginste I am seeing this error now when adding a workout for GPX files that used to work. Could be related to your recent change that separated the GPX data to a separate table?
When editing a workout the "Type" defaults to running.
So if you don't realise you can change your workout type by mistake.
Would it be possible it reads the current "Type" and shows that as the default, that way when you "Update workout" your not changing the type, but you still have the option to do so if needed.
Would be nice to have an option to switch the date displayed.
In Workouts view, and when viewing a specific workout, the date is shown relative to the current time, e.g. 3 days, 1 hour ago instead of 2024-04-15 1:31 PM.
Great work overall, thank you.
Thanks for the app! Would it be possible to add imperial units like miles instead of kilometers?
thanks for making this.
Loving the app, would it be possible to add functionality to export a gpx file from a workout please?
So far there is only english and dutch.
It would be great if there was also a choice of German as a translation.
If you want, I can also do this.
I think there is a typo in pkg/app/tempates.go
; it should be templates.go
FitoTrack has the ability to automatically export a GPX workout at the end of the workout and send it to a server.
Would it be possible to accept these requests?
I have my Workout-Tracker behind a Nginx proxy so I can securely expose Workout-Tracker to the outside world.
I think this would be a cleaner solution than exporting to a folder and then having an additional app running to export it to the server. You wouldn't need something running on the server to except file uploads either.
Hi,
Fitnotes allows you to manually track your workout. Would it be possible to import the data from its backups?
Would it be possible to list all duplicated workout based on start times?
I have imported some files from Garmin connect and from MapMy Ride / Walk and I have duplicate rides/walks.
Would it be possible to highlight identical start times so I can delete one? Or when importing files check if an existing start time already exists?
Total duration
for Walking and Running activities only goes down to whole seconds, but Time paused
goes down to millionths of a second.
I don't think that resolution for Time paused
is required for either activity.
Perhaps Running or Sprinting will require Total duration
to be in 10ths or 100ths of a second?
Hey, thanks for this app!
Works great!
I have an issue with the global statistics above, they are empty althought a track has been uploaded.
By the way, were are stored the uploaded track on the server? I couldn’t find it in workout-tracker directory (but I can download it from the web interface).
Is there a way to choose a data directory (and set it as environment variable?). It would be useful for the package I made for Yunohost. Thanks!
Hi, thank you for developing this project! I am trying out the project with podman on my local machine, and there seems to be an error processing a .fit
file generated using a Wahoo Element Bolt bike computer.
version: v0.13.0
The following error is seen on the page:
The console log follows:
{"time":"2024-04-14T19:25:40.367276288Z","level":"ERROR","msg":"sql: converting argument $5 type: json: unsupported value: NaN","app":"workout-tracker","version":"master","sha":"1131e68d8a262f85731b2a661f5a6af1897aa8af","module":"database","error":"sql: converting argument $5 type: json: unsupported value: NaN","query":"INSERT INTO `map_data_details` (`created_at`,`updated_at`,`deleted_at`,`map_data_id`,`points`) VALUES (\"2024-04-14 19:25:40.366\",\"2024-04-14 19:25:40.366\",NULL,1,\"\") ON CONFLICT (`id`) DO UPDATE SET `map_data_id`=`excluded`.`map_data_id` RETURNING `id`","duration":117335,"rows":0,"file":"/app/pkg/database/workouts.go:197"}
{"time":"2024-04-14T19:25:40.367474547Z","level":"ERROR","msg":"sql: converting argument $5 type: json: unsupported value: NaN","app":"workout-tracker","version":"master","sha":"1131e68d8a262f85731b2a661f5a6af1897aa8af","module":"database","error":"sql: converting argument $5 type: json: unsupported value: NaN","query":"INSERT INTO `map_data` (`created_at`,`updated_at`,`deleted_at`,`workout_id`,`creator`,`name`,`center`,`address`,`total_distance`,`total_duration`,`max_speed`,`pause_duration`,`min_elevation`,`max_elevation`,`total_up`,`total_down`,`points`) VALUES (\"2024-04-14 19:25:40.366\",\"2024-04-14 19:25:40.366\",NULL,1,\"Garmin Connect\",\"\",\"{\"\"Lat\"\":47.45598521556553,\"\"Lng\"\":8.797513872274084}\",\"{\"\"FormattedAddress\"\":\"\"Burgstrasse, Rutzen, Rikon, Zell (ZH), Bezirk Winterthur, Zürich, 8486, Schweiz/Suisse/Svizzera/Svizra\"\",\"\"Street\"\":\"\"Burgstrasse\"\",\"\"HouseNumber\"\":\"\"\"\",\"\"Suburb\"\":\"\"\"\",\"\"Postcode\"\":\"\"8486\"\",\"\"State\"\":\"\"Zürich\"\",\"\"StateCode\"\":\"\"\"\",\"\"StateDistrict\"\":\"\"\"\",\"\"County\"\":\"\"\"\",\"\"Country\"\":\"\"Schweiz/Suisse/Svizzera/Svizra\"\",\"\"CountryCode\"\":\"\"CH\"\",\"\"City\"\":\"\"Rikon\"\"}\",0,6524000000000,0,776000000776,0,0,355.1200000000007,0,NULL) ON CONFLICT (`id`) DO UPDATE SET `workout_id`=`excluded`.`workout_id` RETURNING `id`","duration":1315417,"rows":1,"file":"/app/pkg/database/workouts.go:197"}
{"time":"2024-04-14T19:25:40.368148467Z","level":"ERROR","msg":"sql: converting argument $5 type: json: unsupported value: NaN; sql: converting argument $5 type: json: unsupported value: NaN","app":"workout-tracker","version":"master","sha":"1131e68d8a262f85731b2a661f5a6af1897aa8af","module":"database","error":"sql: converting argument $5 type: json: unsupported value: NaN; sql: converting argument $5 type: json: unsupported value: NaN","query":"INSERT INTO `workouts` (`created_at`,`updated_at`,`deleted_at`,`name`,`date`,`user_id`,`dirty`,`notes`,`type`,`data`,`gpx_data`,`filename`) VALUES (\"2024-04-14 19:25:40.364\",\"2024-04-14 19:25:40.364\",NULL,\"\",\"2024-04-12 14:21:29\",1,false,\"\",\"cycling\",NULL,\"\",\"\") RETURNING `id`,`checksum`","duration":5123591,"rows":1,"file":"/app/pkg/database/workouts.go:197"}
I would happily provide you with the file privately via Email if you decide to tackle the issue, since I don't want to reveal my location. Thanks!
After adding imperial unit support, a few locations need to be updated to use those units.
Hi,
I tried the docker ony my RPI4 but always get this:
The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
Is there a simple fix?
The project looks really nice and I would love to use it :)
Thank you!
Hello,
I have imported a bunch of workouts, but the calendar is displaying the timing of one of them wrong. I assume its basing it off UTC and has meant a workout I did during the day is over two days now.
Here is the calendar showing its over two days:
Here is the average speed / elevation graph showing the correct times at the bottom:
Just want to say thanks for making this, amazing project so far!
View of my summary Dashboard:
(Still need to trim some walks, especially that 27.31 mph walk 😉 )
If I click on User 2, I get User 2's calendar workouts but it still displays my summary:
View of User 2, from their login:
(Need to correct a number of User 2's workouts, as many come up with 0 miles due to long pauses between walks I think)
Would it be possible to disable the share to socials (facebook, whatsapp etc) options underneath the GPX maps.
It would be good if this could be managed through a variable?
Thanks again for the other requests you've actioned.
I would dearly love to be able to import stationary workouts from fitotrack. Primarily treadmill workouts, but elliptical and rowing machine workouts would also be nice.
Hi,
I have started my activity with release version 10.5.
I have imported 5800 workouts. Worked fine.
The Next release what I used as an upgrade was 12.1. Start automatically did the upgrade. The application loads perfectly and even lists the workouts and charts. However, If I click on one particular workout for details I got:
{"message":"Internal Server Error"}
In the log I can see:
8:21PM ERR template: workouts_show.html:65:45: executing "workouts_show.html" at <.StatisticsPer>: error calling StatisticsPer: runtime error: invalid memory address or nil pointer dereference app=workout-tracker version=v0.12.3 sha=6a69273150cab3138749ee5979d462bf959e58ee module=webserver request.time="2024-04-13 20:21:34.73549021 +0200 CEST" request.method=GET
If I click on the workout list view I can make a "refresh". It fixes it.
However, I don't want to do it on more than 5000 entries.
Is there a way to trigger a refresh for all workouts? or is it a bug?
Now I tried 12.3 release as well, but it is the same.
Just tried to download a workout so I could edit it. As I need to trim the last section. But I get the following error.
It then just returns you back to the /workouts
page
Output from Docker, doesn't really give any details?
{"time":"2024-04-13T13:23:13.409458499+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.401963934+01:00","method":"GET","host":"IpAddress:8083","path":"/workouts/66/download","query":"","params":{"id":"66"},"route":"/workouts/:id/download","ip":"IpAddress","referer":"IpAddress:8083/workouts/66","length":0},"response":{"time":"2024-04-13T13:23:13.409449427+01:00","latency":7485492,"status":302,"length":0}}
{"time":"2024-04-13T13:23:13.533190114+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.412059471+01:00","method":"GET","host":"IpAddress:8083","path":"/workouts","query":"","params":{},"route":"/workouts","ip":"IpAddress","referer":"IpAddress:8083/workouts/66","length":0},"response":{"time":"2024-04-13T13:23:13.533169064+01:00","latency":121109592,"status":200,"length":0}}
{"time":"2024-04-13T13:23:13.540818913+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.540362231+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/common.js","query":"","params":{"*":"/common.js"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/workouts","length":0},"response":{"time":"2024-04-13T13:23:13.54081133+01:00","latency":449101,"status":200,"length":0}}
{"time":"2024-04-13T13:23:13.542237343+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.540295663+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/output.css","query":"","params":{"*":"/output.css"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/workouts","length":0},"response":{"time":"2024-04-13T13:23:13.542213138+01:00","latency":1917476,"status":200,"length":0}}
{"time":"2024-04-13T13:23:13.542916617+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.54044599+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/dist/sorttable.js","query":"","params":{"*":"/dist/sorttable.js"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/workouts","length":0},"response":{"time":"2024-04-13T13:23:13.542909427+01:00","latency":2463438,"status":200,"length":0}}
{"time":"2024-04-13T13:23:13.5436673+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.539075891+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/dist/fontawesome/css/all.min.css","query":"","params":{"*":"/dist/fontawesome/css/all.min.css"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/workouts","length":0},"response":{"time":"2024-04-13T13:23:13.543660393+01:00","latency":4584501,"status":200,"length":0}}
{"time":"2024-04-13T13:23:13.669444496+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:13.665640176+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/dist/fontawesome/webfonts/fa-solid-900.woff2","query":"","params":{"*":"/dist/fontawesome/webfonts/fa-solid-900.woff2"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/assets/dist/fontawesome/css/all.min.css","length":0},"response":{"time":"2024-04-13T13:23:13.669433112+01:00","latency":3792932,"status":200,"length":0}}
{"time":"2024-04-13T13:23:14.645143664+01:00","level":"INFO","msg":"Incoming request","app":"workout-tracker","version":"master","sha":"e0a914e06295043ff333727a9780f262715fe778","module":"webserver","request":{"time":"2024-04-13T13:23:14.642138501+01:00","method":"GET","host":"IpAddress:8083","path":"/assets/dist/fontawesome/webfonts/fa-brands-400.woff2","query":"","params":{"*":"/dist/fontawesome/webfonts/fa-brands-400.woff2"},"route":"/assets*","ip":"IpAddress","referer":"IpAddress:8083/assets/dist/fontawesome/css/all.min.css","length":0},"response":{"time":"2024-04-13T13:23:14.645131688+01:00","latency":2993186,"status":200,"length":0}}
It would be fantastic if we could upload photos that were taken on our hikes and them be displayed on the map. I use another service called RideWithGPS which does this so I've attached a screenshot to show what I mean.
Thanks again for this wonderful project it's amazing that you are so responsive. Do you have a way to donate?
Tried to import a swimming file and got the error “ Encountered 1 problems while adding workouts: sql: converting argument $10 type: json: unsupported value: NaN”
Can you please help?
Great project!
When importing a GPX generated by the Workoutdoors iOS app, I get this error: constraint failed: NOT NULL constraint failed: workouts.date (1299)
.
Importing a GPX generated by Strava from the same workout works. The difference appears to be that the Workoutdoors file does not have a <time>
element under <metadata>
in the GPX, whereas the Strava one has it. The Workoutdoors file does have <time>
items under each track point:
<gpx version="1.1" ...>
<metadata><name>Wednesday Evening Run</name></metadata>
<trk><name>Wednesday Evening Run</name><type>running</type>
<trkseg>
<trkpt lat="xxx" lon="xxx">
<ele>-30</ele><time>2024-02-28T17:26:34.000Z</time>
Perhaps the importer could use the earliest date in the file in case a date under metadata is missing.
I can have a go at implementing this myself if such feature would be desirable. If so any guidance on where to start would be appreciated (i.e. which part of the code to look)!
I have noticed a time discrepancy between the Workouts Sumary page and the timeline on the individual Workout page.
The "an hour ago" is correct. But the displayed time should be 16:18
Just for info the Walk started 16:18 and current time as posting is 17:58
Hey, first of all very nice project! I was excited to try it, exported my activities from Strava, and promptly noticed I had a suspicious number of activities listed today. It seems like it only looks at the file modified (or created?) date, and not at the file titles. I think it might be useful to also look at file names, since exported data will often have incorrect modified dates.
First off, awesome work - love what you've done here!
From an architectural perspective when implementing I'd assume other services should also be considered (Strava etc.), but for my own use case I'd like to see an automatic import (on each training session) from Garmin Connect's API (preferably the PUSH approach for instant updates - but also sync/manual import incase of downtime?)
Is this something that's on the roadmap or being considered?
I am running the ulogger server ATM with the android client. Would be great if workout tracker could fetch the gpx files automatically. Ui is looking great so far! I'll give the app a try asap.
I like the simplicity of this app, and I would love to see a REST API accessible on this so that I can integrate it into my Home Assistant instance, some ideas could be:
Thank you, and great work!
I tried to enable the API access so I could integrate directly with fitotrack
Browser error: Something went wrong: constraint failed: UNIQUE constraint failed: profiles.id (1555)
Logs:
workout_tracker | {"time":"2024-05-03T01:04:08.819639955Z","level":"ERROR","msg":"constraint failed: UNIQUE constraint failed: profiles.id (1555)","app":"workout-tracker","version":"master","sha":"<snip>","module":"database","error":"constraint failed: UNIQUE constraint failed: profiles.id (1555)","query":"INSERT INTO `profiles` (`created_at`,`updated_at`,`deleted_at`,`user_id`,`api_active`,`language`,`totals_show`,`timezone`,`auto_import_directory`,`socials_disabled`,`prefer_full_date`,`preferred_units`,`id`) VALUES (\"2024-05-03 01:04:08.818\",\"2024-05-03 01:04:08.818\",NULL,1,true,\"browser\",\"auto\",\"\",\"\",false,false,\"{\"\"speed\"\":\"\"\"\",\"\"distance\"\":\"\"\"\",\"\"elevation\"\":\"\"\"\"}\",3) RETURNING `id`","duration":1465174,"rows":0,"file":"/app/pkg/database/profile.go:64"}
Version: branch master (5ccfa42)
Environment: Containerised, Debian 12 64b, image from ghcr.io/jovandeginste/workout-tracker:master
Sqlite:
sqlite> select * from profiles;
id|created_at|updated_at|deleted_at|user_id|api_active|language|totals_show|timezone|auto_import_directory|socials_disabled|prefer_full_date|preferred_units
1|2024-03-08 12:44:45.089166958+00:00|2024-03-08 12:44:45.089166958+00:00||0|0|browser|running|||||
2|2024-03-08 13:06:39.760551862+00:00|2024-03-08 13:06:39.760551862+00:00||0|0|browser|running|Australia/Brisbane||||
This may be a DB schema issue, since I originally set up the DB on a much earlier version. I am perfectly amenable to deleting the database and re-importing the track files, but I thought the above might be useful.
Hi, with the native installation process I noticed that I get
{"message":"Not Found"}
When I request http://127.0.0.1:8080/assets/dist/fontawesome/css/all.min.css
The binary packages only ship the go binary, not the static css and js files. However, the favicon is there:
Unrelated, is there an environment variable for the port and host to use?
I would like to create an AUR package for workout-tracker and include these variables in an environment file.
First of all amazing project it took 5 mins to spin up on Unraid.
Is it possible to support markdown in the notes field? I'm thinking on using it for instructions for hikes etc.
Once again thank you for an amazing project.
I have just tested the app and it looks great!
What I'm still missing are the other performance graphs that are usually visible in services like Garmin Connect and Strava. I mean things like heart rate, cadence, altitude, performance condition, vertical ratio etc.
I don't know if Garmin's FIT files contain all this information, but I assume they do. I quickly checked the GPX and TCX exports from Garmin Connect and they include heart rate, cadence and temperature, but not all the other things that are visible in Garmin Connect.
I can provide sample files if that helps
Update: I checked a FIT file and it seems to contain all the data just as I assumed.
Great project!
When I try to set the timezone by clicking my username on top and then selecting timezone and clicking "update profile" I am not sure it "sticks". Next time I go to that same page, "Timezone" is empty. (Same goes for language after I have selected it).
I notice my workout (I only have one so far) is off in time - most likely due to the timezone not being set.
Maybe I am doing something wrong .... ?
Inside the docker image the app seems to run as root user:
docker exec -it workout-tracker sh
/data # ps -ef|grep workout
1 root 0:00 /app/workout-tracker
20 root 0:00 grep workout
Maybe it would be great to create a user with less privileges ?
Just going through my imported Workouts from FitoTrack and noticed one had a top speed of 20km/m near the end.
So I wanted to see if I could trim the walk to remove the last few mins. But I get the following error when I press the edit button:
I'm not sure if a log file records any further information or not as I only have the /data
folder exposed on Unraid.
If an error log is created would it be possible to have a copy stored in /data
with the database file?
Would be awesome to be able to connect to the Strava API to sync my workouts from there. Other workout apps would be awesome too. Loving the project so far!
Hi,
I just updated to the newest Version and now my average speed is not correct anymore (it is a little to high)
It would be nice to add equipments like bike, shoes, bag
Currently on the login page anyone can register an account.
Would it be possible to add a variable to disable registration on the login page please?
Loving the project and your response to my other issue was really appreciated!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.