In [ ]:
import base64, json, subprocess, urllib, urllib2, csv
from IPython.display import HTML
In [2]:
# By default, the upload function will send data to the main server at fluxtream.org.
# If you want to have this use a different fluxtream server, change it here
# and make sure the username and password entered below are valid on that server.
fluxtream_server = 'fluxtream.org'
def fluxtream_authenticate(username, password):
global fluxtream_server, fluxtream_username, fluxtream_password, fluxtream_guest_id
fluxtream_username = username
fluxtream_password = password
fluxtream_guest_id = None
try:
req = urllib2.Request('http://%s/api/guest' % fluxtream_server,
headers = fluxtream_headers())
response = json.loads(urllib2.urlopen(req).read())
fluxtream_guest_id = int(response['id'])
except urllib2.HTTPError, e:
print 'Failed to authenticate (server says %s)' % e
return False
print 'Verified credentials for user %s on %s (guest ID=%d)' % (fluxtream_username, fluxtream_server, fluxtream_guest_id)
return True
def fluxtream_headers():
auth = base64.encodestring('%s:%s' % (fluxtream_username, fluxtream_password)).replace('\n', '')
return {'Authorization': 'Basic ' + auth}
def fluxtream_confirm_authenticated():
global fluxtream_guest_id
if fluxtream_guest_id == None:
raise Exception('Need to enter Fluxtream credentials before connecting.')
def fluxtream_upload(dev_nickname, channel_names, data):
post_data = {
'dev_nickname': dev_nickname,
'channel_names': json.dumps(channel_names),
'data': json.dumps(data)
}
print 'Uploading %d data points to %s\'s account on server %s, device %s, channels %s' % (len(data),
fluxtream_username,
fluxtream_server,
dev_nickname,
channel_names)
req = urllib2.Request('https://%s/api/bodytrack/upload' % fluxtream_server,
data = urllib.urlencode(post_data),
headers = fluxtream_headers())
response = urllib2.urlopen(req).read()
if json.loads(response)['result'] != 'OK':
raise Exception('fluxtream_upload: expected OK, got %s' % response)
return True
# To get your own data, pass in the global fluxtream_guest_id which is computed
# in setup_fluxtream_credentials() when you execute the Fluxtream login cell.
# To get a buddy's data, you first need to figure out what their Guest ID is.
# This will show up in the Chrome developer console in tile requests when you
# look at their data in the timeline or BodyTrack app.
# For example, if the test account is my buddy, I would select
# 'View test test's data' from the upper right
# hand menu, turn on developer tools, and go to the Fluxtream
# timeline tab. In the developer tools' network tab I would
# see fetches that look like:
# 7.21370.json
# /api/bodytrack/tiles/1/BodyMedia.activityType
# The value between 'tiles' and the device_name.channel_name is
# that account's Guest ID. In that case, I would call
# fluxtream_get_sources_list with an arg of 1.
# TODO: patch this back into Fluxtream-Library
def fluxtream_get_sources_list(guest_id):
req = urllib2.Request('http://%s/api/bodytrack/users/%d/sources/list' % (fluxtream_server, guest_id),
headers = fluxtream_headers())
return json.loads(urllib2.urlopen(req).read())
def fluxtream_get_device_names(sources_list):
device_names = []
for dev in sources_list['sources']:
device_names.append(dev['name'])
return device_names
def fluxtream_get_device_info(device_name, sources_list):
for dev in sources_list['sources']:
if(dev['name'] == device_name):
return dev
return None
def fluxtream_get_channel_names(device_name, sources_list):
dev_info = fluxtream_get_device_info(device_name, sources_list)
channel_names = []
for channel in dev_info['channels']:
channel_names.append(channel['name'])
return channel_names
def fluxtream_get_channel_info(device_name, channel_name, sources_list):
dev_info = fluxtream_get_device_info(device_name, sources_list)
# Check to make sure that we found info for the requested device.
# If not, return None
if not dev_info:
return None
for channel_info in dev_info['channels']:
if(channel_info['name'] == channel_name):
return channel_info
return None
# Takes a guest_id, an array of <device_name>.<channel_name> strings, and a time range and returns a CSV reader.
# Iterate over the rows using reader.next(), which returns a row array with entries corresponding to
# Epoch, [dev_ch_names]
# Where Epoch is the epoch timestamp (aka unixtime) for the values in the row, and the i+1'th column of the row
# corresponds to the channel in dev_ch_names[i]
# See comment on fluxtream_get_sources_list for info about how to choose the value for guest_id
def fluxtream_get_csv(guest_id, dev_ch_names, start_time, end_time):
global fluxtream_server
fluxtream_confirm_authenticated()
# Send to BodyTrack upload API, documented at
# https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-Storingdata
# Need to convert the dev_ch_names array into json and URL encode it to create the channels arg
# TODO: how do we confirm that dev_ch_names is in fact an array?
ch_spec_str = json.dumps(dev_ch_names)
ch_spec_str = urllib.quote(ch_spec_str)
url = ('https://%s/api/bodytrack/exportCSV/%d/fluxtream-export-from-%d-to-%d.csv?channels=%s' %
(fluxtream_server, guest_id, int(start_time), int(end_time), ch_spec_str))
try:
req = urllib2.Request(url, headers = fluxtream_headers())
result_str = urllib2.urlopen(req).read()
except urllib2.HTTPError, e:
raise Exception('Failed to authenticate (server says %s)' % e)
# If the API call worked, result_str should be a CSV file
# with the first line a header consisting of EpochTime, [dev_ch_names]
# Create a CSV reader that iterates over the lines of the response
csv_reader = csv.reader(result_str.splitlines(), delimiter=',')
header = csv_reader.next()
# Do some checks to make sure we got something reasonable
if len(header) != len(dev_ch_names)+1:
raise Exception("Expected header for CSV export of %s to contain %d columns, but only found %d. Please double check that dev_ch_names are all valid" % (dev_ch_names, len(dev_ch_names)+1, len(header)))
# Check the columns are what we expect
for i in range(0,len(dev_ch_names)):
if(dev_ch_names[i] != header[i+1]):
raise Exception("Expected column %d of CSV header to be %s, but found %s instead. Please double check that dev_ch_names are all valid" % (i+1, dev_ch_names[i], header[i+1]))
# At this point, we can be confident that the columns map to Epoch, [dev_ch_names] as expected.
# Return the csv reader. Iterate over the rows using reader.next()
return csv_reader
def fluxtream_login():
return HTML("""
Fluxtream username: <input id="fluxtream_username" type="text"></input><br>
Fluxtream password: <input id="fluxtream_password" type="password"></input><br>
<button id = "fluxtream_authenticate">Authenticate</button> <span id="fluxtream_auth_result"></span>
<script>
// Send username and password to python
function fluxtream_authenticate(username, password, callback) {
cmd = 'fluxtream_authenticate(' + JSON.stringify(username) + ',' +
JSON.stringify(password) + ')'
console.log(cmd);
function cb(msg) {
console.log(msg);
console.log(msg.content.data['text/plain'] == 'True');
callback(msg.content.data['text/plain'] == 'True')
}
IPython.notebook.kernel.execute(cmd,
{iopub: {output: cb}}, {silent: false});
}
$('#fluxtream_authenticate').click(function() {
$('#fluxtream_auth_result').text('...');
fluxtream_authenticate($('#fluxtream_username').val(),
$('#fluxtream_password').val(),
function(success) {
$('#fluxtream_auth_result').text(success ? 'Success' : 'Failed');
});
});
</script>
""")
In [ ]: