Storing an exported training upload
For support. A training the server keeps refusing stays in the tracking app's upload queue; the app never deletes it. From 3.0.8 the user can hand it over as a file: tap "Pending uploads", then "Export" in the message. This page says what the file is and how to get the training into the account.
When a user sends a file
- Ask them to update first. Most refusals are healed by the app or the server on the next attempt (see offline & sync, "An upload the server refuses"). After the update: open the app with a connection and wait a minute. If "Pending uploads" is gone, nothing is left to do.
- If it stays, ask for the export. The user must not sign out, reinstall or clear the app before the training is stored — the queue on the device is the original.
What the file contains
A JSON document, enode-uploads-<date>.json:
| Field | Meaning |
|---|---|
format, version | "enode.stuck-uploads", 1 |
exportedAt | when the file was made (ISO time) |
uploaderUserID | the signed-in user whose queue it is |
batches[] | one entry per refused upload |
batches[].payload | the sessions exactly as the app sends them to POST /workout_sessions |
batches[].sessionIds | the ids of those sessions |
batches[].lastFailure | the server's last answer: HTTP status, time at, and reason when it gave one |
batches[].exerciseRefs | per session: the exercise definition and the athlete it was recorded for |
batches[].unboundSessions, packageTypeKeys | repair data the app applies before sending; empty for most uploads |
The file holds training data of the account's athletes. Treat it like any other personal data: keep it in the ticket, delete local copies afterwards.
Reading the reason
lastFailure.reason | What it means | What to do |
|---|---|---|
exercise_not_found | the session names an exercise the server no longer has | store it with the exercise from exerciseRefs (below) |
no_access_to_athlete | the uploader may no longer see that athlete | restore the access in the portal, then the app uploads by itself |
unknown_metric | a metric id the server does not know | report to development with the file |
none, status 400 | the server could not read the request | report to development with the file |
none, status 0 | no answer from the server | not a refusal; the app keeps retrying |
Storing the training
The backend has an import route for the file
(POST /api/workout_sessions/import, admin token). For now a backend
developer runs it; support cannot yet:
scripts/import-stuck-uploads.sh <base-url> <file> # in enode_backend_v1
-
Hand the file to a backend developer unchanged, with the ticket. The route takes it as exported.
-
The import acts as
uploaderUserID, with the same checks as a normal upload. A session whose exercise the server does not know is stored on the exercise of the athlete named inexerciseRefs, created if missing. -
The answer lists every session:
outcomeMeaning createdstored now alreadyPresentwas stored before; running a file twice is safe rejected+no_access_to_athletethe uploader may no longer see that athlete — restore the access, run again rejected+exercise_not_foundthe file has no exerciseRefsfor that session (queued by an app before 3.0.8) — a developer has to name the exerciserejected+invalid_payloadthat batch could not be read; the other batches are stored -
1RMs and personal bests are recalculated. No push notification and no session-rating request go out for imported sessions.
-
Tell the user. Their app drops the queue entry on its next attempt, because the server now answers
alreadyPresent.
What cannot be recovered from the file
Set videos are not in it; they stay on the device and can be saved to the photo library from the set's Technique view.