{ "metadata": { "name": "", "signature": "sha256:8dc13f12af2c61baa3d7327bac85b0078c0d6cb4beca7b087a7b191f994fc3c6" }, "nbformat": 3, "nbformat_minor": 0, "worksheets": [ { "cells": [ { "cell_type": "heading", "level": 1, "metadata": {}, "source": [ "Introduction" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
This iPython notebook walks you through reading data from a Fluxtream data channel, computing a function on it, and uploading a new computed channel back to Fluxtream. This particular example reads data from a nonin SpO2 sensor channel and writes back a thresholded channel called SpO2_thresh_X, where X is the value of a given threshold, for data points that are below that threshold.\n", "\n", "
The real value of this notebook, I hope, is to provide an example of how to read data from a Fluxtream datastore channel, do something to it, and write it back. If you have a Nonin 3150, you can import and run the Nonin-WristOx2-3150 notebook to upload your channels to try this on. Otherwise, if you email me at info@fluxtream.org I can add you as a buddy to access the Nonin 3150 channels uploaded to the test account (Guest ID=1). Or you can just modify the channel names and function to do something interesting to your own data channels.\n", "\n", "
If you are new to iPython notebooks, here is the main IP[y] website. You will need to install python and iPython notebook on your local system, run a local ipython kernel, and install a local copy of this notebook to be able to execute and modify the code below. Install instructions are here. On osx systems, you can start the server by going to Terminal and calling 'ipython notebook'. This will start a local web server and open a an IP[y] page talking to it in a web browser. Within the IP[y] page, you can open a saved iPython notebooy by going to File/Open.\n", "\n", "
Once you have IP[y] generally working on your system, here's a brief intro in how to use it: \n", "
When a given cell is executed, it may print output which appears below the cell, and the cursor will continue to the next cell. If the next cell is tall, you might need to scroll back up to see the previous cell's output.\n", "\n", "
Each cell in this notebook pertains to a particular step in the process, topic, or action requiring your input and contains comments at the top saying what it's about and what you should do. \n", "\n", "
Cells that require entry of sensitive information, such as passwords, start with a phrase like \"Execute and fill in the fields below\". These generally create entry forms in the output area below the cell that you need to fill in. Generally, the cell will clear the sensitive input boxes after clicking the button. It may also print out suggestions about how you could set up a new cell for future use if you're confident other's won't see your copy of the notebook.\n", "\n", "
Cells that require customization for your own setup start with \"Modify\". These include cells where you configure which channels you want to process and what you want to call the resulting data. These require some thought.\n", "\n", "
Cells that define functions or do other things that don't require user input or modification generally just start with \"Execute\". These can just be executed without much consideration, though you may want to go back later to understand or modify them. \n", "\n", "
Please enjoy, tinker, modify, etc. Feel free to contact info@fluxtream.org if you have questions. \n", "\n", "
Note that uploading data multiple times to a given device and channel with identical time values each time will safely overwrite the previous values. However, there is no API or user interaction component in Fluxtream that allows the deletion of a previously-uploaded device or channel, and you can't delete data points already uploaded to a given channel. If you create device names or channel names, or upload data at incorrect timepoints within a given channel, and later regret it, please send the info about your situation, including your Fluxtream username, guest ID, and the details of which devices and or channels you want deleted to info@fluxtream.org. You can get your Guest ID by doing the step below to set up your Fluxtream credentials and looking at the value of fluxtream_guest_id
. Also note that the Fluxtream upload API cannot currently handle empty cells within the data array used in an upload call. I'm hoping to fix this in the future."
]
},
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Setup for uploading to Fluxtream"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"# Execute this cell to define the functions for calling the Fluxtream upload API for the \n",
"# credentials entered below\n",
"import json, subprocess, urllib, csv\n",
"\n",
"# By default, the upload function will send data to the main server at fluxtream.org. \n",
"# If you want to have this use a different fluxtream server, change it here\n",
"# and make sure the username and password entered below are valid on that server.\n",
"global fluxtream_server\n",
"fluxtream_server = \"fluxtream.org\"\n",
"\n",
"def setup_fluxtream_credentials():\n",
" # Call the Fluxtream guest API, documented at \n",
" # https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-GettheIDfortheguest\n",
"\n",
" # Make sure it works and harvest the Guest ID for future use\n",
" global fluxtream_server, fluxtream_username, fluxtream_password, fluxtream_guest_id\n",
"\n",
" # Make sure we have fluxtream credentials set properly\n",
" if not('fluxtream_server' in globals() and \n",
" 'fluxtream_username' in globals() and\n",
" 'fluxtream_password' in globals()):\n",
" raise Exception(\"Need to enter Fluxtream credentials before uploading data. See above.\")\n",
"\n",
" cmd = ['curl', '-v']\n",
" cmd += ['-u', '%s:%s' % (fluxtream_username, fluxtream_password)]\n",
" cmd += ['https://%s/api/guest' % fluxtream_server]\n",
"\n",
" result_str = subprocess.check_output(cmd)\n",
" #print ' Result=%s' % (result_str)\n",
"\n",
" try:\n",
" response = json.loads(result_str)\n",
"\n",
" if 'id' in response:\n",
" fluxtream_guest_id = int(response['id'])\n",
" else:\n",
" raise Exception('Received unexpected response %s while trying to check credentials for %s on %s' % (response, \n",
" fluxtream_username, \n",
" fluxtream_server))\n",
"\n",
" print 'Verified credentials for user %s on %s work. Guest ID=%d' % (fluxtream_username, fluxtream_server, fluxtream_guest_id)\n",
" except:\n",
" print \"Attempt to check credentials of user %s failed\" % (fluxtream_username)\n",
" print \"Server returned response of: %s\" % (result_str)\n",
" print \"Check login to https://%s works and re-enter your Fluxtream credentials above\" % (fluxtream_server)\n",
" raise\n",
" \n",
"def fluxtream_upload(dev_nickname, channel_names, data):\n",
" global fluxtream_server, fluxtream_username, fluxtream_password\n",
" \n",
" # Make sure we have some data to send\n",
" if data == None or len(data)<1:\n",
" print 'Nothing to upload to %s %s' % (dev_nickname, channel_names) \n",
" return\n",
"\n",
" # Make sure we have fluxtream credentials set properly\n",
" if not('fluxtream_server' in globals() and \n",
" 'fluxtream_username' in globals() and\n",
" 'fluxtream_password' in globals()):\n",
" raise Exception(\"Need to enter Fluxtream credentials before uploading data. See above.\")\n",
"\n",
" # Send to BodyTrack upload API, documented at \n",
" # https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-Storingdata\n",
" cmd = ['curl', '-v']\n",
" cmd += ['-u', '%s:%s' % (fluxtream_username, fluxtream_password)]\n",
" cmd += ['-d', 'dev_nickname=%s' % dev_nickname]\n",
" cmd += ['-d', 'channel_names=%s' % json.dumps(channel_names)]\n",
" cmd += ['-d', 'data=%s' % json.dumps(data)]\n",
" cmd += ['https://%s/api/bodytrack/upload' % fluxtream_server]\n",
"\n",
" print 'Uploading %d data points to %s\\'s account on server %s, device %s, channels %s' % (len(data), \n",
" fluxtream_username,\n",
" fluxtream_server, \n",
" dev_nickname,\n",
" channel_names)\n",
" \n",
" # If you're having trouble debugging this function, uncomment the following two print statements \n",
" # to see the exact curl command and result string\n",
" #print ' Cmd=%s' % (cmd)\n",
" result_str = subprocess.check_output(cmd)\n",
" #print ' Result=%s' % (result_str)\n",
"\n",
" try:\n",
" response = json.loads(result_str)\n",
" if response['result'] != 'OK':\n",
" raise Exception('Received non-OK response %s while trying to upload to %s' % (response, dev_nickname))\n",
" \n",
" print 'Upload to %s %s (%d rows, %d to %d) succeeded' % (dev_nickname, channel_names, len(data), data[0][0], data[-1][0])\n",
" except:\n",
" print \"Attempt to upload to %s as user %s failed. Check that your credentials are ok\" % (fluxtream_server, \n",
" fluxtream_username)\n",
" print \"Server returned response: %s\" % (result_str)\n",
" raise\n",
" \n",
"# To get your own data, pass in the global fluxtream_guest_id which is computed \n",
"# in setup_fluxtream_credentials() when you execute the Fluxtream login cell.\n",
"# To get a buddy's data, you first need to figure out what their Guest ID is.\n",
"# This will show up in the Chrome developer console in tile requests when you \n",
"# look at their data in the timeline or BodyTrack app. \n",
"\n",
"# For example, if the test account is my buddy, I would select \n",
"# 'View test test's data' from the upper right \n",
"# hand menu, turn on developer tools, and go to the Fluxtream\n",
"# timeline tab. In the developer tools' network tab I would \n",
"# see fetches that look like:\n",
"# 7.21370.json\n",
"# /api/bodytrack/tiles/1/BodyMedia.activityType\n",
"# The value between 'tiles' and the device_name.channel_name is\n",
"# that account's Guest ID. In that case, I would call\n",
"# fluxtream_get_sources_list with an arg of 1.\n",
"def fluxtream_get_sources_list(guest_id):\n",
" global fluxtream_server, fluxtream_username, fluxtream_password\n",
"\n",
" # Make sure we have fluxtream credentials set properly. \n",
" if not('fluxtream_server' in globals() and \n",
" 'fluxtream_username' in globals() and\n",
" 'fluxtream_password' in globals()):\n",
" raise Exception(\"Need to enter Fluxtream credentials. See above.\")\n",
"\n",
" # Send to BodyTrack upload API, documented at \n",
" # https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-Storingdata\n",
" cmd = ['curl', '-v']\n",
" cmd += ['-u', '%s:%s' % (fluxtream_username, fluxtream_password)]\n",
" cmd += ['https://%s/api/bodytrack/users/%d/sources/list' % (fluxtream_server, guest_id)]\n",
"\n",
" result_str = subprocess.check_output(cmd)\n",
" #print ' Result=%s' % (result_str)\n",
"\n",
" try:\n",
" response = json.loads(result_str)\n",
" print 'Read of sources list for guest_id=%d succeeded' % (guest_id)\n",
" return response\n",
" except:\n",
" print \"Attempt to upload to %s as user %s failed. Check that your credentials are ok\" % (fluxtream_server, \n",
" fluxtream_username)\n",
" print \"Server returned response: %s\" % (result_str)\n",
" raise\n",
"\n",
"def fluxtream_get_device_names(sources_list):\n",
" device_names = []\n",
" for dev in sources_list['sources']:\n",
" device_names.append(dev['name'])\n",
" \n",
" return device_names\n",
"\n",
"def fluxtream_get_device_info(device_name, sources_list):\n",
" for dev in sources_list['sources']:\n",
" if(dev['name'] == device_name):\n",
" return dev\n",
" \n",
" return None\n",
"\n",
"def fluxtream_get_channel_names(device_name, sources_list):\n",
" dev_info = fluxtream_get_device_info(device_name, sources_list)\n",
"\n",
" channel_names = []\n",
" \n",
" for channel in dev_info['channels']:\n",
" channel_names.append(channel['name'])\n",
" \n",
" return channel_names\n",
"\n",
"def fluxtream_get_channel_info(device_name, channel_name, sources_list):\n",
" dev_info = fluxtream_get_device_info(device_name, sources_list)\n",
" \n",
" # Check to make sure that we found info for the requested device.\n",
" # If not, return None\n",
" if not dev_info:\n",
" return None\n",
" \n",
" for channel_info in dev_info['channels']:\n",
" if(channel_info['name'] == channel_name):\n",
" return channel_info\n",
" \n",
" return None\n",
"\n",
"# Takes a guest_id, an array of