Introduction

This iPython notebook walks you through reading data from SnoreLab CSV files

Please enjoy, tinker, modify, etc. Feel free to contact info@fluxtream.org if you have questions.

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.

Setup for uploading to Fluxtream


In [1]:
# Execute this cell to define the functions for calling the Fluxtream upload API for the 
# credentials entered below
import json, subprocess
import datetime
from dateutil import tz

def epoch_time(dt):
    epoch = datetime.datetime(1970, 1, 1, tzinfo=tz.tzutc())
    return (dt - epoch).total_seconds()    

# 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.
global fluxtream_server
fluxtream_server = "fluxtream.org"

def setup_fluxtream_credentials():
    # Call the Fluxtream guest API, documented at 
    #   https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-GettheIDfortheguest

    # Make sure it works and harvest the Guest ID for future use
    global fluxtream_server, fluxtream_username, fluxtream_password, fluxtream_guest_id

    # Make sure we have fluxtream credentials set properly
    if not('fluxtream_server' in globals() and 
           'fluxtream_username' in globals() and
           'fluxtream_password' in globals()):
        raise Exception("Need to enter Fluxtream credentials before uploading data.  See above.")

    cmd = ['curl', '-v']
    cmd += ['-u', '%s:%s' % (fluxtream_username, fluxtream_password)]
    cmd += ['https://%s/api/guest' % fluxtream_server]

    result_str = subprocess.check_output(cmd)
    #print '  Result=%s' % (result_str)

    try:
        response = json.loads(result_str)

        if 'id' in response:
            fluxtream_guest_id = int(response['id'])
        else:
            raise Exception('Received unexpected response %s while trying to check credentials for %s on %s' % (response, 
                                                                                                            fluxtream_username, 
                                                                                                            fluxtream_server))

        print 'Verified credentials for user %s on %s work. Guest ID=%d' % (fluxtream_username, fluxtream_server, fluxtream_guest_id)
    except:
        print "Attempt to check credentials of user %s failed" % (fluxtream_username)
        print "Server returned response of: %s" % (result_str)
        print "Check login to https://%s works and re-enter your Fluxtream credentials above" % (fluxtream_server)
        raise
    
def fluxtream_upload(dev_nickname, channel_names, data):
    global fluxtream_server, fluxtream_username, fluxtream_password
    
    # Make sure we have some data to send
    if data == None or len(data)<1:
        print 'Nothing to upload to %s %s' % (dev_nickname, channel_names)        
        return

    # Make sure we have fluxtream credentials set properly
    if not('fluxtream_server' in globals() and 
           'fluxtream_username' in globals() and
           'fluxtream_password' in globals()):
        raise Exception("Need to enter Fluxtream credentials before uploading data.  See above.")

    # Send to BodyTrack upload API, documented at 
    #   https://fluxtream.atlassian.net/wiki/display/FLX/BodyTrack+server+APIs#BodyTrackserverAPIs-Storingdata
    cmd = ['curl', '-v']
    cmd += ['-u', '%s:%s' % (fluxtream_username, fluxtream_password)]
    cmd += ['-d', 'dev_nickname=%s' % dev_nickname]
    cmd += ['-d', 'channel_names=%s' % json.dumps(channel_names)]
    cmd += ['-d', 'data=%s' % json.dumps(data)]
    cmd += ['https://%s/api/bodytrack/upload' % fluxtream_server]

    result_str = subprocess.check_output(cmd)
    #print '  Result=%s' % (result_str)

    try:
        response = json.loads(result_str)
        if response['result'] != 'OK':
            raise Exception('Received non-OK response %s while trying to upload to %s' % (response, dev_nickname))
        
        print 'Upload to %s %s (%d rows, %d to %d) succeeded' % (dev_nickname, channel_names, len(data), data[0][0], data[-1][0])
    except:
        print "Attempt to upload to %s as user %s failed. Check that your credentials are ok" % (fluxtream_server, 
                                                                                                 fluxtream_username)
        print "Server returned response: %s" % (result_str)
        raise

In [2]:
# Execute and fill in the fields below to set your Fluxtream credentials.  

from IPython.html import widgets # Widget definitions
from IPython.display import display # Used to display widgets in the notebook

def set_fluxtream_password(this):
    global fluxtream_username, fluxtream_password
    fluxtream_username = fluxtream_username_widget.value
    fluxtream_password = fluxtream_password_widget.value
    fluxtream_password_widget.value = ''
    setup_fluxtream_credentials()

    print "To make persistent for future restarts, insert a cell, paste in:"
    print ""
    print "global fluxtream_username, fluxtream_password"
    print "fluxtream_username = \"%s\"" % (fluxtream_username)
    print "fluxtream_password = \"xxx\""
    print "setup_fluxtream_credentials()"
    print ""
    print "replace xxx with your password, and execute that cell instead."
    print "Only do this if you're keeping this copy of your iPython notebook private,"
    print "and remove that cell before sharing"    
    
display(widgets.HTMLWidget(value='Fluxtream Username'))
fluxtream_username_widget = widgets.TextWidget()
display(fluxtream_username_widget)
display(widgets.HTMLWidget(value='Fluxtream Password'))
fluxtream_password_widget = widgets.TextWidget()
display(fluxtream_password_widget)

set_fluxtream_login_button = widgets.ButtonWidget(description='Set Fluxtream credentials')
set_fluxtream_login_button.on_click(set_fluxtream_password)
display(set_fluxtream_login_button)

# Enter Fluxtream username and password and click "Set Fluxtream credentials" button.  
# Password field will blank afterwards, but variables will be set


Verified credentials for user rsargent on fluxtream.org work. Guest ID=14
To make persistent for future restarts, insert a cell, paste in:

global fluxtream_username, fluxtream_password
fluxtream_username = "rsargent"
fluxtream_password = "xxx"
setup_fluxtream_credentials()

replace xxx with your password, and execute that cell instead.
Only do this if you're keeping this copy of your iPython notebook private,
and remove that cell before sharing

In [8]:
import datetime, exifread, glob, json, os, pprint, subprocess
from dateutil import tz

# Keep track of where you save the files, and enter in the next section
import httplib, urllib, time, base64, string, datetime, json, csv, calendar
from dateutil import tz
from dateutil import parser

def epoch_time(dt):
    epoch = datetime.datetime(1970, 1, 1, tzinfo=tz.tzutc())
    return (dt - epoch).total_seconds()    

# Returns 2D array of data suitable for posting to Fluxtream API
def snorelab_csv_to_post_data(filename):
    reader = csv.reader(open(filename,'rb'), delimiter=',')

    # skip 4 rows of headers
    for i in range(0,5):
        header = reader.next()

    rowcount = 0;
    data = []
    
    for row in reader:
        # In snorelab, the zeroth column is the date of the start of sleep, the first column is the time of start 
        # of sleep, and the second column is the time of end of sleep.  If time of end of sleep < time of start of sleep
        # then the date of end of sleep is going to be the following day.  
        start_date_str= row[0]
        start_time_str= row[1]
        end_time_str = row[2]
        start_dt = datetime.datetime.strptime("%s %s" % (start_date_str, start_time_str), '%d-%b-%Y %H:%M').replace(tzinfo=tz.tzlocal())
        end_dt = datetime.datetime.strptime("%s %s" % (start_date_str, end_time_str), '%d-%b-%Y %H:%M').replace(tzinfo=tz.tzlocal())
        
        if(end_dt<start_dt):
            end_dt += datetime.timedelta(days=1)
            
        snorescore=float(row[6])
        comment = row[8]
        data.append([epoch_time(end_dt), snorescore, comment])
        
    
    return data

In [9]:
filename="/Users/anne/education/bodytrack/data/snorelab/randy/SnoreLab Data Export 31-Mar-2015.csv"

In [10]:
data = snorelab_csv_to_post_data(filename)
#fluxtream_upload("SnoreLab", ["SnoreScore", "SnoreScore._comment"], data)
fluxtream_upload("SnoreLab", ["SnoreScore", "Remedies"], data)


Upload to SnoreLab ['SnoreScore', 'Remedies'] (67 rows, 1415358000 to 1427799120) succeeded

In [ ]: