From 82d1418bcbaeaab49bd0c2ce7d809547f2ac15db Mon Sep 17 00:00:00 2001 From: Sander Roosendaal Date: Wed, 21 Oct 2020 08:55:08 +0200 Subject: [PATCH] better API doc --- rowers/serializers.py | 1 + rowers/templates/developers.html | 586 +++++++++++++++++++++++++------ rowers/urls.py | 2 +- rowers/views/apiviews.py | 4 +- 4 files changed, 481 insertions(+), 112 deletions(-) diff --git a/rowers/serializers.py b/rowers/serializers.py index 31ba133c..7bc3e8e9 100644 --- a/rowers/serializers.py +++ b/rowers/serializers.py @@ -114,6 +114,7 @@ class VirtualRaceSerializer(serializers.ModelSerializer): fields = ( 'id', 'name', + 'course', 'registration_closure', 'evaluation_closure', 'start_time', diff --git a/rowers/templates/developers.html b/rowers/templates/developers.html index 20492942..bebe6949 100644 --- a/rowers/templates/developers.html +++ b/rowers/templates/developers.html @@ -147,126 +147,492 @@

The workout summary data and the stroke data are obtained and sent separately.

-

Challenges are accessed through /rowers/api/challenges/ - and /rowers/api/challenges/{id} where {id} is the ID of the challenge. Allowed method - is GET.

-

Entries are accessed through /rowers/api/entries/ - and /rowers/api/entries/{id} where {id} is the ID of the entry. - Allowed methods are GET.

-

Charts are accessed through /rowers/api/charts/ - and /rowers/api/charts/{id} where {id} is the ID of the chart. - Allowed method are GET, DELETE.

-

Courses are accessed through /rowers/api/geocourses/ - and /rowers/api/geocourses/{id} where {id} is the ID of the Course. - Allowed method are GET.

-

Course Standard Collections are accessed through /rowers/api/standardcollections/ - and /rowers/api/standardcollections/{id} where {id} is the ID of the Standard Collection. - Allowed method are GET.

-

Individual Course Standards are accessed through /rowers/api/standards/{id} where {id} is the ID of the Course Standard. - Allowed method are GET.

-

Workouts are accessed through /rowers/api/workouts/ - and /rowers/api/workouts/{id} where {id} is the ID of the Workout. - Allowed method are GET, POST, DELETE.

- - - -

POST stroke data - API

- -

You can only post stroke data to an existing workout with - workout number {id}. If the workout already has stroke data, you - will get a duplication error. This functionality will be expanded in the - future to enable updating stroke data. Stroke data for workout {id} are - posted to:

- -

API v1

- -

-

-

- -

The payload is application/json data and looks as follows:

- -

-    {
-  "distance": [5,12,19,27,35,43,51,59,67,75,82,90,100],
-  "power": [112,221,511,673,744,754,754,749,729,729,726,709,707],
-  "hr": [132,131,131,132,133,136,139,142,145,147,150,152,153],
-  "pace": [145800,116400,88100,80400,77700,77400,77400,77600,78300,78300,78400,79000,79100],
-  "spm": [11,41,56,59,55,48,48,48,48,48,48,48,49],
-  "time": [0,2200,4599,7000,9599,12000,14400,16799,19400,21799,24200,26599,2900],
+    

+ In the table below {id} indicates the ID of the item. + + + + + + + + + + + + -

API v2

+ + + + + + -

-

    -
  • https://{{ request.get_host }}/rowers/api/v2/workouts/{id}/strokedata
  • -
-

+ + + + + + -

The payload is application/json data and looks as follows:

+ + + + + + -

-    {"data":
-    [
-    {"distance":5, "power": 112, "hr": 132, "pace": 145800, "spm": 11, "time": 0},
-    {"distance":12, "power": 221, "hr": 131, "pace": 116400, "spm": 41, "time": 2200},
-    {"distance":19, "power": 511, "hr": 131, "pace": 88100, "spm": 56, "time": 4599},
-    {"distance":27, "power": 673, "hr": 132, "pace": 80400, "spm": 59, "time": 7000},
-    {"distance":35, "power": 744, "hr": 133, "pace": 77700, "spm": 55, "time": 9599},
-    {"distance":43, "power": 754, "hr": 136, "pace": 77400, "spm": 48, "time": 12000},
-    {"distance":51, "power": 754, "hr": 139, "pace": 77400, "spm": 48, "time": 14400},
-    {"distance":59, "power": 749, "hr": 142, "pace": 77600, "spm": 48, "time": 16799},
-    {"distance":67, "power": 729, "hr": 145, "pace": 78300, "spm": 48, "time": 19400},
-    {"distance":75, "power": 729, "hr": 147, "pace": 78300, "spm": 48, "time": 21799},
-    {"distance":82, "power": 726, "hr": 150, "pace": 78400, "spm": 48, "time": 24200},
-    {"distance":90, "power": 709, "hr": 152, "pace": 79000, "spm": 48, "time": 26599},
-    {"distance":100, "power": 707, "hr": 153, "pace": 79100, "spm": 49, "time": 29000},
+          
+ + + + + -

For both v1 and v2, mandatory data fields are:

-

-

    -
  • time: Time (milliseconds since workout start)
  • -
  • distance: Distance (meters)
  • -
  • pace: Pace (milliseconds per 500m)
  • -
  • spm Stroke rate (strokes per minute)
  • -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + +
ItemAccess PointsPermissionsExample
Workout/rowers/api/workouts/ +
+ /rowers/api/workouts/{id}
GET, POST, DELETE +
{
+    "id": 18,
+    "name": "2x",
+    "date": "2020-10-15",
+    "workouttype": "water",
+    "starttime": "08:40:50",
+    "distance": 9751,
+    "duration": "00:52:40",
+    "averagehr": 104,
+    "maxhr": 122,
+    "notes": "",
+    "summary": "Workout Summary - media/97ec07a851-20201015-192157-95763ffd-fd4e-4b5f-bf5f-8a8495c16639.csv\n--|Total|-Total----|--Avg--|-Avg-|Avg-|-Avg-|-Max-|-Avg\n--|Dist-|-Time-----|-Pace--|-Pwr-|SPM-|-HR--|-HR--|-DPS\n--|09751|00:52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\nW-|09751|00:52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\nR-|00000|00:00:00.0|00:00.0|000.0|00.0|000.0|122.0|00.0\nWorkout Details\n#-|SDist|-Split-|-SPace-|-Pwr-|SPM-|AvgHR|MaxHR|DPS-\n00|09751|52:40.0|02:42.0|173.0|18.0|104.4|122.0|10.3\n",
+    "boattype": "2x",
+    "timezone": "Europe/Amsterdam",
+    "forceunit": "N",
+    "inboard": 0.88,
+    "oarlength": 2.89,
+    "privacy": "visible",
+    "rankingpiece": false
 }
-    

+ +
Favorite Chart/rowers/api/charts/ +
+ /rowers/api/charts/{id}
GET, DELETE +
{
+    "id": 7,
+    "xparam": "time",
+    "yparam1": "hr",
+    "yparam2": "power",
+    "plottype": "line",
+    "workouttype": "otw",
+    "reststrokes": true,
+    "user": 1
+}
+              
+
Challenge/rowers/api/challenges/ +
+ /rowers/api/challenges/{id}
GET +
+{
+    "id": 1,
+    "name": "Alphen",
+    "course": 1,
+    "registration_closure": "2020-10-18T08:45:00+02:00",
+    "evaluation_closure": "2020-10-18T12:16:00+02:00",
+    "start_time": "08:45:00",
+    "end_time": "12:00:00",
+    "country": "Nederland",
+    "timezone": "Europe/Amsterdam",
+    "contact_phone": "",
+    "contact_email": "",
+    "entries": [
+        {
+            "id": 1,
+            "username": "Sander Roosendaal",
+            "teamname": null,
+            "boattype": "1x",
+            "sex": "male",
+            "age": 48,
+            "adaptiveclass": "None",
+            "skillclass": "Open",
+            "coursecompleted": false,
+            "distance": 0,
+            "duration": "00:00:00",
+            "points": 0.0,
+            "entrycategory": null
+        }
+    ],
+    "coursestandards": null
+}
+
+
Challenge Entry/rowers/api/entries/ +
+ /rowers/api/entries/{id}
GET, POST +
{
+    "id": 1,
+    "teamname": null,
+    "adaptiveclass": "None",
+    "skillclass": "Open",
+    "race": {
+        "id": 1,
+        "name": "Alphen",
+        "course": 1,
+        "registration_closure": "2020-10-18T08:45:00+02:00",
+        "evaluation_closure": "2020-10-18T12:16:00+02:00",
+        "start_time": "08:45:00",
+        "end_time": "12:00:00",
+        "country": "Nederland",
+        "timezone": "Europe/Amsterdam",
+        "contact_phone": "",
+        "contact_email": "",
+        "entries": [
+            {
+                "id": 1,
+                "username": "Sander Roosendaal",
+                "teamname": null,
+                "boattype": "1x",
+                "sex": "male",
+                "age": 48,
+                "adaptiveclass": "None",
+                "skillclass": "Open",
+                "coursecompleted": false,
+                "distance": 0,
+                "duration": "00:00:00",
+                "points": 0.0,
+                "entrycategory": null
+            }
+        ],
+        "coursestandards": null
+    },
+    "distance": 0,
+    "duration": "00:00:00",
+    "points": 0.0,
+    "boattype": "1x",
+    "sex": "male",
+    "age": 48,
+    "entrycategory": null
+}
+              
+
Course/rowers/api/geocourses/ +
+ /rowers/api/geocourses/{id}
GET + Example shortened for brevity (omitted gates 2-14): +
+{
+    "id": 1,
+    "name": "Alphen - Alphen aan den Rijn",
+    "distance": 14847,
+    "country": "Nederland",
+    "notes": "\n\n",
+    "polygons": [
+    {
+            "id": 1,
+            "name": "Start",
+            "order_in_course": 0,
+            "points": [
+                {
+                    "id": 1,
+                    "latitude": 52.14611068342334,
+                    "longitude": 4.704149601313898,
+                    "order_in_poly": 0
+                },
+                {
+                    "id": 2,
+                    "latitude": 52.14606840788696,
+                    "longitude": 4.704648516706039,
+                    "order_in_poly": 1
+                },
+                {
+                    "id": 3,
+                    "latitude": 52.14626893773362,
+                    "longitude": 4.704642182077736,
+                    "order_in_poly": 2
+                },
+                {
+                    "id": 4,
+                    "latitude": 52.14628828501986,
+                    "longitude": 4.704151599747837,
+                    "order_in_poly": 3
+                },
+                {
+                    "id": 5,
+                    "latitude": 52.14611068342334,
+                    "longitude": 4.704149601313898,
+                    "order_in_poly": 4
+                }
+            ]
+        },
+        {
+            "id": 15,
+            "name": "Finish",
+            "order_in_course": 14,
+            "points": [
+                {
+                    "id": 71,
+                    "latitude": 52.1461319705247,
+                    "longitude": 4.70414987291546,
+                    "order_in_poly": 0
+                },
+                {
+                    "id": 72,
+                    "latitude": 52.14607111930849,
+                    "longitude": 4.704561170436561,
+                    "order_in_poly": 1
+                },
+                {
+                    "id": 73,
+                    "latitude": 52.14626893773362,
+                    "longitude": 4.704642182077736,
+                    "order_in_poly": 2
+                },
+                {
+                    "id": 74,
+                    "latitude": 52.14628831020436,
+                    "longitude": 4.70415735390207,
+                    "order_in_poly": 3
+                },
+                {
+                    "id": 75,
+                    "latitude": 52.1461319705247,
+                    "longitude": 4.70414987291546,
+                    "order_in_poly": 4
+                }
+            ]
+        }
     ]
 }
-    

+ +
Course Time Standard Collection/rowers/api/standardcollections/ +
+ /rowers/api/standarcollections/{id}
GET + Example shortened for brevity (showing only first three): +
{
+    "id": 1,
+    "name": "Charles River Times Standards",
+    "notes": "",
+    "active": true,
+    "standards": [
+        {
+            "id": 1,
+            "name": "M1x",
+            "coursedistance": 4700,
+            "coursetime": "17:15.0",
+            "agemin": 0,
+            "agemax": 120,
+            "boatclass": "water",
+            "boattype": "1x",
+            "sex": "male",
+            "weightclass": "hwt",
+            "adaptiveclass": "None",
+            "skillclass": "Open",
+            "standardcollection": 1
+        },
+        {
+            "id": 2,
+            "name": "MLW1x",
+            "coursedistance": 4700,
+            "coursetime": "17:15.0",
+            "agemin": 0,
+            "agemax": 120,
+            "boatclass": "water",
+            "boattype": "1x",
+            "sex": "male",
+            "weightclass": "lwt",
+            "adaptiveclass": "None",
+            "skillclass": "Open",
+            "standardcollection": 1
+        },
+        {
+            "id": 3,
+            "name": "MYouth1x",
+            "coursedistance": 4700,
+            "coursetime": "18:30.0",
+            "agemin": 15,
+            "agemax": 18,
+            "boatclass": "water",
+            "boattype": "1x",
+            "sex": "male",
+            "weightclass": "hwt",
+            "adaptiveclass": "None",
+            "skillclass": "Open",
+            "standardcollection": 1
+        },
+    ]
+}
+              
+
Individual Course Time Standard/rowers/api/standards/ +
+ /rowers/api/standards/{id}
GET, POST + +
{
+    "id": 1,
+    "name": "M1x",
+    "coursedistance": 4700,
+    "coursetime": "17:15.0",
+    "agemin": 0,
+    "agemax": 120,
+    "boatclass": "water",
+    "boattype": "1x",
+    "sex": "male",
+    "weightclass": "hwt",
+    "adaptiveclass": "None",
+    "skillclass": "Open",
+    "standardcollection": 1
+}
+              
+
Stroke Data (v1)/rowers/api/workouts/{id}/strokedata + GET, POST +
{
+      "distance": [23, 46, 48],
+      "time": [3200, 6700, 10099],
+      "spm": [16.4, 21.2, 19.8],
+      "pace": [155068, 144402, 138830],
+      "power": [84,6, 117.2, 141.3],
+      "hr": [85, 91, 95]
+              }
+              
+ You can only post stroke data to an existing workout with + workout number {id}. If the workout already has stroke data, you + will get a duplication error. This functionality will be expanded in the + future to enable updating stroke data. +
+

For both v1 and v2, 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)
  • + +
+

+

For both API versions, 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.

+
Stroke Data (v2)/rowers/api/v2/workouts/{id}/strokedata + GET, POST +
[
+    "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
+      }
+    ]
+]
+              
+ You can only post stroke data to an existing workout with + workout number {id}. If the workout already has stroke data, you + will get a duplication error. This functionality will be expanded in the + future to enable updating stroke data. +
+

For both v1 and v2, 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)
  • + +
+

+

For both API versions, 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.

+

-

Optional data fiels are:

-

-

-

-

For both API versions, 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/urls.py b/rowers/urls.py index f395f590..fe50d571 100644 --- a/rowers/urls.py +++ b/rowers/urls.py @@ -115,7 +115,7 @@ class EntryViewSet(viewsets.ModelViewSet): except TypeError: return [] - http_method_names = ['get'] + http_method_names = ['get','post'] permission_classes = ( IsCompetitorOrNot, diff --git a/rowers/views/apiviews.py b/rowers/views/apiviews.py index 6bc75bab..1ba5441d 100644 --- a/rowers/views/apiviews.py +++ b/rowers/views/apiviews.py @@ -107,8 +107,10 @@ def strokedatajson_v2(request,id): logfile.write(request.user.username+"(GET) \n") data = datadf.to_json(orient='records') + data2 = json.loads(data) + data2 = {"data":data2} - return JSONResponse(data) + return JSONResponse(data2) if request.method == 'POST': with open('media/apilog.log','a') as logfile: