Legacy BodyTrack server APIs

Table of Contents

WARNING

This is a historical document referring to a stage early in the integration of the original Fluxtream and BodyTrack projects, and is inaccurate with respect to the current state of Fluxtream.  
An updated version of this document here should be used instead.

Overview

The following are server APIs from BodyTrack which can be used to implement a continuous time view in Fluxtream.  These are currently proxied by handlers in BodyTrackController.java to a local BodyTrack server running on port 3000.  

The URLs are relative to the root of the BodyTrack server, and within a top-level /bodytrack hierarchy on the Fluxtream server.  For instance, if you want to get the views for user 3, the local BodyTrack server URL is http://localhost:3000/users/3/views, and the local Fluxtream URL would be http://localhost:8080/bodytrack/users/3/views.

If you are running a BodyTrack instance locally under mongrel (the single-threaded Ruby on Rails test server) on port 3000 this should just work.  If you want to use a remote BodyTrack server and have a login on that server, you can forward the port with the following command: 

ssh -L3000:localhost:3000 <username>@<bodytrack server>

Note: The following is a partial mirror of the original BodyTrack Web API 1.0 document which is on the private BodyTrack project wiki.  For more details or access, please contact Anne Wright

Login and session management

Log in to server and create a session

/login.json

Log into BodyTrack with the specified login name and password.  Both POST params are required:

Example:

curl --cookie-jar btsession http://localhost:3000/login.json -d login=frog -d password=frog

On success, returns:

{"name":"Frog Jones","user_id":3,"login":"frog"}

On failure, returns:

{"fail":"Invalid login parameters"}

Note:

Name is used only for display, not login, and would typically be the user's first and last name, or whatever they chose to enter.

Check session login status

/login_status.json

Example:

curl -b btsession http://localhost:3000/login_status.json

If user is logged in, returns:

{"name":"Frog Jones","user_id":3,"login":"frog"}

If user is not logged in, returns:

{"fail":"No user currently logged in"}

Logout

/logout.json

Example:

curl -b btsession http://localhost:3000/logout.json

Returns:

{"success":"User not logged in"}

Data access

Fetch tile

/tiles/UID/DeviceNickname.ChannelName/Level.Offset.json

Note that negative levels are valid;  the system is designed to work with double-precision floating point timestamps, with effective resolution of 1 ns for contemporary times

Notes:
- Negative offsets are valid and represent times before Jan 1, 1970
- A 64-bit signed integer is sufficient for storing offset, while a 32-bit integer is not.
Returns
{
   "fields":["time","mean","stddev","count"],
   "data":[
               [1320091712.148625,14249.90551181102,350.02294921875,127],
               [1320091839.904598,15580.9453125,391.9547729492188,128],
               [1320091967.904517,16568.71875,223.3632507324219,128],
               [1320092095.904574,17457.375,291.457763671875,128],
                ...
               [1320157120.046765,32857.5,26613.29296875,128]
             ],
   "level":7,
   "offset":20143,
   "sample_width":128
}

Photo Tiles

/photos/UID/DeviceNickname.ChannelName/Level.Offset.json

Requires session.  Level and offset semantics are the same as for a regular tile.

Returns:

[{"nsfw":false,"id":12,"begin_d":1340960972,"begin":"2012-06-29T05:09:32.000-04:00","dev_id":"picasa","dev_nickname":"Picasa","channel_name":"photo","url":"https://lh4.googleusercontent.com/-ERAmKTI3Wes/UDZB_kNvb7I/AAAAAAAAAKI/X3eD3YuQ5_Q/IMG_1845.JPG","tags":[],"thumbnails":[{"url":"https://lh4.googleusercontent.com/-ERAmKTI3Wes/UDZB_kNvb7I/AAAAAAAAAKI/X3eD3YuQ5_Q/s72/IMG_1845.JPG","width":72,"height":54},{"url":"https://lh4.googleusercontent.com/-ERAmKTI3Wes/UDZB_kNvb7I/AAAAAAAAAKI/X3eD3YuQ5_Q/s144/IMG_1845.JPG","width":144,"height":108},{"url":"https://lh4.googleusercontent.com/-ERAmKTI3Wes/UDZB_kNvb7I/AAAAAAAAAKI/X3eD3YuQ5_Q/s288/IMG_1845.JPG","width":288,"height":216}],"count":2},{"nsfw":false,"id":7,"begin_d":1340961674,"begin":"2012-06-29T05:21:14.000-04:00","dev_id":"picasa","dev_nickname":"Picasa","channel_name":"photo","url":"https://lh3.googleusercontent.com/-zpYMZPVA2Z0/UDZB0Zl3AFI/AAAAAAAAAJU/Ut4CmFAxSd0/IMG_1856.JPG","tags":[],"thumbnails":[{"url":"https://lh3.googleusercontent.com/-zpYMZPVA2Z0/UDZB0Zl3AFI/AAAAAAAAAJU/Ut4CmFAxSd0/s72/IMG_1856.JPG","width":54,"height":72},{"url":"https://lh3.googleusercontent.com/-zpYMZPVA2Z0/UDZB0Zl3AFI/AAAAAAAAAJU/Ut4CmFAxSd0/s144/IMG_1856.JPG","width":108,"height":144},{"url":"https://lh3.googleusercontent.com/-zpYMZPVA2Z0/UDZB0Zl3AFI/AAAAAAAAAJU/Ut4CmFAxSd0/s288/IMG_1856.JPG","width":216,"height":288}],"count":1},{"nsfw":false,"id":5,"begin_d":1340963181,"begin":"2012-06-29T05:46:21.000-04:00","dev_id":"picasa","dev_nickname":"Picasa","channel_name":"photo","url":"https://lh5.googleusercontent.com/-2V6WBDfbB7Y/UDZB0kLEQeI/AAAAAAAAAJc/J021laIZ4x0/IMG_1858.JPG","tags":[],"thumbnails":[{"url":"https://lh5.googleusercontent.com/-2V6WBDfbB7Y/UDZB0kLEQeI/AAAAAAAAAJc/J021laIZ4x0/s72/IMG_1858.JPG","width":54,"height":72},{"url":"https://lh5.googleusercontent.com/-2V6WBDfbB7Y/UDZB0kLEQeI/AAAAAAAAAJc/J021laIZ4x0/s144/IMG_1858.JPG","width":108,"height":144},{"url":"https://lh5.googleusercontent.com/-2V6WBDfbB7Y/UDZB0kLEQeI/AAAAAAAAAJc/J021laIZ4x0/s288/IMG_1858.JPG","width":216,"height":288}],"count":1}]
This method is a work-in-progress and will eventually provide all the functionality of /photos/UID/Level.Offset.json (which doesn't work at the moment anyway). The return value is an array of objects.  The url and thumbnails fields are used to download the image itself.  The begin_d is the date at which the photo should be placed on the timeline.  The nsfw flag is true for NSFW images and false for all other images.  Each of these object may also have a tags field, which is an array of strings representing the tags on the image.  Also, there is an optional count field for each image, with value equal to the count that the grapher should display to the user.  This count field is only used whenever the user is zoomed out far enough that multiple photos would overlap, so the grapher should show a count of images at that point in time.

/photos/UID/Level.Offset.json

Requires session.  Level and offset semantics are the same as for a regular tile.

The following parameters are optional.  Note that only one of the any_tags or all_tags parameters may be used within a given query:
  • dev_nickname=<name> Limit the photos returned to only be the ones from the specified data source name.  Otherwise returns photos from any data source.
  • tags = <String> Used for filtering requested photos by tags.  This is a comma-separated list of tags.
  • tag-match = <String> Used for specifying the tag matching strategy.  Supported values are "any", "all", "none", and "untagged".  Function of the values is as follows:
    • any - returns photos having at least one of the tags specified in the tags parameter
    • all - returns photos having all of the tags specified in the tags parameter
    • none - returns photos having none of the tags specified in the tags parameter
    • untagged - returns photos which have no tags
  • nsfw If present and not zero, this specifies that NSFW photos should be included in the response as well.  Otherwise, NSFW photos are not included in the response JSON.  
  • min_interval=secs Minimum time interval in floating point seconds between returned items.  If not specified the effectivemin_interval defaults to 1/20th of the tile width.  
The first item matching the specified parameters is kept.  Subsequent matching items are kept if they are >= min_intervalseconds after the previous kept item, otherwise the count on the previous kept item is just incremented.

Returns:

[{"nsfw":false,"datafile_id":null,"comment":"","begin":"2009-06-17T19:07:53-04:00","photo_file_size":2222524,"created_at":"2010-10-11T12:22:07-04:00","updated_at":"2010-11-20T08:16:59-05:00","photo_file_name":"DSCN3801.JPG","import_id":null,"photo_content_type":"image/jpeg","end_d":1245280073.0,"dev_nickname":null,"datahash":null,"tags":[],"id":1571,"end":"2009-06-17T19:07:53-04:00","dev_id":null,"dat_fields":null,"begin_d":1245280073.0,"count":2,"type":"Logphoto","user_id":1,"photo_updated_at":"2010-10-11T16:22:03-04:00"}]
The return value is an array of objects, where each object has "id", "begin_d", and "nsfw" fields.  The id is used to download the image itself, at /users/UID/logphotos/ID.width.jpg.  The begin_d is the date at which the photo should be placed on the timeline.  The nsfw flag is true for NSFW images and false for all other images.  Each of these object may also have a tags field, which is an array of strings representing the tags on the image.  Also, there is an optional count field for each image, with value equal to the count that the grapher should display to the user.  This count field is only used whenever the user is zoomed out far enough that multiple photos would overlap, so the grapher should show a count of images at that point in time.


/photos/UID/DeviceNickname.ChannelName/UnixTimeInSecs/Count

Requires session.  Level and offset semantics are the same as for a regular tile.  The UnixTimeInSecs field is a double, but the decimal portion is optional.  E-notation is allowed as well.
Used for fetching up to <Count> photos before or after the given time.  Optional parameters:
  • isBefore = <boolean> Specifies whether to fetch photos before or after the given time
  • tags = <String> Used for filtering requested photos by tags.  This is a comma-separated list of tags.
  • tag-match = <String> Used for specifying the tag matching strategy.  Supported values are "any", "all", "none", and "untagged".  Function of the values is as follows:
    • any - returns photos having at least one of the tags specified in the tags parameter
    • all - returns photos having all of the tags specified in the tags parameter
    • none - returns photos having none of the tags specified in the tags parameter
    • untagged - returns photos which have no tags
Examples:
Returns:
[{"nsfw":false,"id":57,"description":"Bridge","comment":"Roberto Clemente bridge","begin_d":1347194160,"begin":"2012-09-09T08:36:00.000-04:00","end_d":1347194160,"end":"2012-09-09T08:36:00.000-04:00","dev_id":"flickr","dev_nickname":"Flickr","object_type_name":"photo","channel_name":"photo","url":"http://farm9.static.flickr.com/8462/7981218440_6770ea380e_z.jpg","tags":[],"thumbnails":[],"count":1}]
The return value is an array of photo objects.

Fluxtream Capture Photos

/photo/PhotoStoreId

Requires session.  Returns the full-res photo referenced by the given photo store ID.  See the Fluxtream Capture Photo Architecture docs for detailed discussion about the format of the photo store ID.

Returns: A photo if the photo store ID references an image belonging to the current user (or one of the user's coachees).  Otherwise, JSON is returned with an appropriate message body and HTTP Status Code.  Example failure cases:

HTTP 403 Forbidden
{"result":"KO","message":"User [1] is not authorized to view photo [3.FluxtreamCapture.photo.2012347.1355321332000_1ac240d22be5b76400d1ee8b75b2441566782017002665044f3af781143b7c61]"}

HTTP 404 Not Found
{"result":"KO","message":"Photo [bogus] requested by user [1] not found"}

HTTP 500 Internal Server Error
{"result":"KO","message":"Exception while trying to get photo [1.FluxtreamCapture.photo.2012347.1355321332000_1ac240d22be5b76400d1ee8b75b2441566782017002665044f3af781143b7c61]"}

/photoThumbnail/UID/PhotoId/ThumbnailIndex

Requires session.  Returns the photo thumbnail referenced by the given photo ID and thumbnail Index.  The PhotoId is simply the record ID in the database's photo table.  The ThumbnailIndex is used to specify which thumbnail is desired.  We currently support only two thumbnail sizes: a value of "0" will return the small (150px) thumbnail and "1" will return the large (300px) thumbnail.  Any value in the URL for ThumbnailIndex other than "1" will return the "0" (small) thumbnail.  See the Fluxtream Capture Photo Architecture docs for more info.

Returns: A photo thumbnail if the photo ID references an image belonging to the current user (or one of the user's coachees).  Otherwise, JSON is returned with an appropriate message body and HTTP Status Code.  Example failure cases:

HTTP 403 Forbidden
{"result":"KO","message":"User [1] is not authorized to view photo [3/260/1]"}

HTTP 404 Not Found
{"result":"KO","message":"Photo [bogus] requested by user [1] not found"}

HTTP 500 Internal Server Error
{"result":"KO","message":"Exception while trying to get photo [3/260/1]"}

Views

/users/UID/views

Returns a JSON map containing all the views for a given user.  The top level map has a single key called "views" whose value is an array of view maps.  

Each view map contains the following fields:

  • name String specifying the name of the view
  • last_used Date/time of the most recent time that that view was used

Example:

http://localhost:3000/users/3/views

{"views":[{"name":"zeo-armband1","last_used":"2012-03-22T11:41:13-04:00"},{"name":"bill-zeo","last_used":"2012-03-20T06:17:55-04:00"}]}

/users/UID/views/get?name=view_name

Get a JSON map reflecting the state of the requested view

Example: 

http://localhost:3000/users/3/views/get?name=zeo-armband1

{"name":"zeo-armband1","v2":{"x_axis":{"min":1330502665.3873513,"max":1332509318.7292342},"y_axes":[{"device_name":"Zeo","channel_name":"Sleep_Graph","min":-0.4,"max":4.4,"style":{"styles":[{"type":"zeo","show":true},{"type":"value","show":false,"fillColor":"rgb(255, 0, 0)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"highlight":{"styles":[{"type":"value","show":false,"fillColor":"rgb(255, 0, 0)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"lineWidth":1},"comments":{"show":true,"styles":[{"type":"point","show":true,"lineWidth":1,"radius":3,"color":"rgb(255, 0, 0)","fill":true,"fillColor":"rgb(255, 0, 0)"}],"verticalMargin":4}},"channel_height":67},{"device_name":"Armband","channel_name":"mets","min":-6.309992592693319,"max":18.023606795840728,"style":{"comments":{"show":true,"styles":[{"type":"point","show":true,"lineWidth":1,"radius":3,"color":"rgb(0, 153, 255)","fill":true,"fillColor":"rgb(0, 153, 255)"}],"verticalMargin":4},"styles":[{"type":"line","show":true,"color":"rgb(0, 153, 255)","lineWidth":1},{"type":"lollipop","show":false,"lineWidth":1,"radius":0,"color":"rgb(0, 153, 255)","fill":false},{"type":"point","show":false,"lineWidth":1,"radius":2,"color":"rgb(0, 153, 255)","fill":true,"fillColor":"rgb(0, 153, 255)"},{"type":"value","show":false,"fillColor":"rgb(0, 153, 255)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"highlight":{"styles":[{"type":"value","show":false,"fillColor":"rgb(0, 153, 255)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"lineWidth":2}},"min_time":1292907660,"max_time":1321992960,"channel_height":67},{"device_name":"Armband","channel_name":"lying","min":-0.7389829126760572,"max":1.3868902873239444,"style":{"comments":{"show":true,"styles":[{"type":"point","show":true,"lineWidth":1,"radius":3,"color":"rgb(255, 0, 255)","fill":true,"fillColor":"rgb(255, 0, 255)"}],"verticalMargin":4},"styles":[{"type":"line","show":true,"color":"rgb(255, 0, 255)","lineWidth":1},{"type":"lollipop","show":false,"lineWidth":1,"radius":0,"color":"rgb(255, 0, 255)","fill":false},{"type":"point","show":false,"lineWidth":1,"radius":2,"color":"rgb(255, 0, 255)","fill":true,"fillColor":"rgb(255, 0, 255)"},{"type":"value","show":false,"fillColor":"rgb(255, 0, 255)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"highlight":{"styles":[{"type":"value","show":false,"fillColor":"rgb(255, 0, 255)","marginWidth":5,"verticalOffset":7,"numberFormat":"###,##0.0##"}],"lineWidth":2}},"min_time":1292864400,"max_time":1321992960,"channel_height":67}],"show_add_pane":true}}

/users/UID/views/set

Post with the following required fields to set the state of a given view:

  • name The name of the view to set.  If name matches an existing view the server updates that view to store the JSON supplied in the data field.  Otherwise the server created a new view for name containing the JSON supplied in the data field.
  • data A block of JSON data to store for this view.

See views get example above for sample values of the name and data parameters (data JSON should be like the return value from get).

Data Sources

/users/UID/sources

Returns a JSON encoded hash containing a key for each data source

Example:  http://localhost:3000/users/3/sources

{

   "sources":[

      {

         "grapherUrl":"/#timeline/source/Armband",

         "name":"Armband",

         "id":2,

         "lastSync":"Mar 22, 2012 12:46 EDT",

         "type":"body_media",

         "latestData":"Mar 21, 2012 08:41 EDT",

         "channels":[

            "mets",

            "lying"

         ]

      },

      ...

   ]

}

/users/UID/sources/list

Returns a JSON encoded hash of the data sources, channels, styles, and range data for a given user.

Example:  http://localhost:3000/users/3/sources/list

{
   "sources":[
      {
         "name":"All",
         "type":"push",
         "channels":[
            {
               "name":"comments",
               "builtin_default_style":{},
               "type":"comments",
               "style":{}
            },
            {
               "name":"photos",
               "builtin_default_style":{},
               "max":0.995803209351,
               "type":"photos",
               "min":0.634126389122,
               "style":{}
            }
         ]
      },
      {
         "name":"Armband",
         "channels":[
            {
               "name":"activityLevel",
               "builtin_default_style":{
                  "comments":{
                     "show":true
                  },
                  "styles":[{
                        "type":"line",
                        "lineWidth":1
                     }]
               },
               "max":3,
               "min_time":1292907660,
               "min":1,
               "style":{
                  "comments":{
                     "show":true
                  },
                  "styles":[
                     {
                        "type":"line",
                        "lineWidth":1
                     }
                  ]
               },
               "max_time":1332333660
            },
          ]
      },
   ]
}

/users/UID/sources/default_graph_specs?name=source_name

Returns a JSON encoded hash of the data sources, channels, styles, and range data for a given source for a given user.  Requiresname parameter to specify which source to return data for.

Example:  http://localhost:3000/users/3/sources/default_graph_specs&name=Armband

{
   "info":{
      "device":"Armband",
      "min_time":1332247260.0,
      "channels":[
         {
            "name":"mets",
            "max":10.0,
            "min":0.0,
            "style":{...}
         },
         {
            "name":"lying",
            "max":1,
            "min":0,
            "style":{...}
         }
      ],
      "max_time":1332333660.0
   }
}

Logrec Metadata

/users/UID/logrecs/LOGREC_ID/get

 
Returns a JSON encoded hash of the metadata for logrec LOGREC_ID.  
 
Example: http:/localhost:3000/users/1/logrecs/38562/get returns
 
{"nsfw":false,"datafile_id":null,"comment":"","begin":"2011-01-23T11:32:43-08:00","photo_file_size":797510,"created_at":"2011-03-22T09:28:38-07:00","updated_at":"2011-03-22T09:28:38-07:00","photo_file_name":"IMG_20110123_143243.jpg","import_id":null,"photo_content_type":"image/jpeg","end_d":1295811163.0,"dev_nickname":null,"datahash":null,"tags":["a","b","c"],"id":38562,"end":"2011-01-23T11:32:43-08:00","dev_id":null,"dat_fields":null,"begin_d":1295811163.0,"type":"Logphoto","user_id":1,"photo_updated_at":"2011-03-22T13:28:36-07:00"}

/users/UID/logrecs/LOGREC_ID/set

 
Modifies selected fields in a given logrec with id = LOGREC_ID and returns a JSON encoded hash of the metadata for that logrec.  
 
Allowed parameters, all of which are optional, are:
 
  • comment=<string> Set the comment field to the provided string
  • tags=<list of tags separated by commas> Set the tags for the logrec.  Behaves the same as /users/UID/tags/LOGREC_ID/set?tags=<value> other than having a different return value.
  • nsfw=<value> If specified, alters the value of the NSFW flag on the logrec and modifies tag list appropriately to either include an "nsfw" tag if value is true, or remove any existing "nsfw" tags if value is false.
 
Example: http://www.bodytrack.org/users/1/logrecs/38562/set?comment=new%20comment&tags=a,b,c
 
{"nsfw":false,"datafile_id":null,"comment":"new comment","begin":"2011-01-23T11:32:43-08:00","photo_file_size":797510,"created_at":"2011-03-22T09:28:38-07:00","updated_at":"2011-03-22T09:28:38-07:00","photo_file_name":"IMG_20110123_143243.jpg","import_id":null,"photo_content_type":"image/jpeg","end_d":1295811163.0,"dev_nickname":null,"datahash":null,"tags":["a","b","c"],"id":38562,"end":"2011-01-23T11:32:43-08:00","dev_id":null,"dat_fields":null,"begin_d":1295811163.0,"type":"Logphoto","user_id":1,"photo_updated_at":"2011-03-22T13:28:36-07:00"}

Facet Metadata

/metadata/UID/ConnectorName.ObjectTypeName/FACET_ID/get

Returns the metadata (currently only the tags and comment) for the facet specified by the given connector name, object type name, and facet ID.  The array of tags contains the tags in alphabetical order.

Example: curl -u test:testtest http://localhost:8080/api/bodytrack/metadata/1/FluxtreamCapture.photo/56/get

{"comment":"this is the comment","tags":["bar","foo"]}

/metadata/UID/ConnectorName.ObjectTypeName/FACET_ID/set

Sets the metadata (currently only the tags and/or comment for the facet specified by the given connector name, object type name, and facet ID.  At least one of the following parameters is required:

This method will only modify fields which are specified.  That is, if the client only specifies the comment field, then the existing tags will remain unchanged.  Likewise, if the client only specifies the tags field, then the existing comment will remain unchanged. If neither comment nor tags is specified, nothing happens, but an error is returned (see below) and the HTTP Status Code will be a 400.

Example: curl -u test:testtest --data "comment=this+is+the+comment&tags=foo,bar" http://localhost:8080/api/bodytrack/metadata/1/FluxtreamCapture.photo/56/set

{"result":"OK","message":"Metadata updated successfully!","payload":{"comment":"this is the comment","tags":["bar","foo"]}}

Example: curl -u test:testtest --data "foo=bar" http://localhost:8080/api/bodytrack/metadata/1/FluxtreamCapture.photo/56/set

{"result":"KO","message":"Nothing changed since comment and tags were both null"}


Tags

/users/{UID}/tags

Returns a JSON object containing a tags field whose value is array of tag strings.  The array of tags contains the tags in alphabetical order.  Example: {"tags": ["tag1", "tag2", "tag3"]}

/users/UID/tags/LOGREC_ID/set



/users/UID/tags/LOGREC_ID/get


Log Items

Get a JSON encoded array of log items for a given user with the following parameters:

Required:

Optional:

Channel Specs

Data upload

Data sources, devices and channels

A data source can either represent a physical device, like a cell phone or a chest strap, or any other source of data, such as from an observation capture app, Fluxtream, etc.  Each data source in the system for a given user has a unique name and may contain one or more channels.  For historical reasons, the name of the data source to upload to is generally referred to as dev_nickname.  

A data channel within a device also has a name, but that name must only be unique within the device.  

Source and channel names must include only alphanumeric characters (A-Z, a-z, 0-9) plus underscores.  If the names contain other characters they will be converted by the server to underscores.  

Formatting of time

All time fields is represented as double-precision floating point time in seconds since Jan 1, 1970 in UTC, not counting leap second (standard UNIX-style time).  Decimal seconds are allowed.  Times before 1/1/70 are representable by negative numbers.

Storing data

/api/bodytrack/upload?dev_nickname=<name>&channel_names=<channel_names>&data=<data>

POSTs data for a given source to a given user's account.  Uses basic auth for both authentication to determine which user the data is being uploaded to.  Required parameters are:

The following optional fields may also be used:

Examplecurl -i -u test:testtest http://fluxtream.org/api/bodytrack/upload -d dev_nickname=test -d channel_names='["a","b"]' -d data='[[1332754616,1,10], [1332754617,-1,20]]'

Returns:

{"max_datetime":null,"min_time":1332754616,"failed_records":0,"channel_specs":{"a":{"max_value":1,"min_value":-1,"min_time":1332754616,"channel_bounds":{"max_value":1,"min_value":-1,"min_time":1332754616,"max_time":1332754617},"imported_bounds":{"max_value":1,"min_value":-1,"min_time":1332754616,"max_time":1332754617},"max_time":1332754617},"b":{"max_value":20,"min_value":10,"min_time":1332754616,"channel_bounds":{"max_value":20,"min_value":10,"min_time":1332754616,"max_time":1332754617},"imported_bounds":{"max_value":20,"min_value":10,"min_time":1332754616,"max_time":1332754617},"max_time":1332754617}},"logrec_id":85284,"successful_records":1,"min_datetime":null,"max_time":1332754617}

Storing comments

/api/bodytrack/upload?comment=<comment>

POSTs a comment to a given user's account.  Uses basic auth for both authentication to determine which user the data is being uploaded to.  Required parameters are:

The following optional fields may also be used:

Storing photos

/api/bodytrack/upload?photo=<image in binary>

POSTs a photo to a given user's account.  Uses basic auth for both authentication to determine which user the data is being uploaded to.  Required parameters are:

The following optional fields may also be used:

Storing Fluxtream Capture photos

/photoUpload?connector_name=CONNECTOR_NAME

Accepts a photo uploaded via multipart/form-data to the authenticated user's account for the specified connector.  Currently only supports the Fluxtream Capture connector (specified as "fluxtream_capture" in the query string). The multipart form body must contain two parts:

{"capture_time_secs_utc":1355314332.000}

       {"capture_time_secs_utc":1355314332.000, "tags":"snack,fruit,strawberries", "comment":"Snacked on strawberries from my garden."}

The user need not explicitly add the Fluxtream Capture connector to his/her account prior to upload (and there's actually no way to do so).  Instead, the connector is added automatically upon first upload.  Valid photo formats are JPEG, PNG, and GIF.

See the Fluxtream Capture Photo Architecture docs for more info.

Example: curl -v -u username:password --form 'metadata={"capture_time_secs_utc":1355314332.000}' --form photo=@orientation_4.jpg http://localhost:8080/api/bodytrack/photoUpload?connector_name=fluxtream_capture;

Returns: JSON describing whether the upload was successful.  The HTTP status code will be 200 upon success, and non-200 upon failure.  Here are several examples:

HTTP 200 OK
{"result":"OK","message":"photo created sucessfully!","payload":{"operation":"created","id":42, "key":"1.FluxtreamCapture.photo.2012348.1355414332000_9c2303170b4601917c59449cdecaecaa0ccbd40b6cf26406f07316261da7fe53"}}

HTTP 200 OK
{"result":"OK","message":"photo updated sucessfully!","payload":{"operation":"updated","id":42,"key":"1.FluxtreamCapture.photo.2012347.1355311332000_0dae72b9ee44a61c1a6efe24814cb713026d58fb5af92f91e88cd2a2bf24d41c"}}

HTTP 400 Bad Request
{"result":"KO","message":"Upload failed because the connector [bogus_connector] is unknown"}

HTTP 400 Bad Request
{"result":"KO","message":"Upload failed because photo uploads are currently only allowed for the Fluxtream Capture connector"}

HTTP 400 Bad Request
{"result":"KO","message":"Upload failed because both the \u0027photo\u0027 and \u0027metadata\u0027 parts are required and must be non-null"}

HTTP 400 Bad Request
{"result":"KO","message":"Upload failed because the Multipart was null"}

HTTP 400 Bad Request
{"result":"KO","message":"InvalidDataException while trying to save the photo"}

HTTP 415 Unsupported Media Type
{"result":"KO","message":"UnsupportedImageFormatException while trying to save the photo"}

HTTP 500 Internal Server Error
{"result":"KO","message":"StorageException while trying to save the photo"}

HTTP 500 Internal Server Error
{"result":"KO","message":"Upload failed due to an unexpected exception"}