diff --git a/rowers/templates/developers.html b/rowers/templates/developers.html
index b6529e1a..3e5ed958 100644
--- a/rowers/templates/developers.html
+++ b/rowers/templates/developers.html
@@ -658,6 +658,90 @@
fails a test, its values are silently replaced by zeros.
+
+ | Stroke Data (v3) |
+ /rowers/api/v3/workouts/
+ |
+ POST |
+
+ {
+ "totalDistance": 100,
+ "elapsedTime": 29000,
+ "title": "Test Workout (GO)",
+ "startdatetime": "2023-01-16 17:54:35.588838+00:00",
+ "workouttype": "water",
+ "boattype": "1x",
+ "notes": "some\nnotes",
+ "strokes": {
+ "data": [
+ {
+ "time": 3200.0000476837,
+ "pace": 155068.4885951763,
+ "hr": 85.7857142857,
+ "power": 84.6531131591,
+ "distance": 23,
+ "spm": 16.380952381
+ },
+ {
+ "time": 6700.0000476837,
+ "pace" : 144402.6407586741,
+ "hr": 91.2142857143,
+ "power": 117.458827834,
+ "distance": 36,
+ "spm": 21.1666666667
+ },
+ {
+ "time": 10099.9999046326,
+ "pace": 138830.8712654931,
+ "hr": 95.7142857143,
+ "power": 141.31057207,
+ "distance": 48,
+ "spm": 19.8095238095
+ }
+ ]
+ }
+}
+
+ With v3, you can post stroke data and workout metadata in one JSON object.
+
+ For v3, mandatory data fields are:
+
+
+ - time: Time (milliseconds since workout start)
+ - distance: Distance (meters)
+ - pace: Pace (milliseconds per 500m)
+ - spm Stroke rate (strokes per minute)
+
+
+ Optional data fiels are:
+
+
+ - power: Power (Watt)
+ - latitude: GPS position (latitude)
+ - longitude: GPS position (longitude)
+ - drivelength: Drive length (meters)
+ - dragfactor: Drag factor
+ - drivetime: Drive time (ms)
+ - strokerecoverytime: Recovery time (ms)
+ - averagedriveforce: Average handle force (lbs)
+ - peakdriveforce: Peak handle force (lbs)
+ - lapidx: Lap identifier
+ - hr: Heart rate (beats per minute)
+ - wash: Wash as defined per Empower oarlock (degrees)
+ - catch: Catch angle per Empower oarlock (degrees)
+ - finish: Finish angle per Empower oarlock (degrees)
+ - peakforceangle: Peak Force Angle per Empower oarlock (degrees)
+ - slip: Slip as defined per Empower oarlock (degrees)
+
+
+
+ Also for this API version, consistency checks will be done and the stroke data will be
+ refused if the mandatory data fields don't pass the checks.
+ All mandatory data fields
+ must have the same number of records. If an optional data field
+ fails a test, its values are silently replaced by zeros.
+ |
+
diff --git a/rowers/tests/test_api.py b/rowers/tests/test_api.py
index f1a6f73b..32fc2af6 100644
--- a/rowers/tests/test_api.py
+++ b/rowers/tests/test_api.py
@@ -180,3 +180,75 @@ class OwnApi(TestCase):
response = strokedatajson_v2(request,id=w.id)
self.assertEqual(response.status_code,200)
+
+ def test_strokedataform_v3(self):
+ login = self.c.login(username=self.u.username, password=self.password)
+ self.assertTrue(login)
+
+ w = self.user_workouts[1]
+
+ url = reverse('strokedatajson_v3')
+
+ request = self.factory.get(url)
+ request.user = self.u
+ force_authenticate(request, user=self.u)
+ response = strokedatajson_v3(request)
+ self.assertEqual(response.status_code,405)
+
+
+ strokedata = {
+ "data": [
+ {
+ "time": 3200.0000476837,
+ "pace": 155068.4885951763,
+ "hr": 85.7857142857,
+ "power": 84.6531131591,
+ "distance": 23,
+ "spm": 16.380952381
+ },
+ {
+ "time": 6700.0000476837,
+ "pace" : 144402.6407586741,
+ "hr": 91.2142857143,
+ "power": 117.458827834,
+ "distance": 36,
+ "spm": 21.1666666667
+ },
+ {
+ "time": 10099.9999046326,
+ "pace": 138830.8712654931,
+ "hr": 95.7142857143,
+ "power": 141.31057207,
+ "distance": 48,
+ "spm": 19.8095238095
+ }
+ ]
+ }
+
+ form_data = {
+ "totalDistance": 100,
+ "elapsedTime": 29000,
+ "title": "Test Workout (GO)",
+ "startdatetime": "2023-01-16 17:54:35.588838+00:00",
+ "workouttype": "water",
+ "boattype": "1x",
+ "notes": "some\nnotes",
+ "strokes": strokedata,
+ }
+
+
+ url = reverse('strokedatajson_v3')
+
+ request = self.factory.post(url,form_data,format='json')
+ request.user = self.u
+ request.data = json.dumps(form_data)
+
+ force_authenticate(request, user=self.u)
+ with patch('rowers.dataprep.getrowdata_db') as mock_getrowdata:
+ mock_getrowdata.return_value = (pd.DataFrame(),None)
+ response = strokedatajson_v3(request)
+
+ self.assertEqual(response.status_code,200)
+ response = json.loads(response.content)
+ x = response['workout id']
+
diff --git a/rowers/urls.py b/rowers/urls.py
index 9c59e802..c9760f9a 100644
--- a/rowers/urls.py
+++ b/rowers/urls.py
@@ -247,6 +247,8 @@ urlpatterns = [
name='strokedatajson'),
re_path(r'^api/v2/workouts/(?P