diff --git a/.gitignore b/.gitignore
index d6549b8..a79ed46 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
.DS_Store
-*.iws
\ No newline at end of file
+*.iws
+node_modules
diff --git a/Node Datastore Server.iml b/Node Datastore Server.iml
index f5c4d11..b26ebe1 100644
--- a/Node Datastore Server.iml
+++ b/Node Datastore Server.iml
@@ -7,9 +7,8 @@
-
-
+
diff --git a/Node Datastore Server.ipr b/Node Datastore Server.ipr
index 656f981..25ce9f1 100644
--- a/Node Datastore Server.ipr
+++ b/Node Datastore Server.ipr
@@ -19,9 +19,7 @@
-
-
-
+
@@ -62,7 +60,6 @@
-
@@ -298,6 +295,11 @@
+
+
+
+
+
@@ -380,7 +382,6 @@
-
@@ -418,11 +419,6 @@
-
-
-
-
-
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..a9d5b91
--- /dev/null
+++ b/README.md
@@ -0,0 +1,76 @@
+Node Datastore Server
+=====================
+
+This is a simple, minimal Node.js server demonstrating communication between the BodyTrack Grapher and the BodyTrack Datastore. Not intended for use in production.
+
+The BodyTrack Datastore repository is at:
+
+ https://github.com/BodyTrack/datastore
+
+The BodyTrack Grapher repository is at:
+
+ https://github.com/BodyTrack/Grapher
+
+This project includes binaries for the Grapher, but you'll need to
+fetch and build the Datastore--see instructions below.
+
+Installation And Setup
+======================
+
+Here's what to do to get the server running:
+
+1. Fetch the BodyTrack Datastore:
+
+ git clone https://github.com/BodyTrack/datastore.git
+
+2. Follow the build and install instructions for the BodyTrack Datastore, but install it into this project's "datastore" directory. This project's "datastore" directory should be the parent to the "datastore" and "db" subdirectories.
+
+3. Install Node.js (http://nodejs.org/)
+
+4. Open a terminal window and cd to this project's root directory
+
+5. Install the dependences:
+
+ npm install
+
+6. Optionally install nodemon (https://github.com/remy/nodemon)
+
+ npm install -g nodemon
+
+7. Start the server:
+
+ node app.js
+
+ Or, if you installed nodemon, start it with:
+
+ nodemon app.js
+
+8. Open a brower and go to:
+
+ http://localhost:3000/
+
+You should see the server's home page with a message that the
+datastore is empty. See the "Uplaoding Data" section below for a
+few different ways to get data in to the datastore.
+
+Uploading Data
+==============
+
+This server supports uploading of new data. Here are a few ways
+to get data in:
+
+1. For a quick test with some sample Speck data, find the db.tgz tarball in this project's etc directory and untar it into the datastore directory. OK, fine...so this isn't *uploading*, but it gets data in there so stop complaining. :-)
+
+2. To upload using curl, make sure the server is running, open a terminal with the current directory set to this project's root, and run the following:
+
+ curl -H "Content-Type:application/json" http://localhost:3000/upload?dev_nickname=Speck -d @etc/speck_data.json
+
+Doing that will get you the exact same data as what's in the db.tgz mentioned above.
+
+3) If you're using the Speck Gateway (http://specksensor.org/), simply point the uploader at localhost:3000. This server doesn't support multiple users (all data is stored under user ID 1), so the username and password required by uploader are ignored--set them to whatever you want.
+
+Disclaimer
+==========
+
+This is my first time writing code for Node.js. I probably did a lot
+of things wrong and/or in a stupid way. Feedback welcome. Thanks.
\ No newline at end of file
diff --git a/README.txt b/README.txt
deleted file mode 100644
index 91d8e28..0000000
--- a/README.txt
+++ /dev/null
@@ -1,97 +0,0 @@
-=====================================================================
-NODE DATASTORE SERVER
----------------------------------------------------------------------
-
-This is a simple, minimal Node.js server demonstrating communication
-between the BodyTrack Grapher and the BodyTrack Datastore. Not
-intended for use in production.
-
-The BodyTrack Datastore repository is at:
-
- https://github.com/BodyTrack/datastore
-
-The BodyTrack Grapher repository is at:
-
- https://github.com/BodyTrack/Grapher
-
-This project includes binaries for the Grapher, but you'll need to
-fetch and build the Datastore--see instructions below.
-
-=====================================================================
-INSTALLATION AND SETUP
----------------------------------------------------------------------
-
-Here's what to do to get the server running:
-
-1) Fetch the BodyTrack Datastore:
-
- git clone https://github.com/BodyTrack/datastore.git
-
-2) Follow the build and install instructions for the BodyTrack
- Datastore, but install it into this project's "datastore"
- directory. This project's "datastore" directory should be
- the parent to the "datastore" and "db" subdirectories.
-
-3) Install Node.js (http://nodejs.org/)
-
-4) Open a terminal window and cd to this project's root directory
-
-5) Install the dependences:
-
- npm install
-
-6) Optionally install nodemon (https://github.com/remy/nodemon)
-
- npm install -g nodemon
-
-7) Start the server:
-
- node app.js
-
- Or, if you installed nodemon, start it with:
-
- nodemon app.js
-
-8) Open a brower and go to:
-
- http://localhost:3000/
-
-You should see the server's home page with a message that the
-datastore is empty. See the "Uplaoding Data" section below for a
-few different ways to get data in to the datastore.
-
-=====================================================================
-UPLOADING DATA
----------------------------------------------------------------------
-
-This server supports uploading of new data. Here are a few ways
-to get data in:
-
-1) For a quick test with some sample Speck data, find the db.tgz
- tarball in this project's etc directory and untar it into the
- datastore directory. OK, fine...so this isn't *uploading*, but it
- gets data in there so stop complaining. :-)
-
-2) To upload using curl, make sure the server is running, open a
- terminal with the current directory set to this project's root,
- and run the following:
-
- curl -H "Content-Type:application/json" http://localhost:3000/api/bodytrack/jupload?dev_nickname=Speck -d @etc/speck_data.json
-
- Doing that will get you the exact same data as what's in the
- db.tgz mentioned above.
-
-3) If you're using the Speck Gateway (http://specksensor.org/),
- simply point the uploader at localhost:3000. This server doesn't
- support multiple users (all data is stored under user ID 1), so
- the username and password required by uploader are ignored--set
- them to whatever you want.
-
-=====================================================================
-DISCLAIMER
----------------------------------------------------------------------
-
-This is my first time writing code for Node.js. I probably did a lot
-of things wrong and/or in a stupid way. Feedback welcome. Thanks.
-
-=====================================================================
diff --git a/app.js b/app.js
index 9f35b63..d9008c2 100644
--- a/app.js
+++ b/app.js
@@ -1,6 +1,5 @@
var express = require('express');
-var routes = require('./routes');
-var datastore = require('./routes/datastore');
+var routes = require('./routes/index');
var http = require('http');
var path = require('path');
@@ -17,7 +16,7 @@ app.use(express.compress()); // enables gzip compression
app.use(app.router);
app.use(express.static(path.join(__dirname, 'public')));
app.set("jsonp callback", true);
-app.set('json spaces',0); // setting this to 0 removes whitespace from json
+app.set('json spaces', 0); // setting this to 0 removes whitespace from json
// development only
if ('development' == app.get('env')) {
@@ -27,9 +26,10 @@ if ('development' == app.get('env')) {
app.get('/', routes.index);
app.get('/grapher/:uid', routes.index);
app.get('/grapher/:uid/:deviceNickname/:channelName', routes.index);
-app.get('/users/:uid/sources/list', datastore.listSources);
-app.get('/tiles/:uid/:deviceNickname.:channelName/:level.:offset.json', datastore.getTile);
-app.post('/api/bodytrack/jupload', datastore.uploadJson);
+app.get('/users/:uid/sources/list', routes.listSources);
+app.get('/tiles/:uid/:deviceNickname.:channelName/:level.:offset.json', routes.getTile);
+app.post('/api/bodytrack/jupload', routes.uploadJson);
+app.post('/upload', routes.uploadJson);
http.createServer(app).listen(app.get('port'), function() {
console.log('Express server listening on port ' + app.get('port'));
diff --git a/node_modules/.gitignore b/node_modules/.gitignore
deleted file mode 100644
index b459c05..0000000
--- a/node_modules/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.DS_Store
-.bin
-express
-hjs
-temp
\ No newline at end of file
diff --git a/node_modules/bodytrack-datastore/lib/bodytrack-datastore.js b/node_modules/bodytrack-datastore/lib/bodytrack-datastore.js
deleted file mode 100644
index beb793c..0000000
--- a/node_modules/bodytrack-datastore/lib/bodytrack-datastore.js
+++ /dev/null
@@ -1,247 +0,0 @@
-var exec = require('child_process').exec;
-var temp = require('temp');
-var fs = require('fs');
-
-//======================================================================================================================
-// CONSTANTS
-//======================================================================================================================
-
-const DATASTORE_EXEC_LOCATION = "./datastore/datastore";
-const DATASTORE_DB_LOCATION = "./datastore/db/dev.kvs";
-const DEFAULT_USER_ID = 1;
-
-//======================================================================================================================
-// PRIVATE
-//======================================================================================================================
-
-var buildDatastoreCommand = function(command, parameters) {
- var launchCommand = DATASTORE_EXEC_LOCATION + "/" + command + " " + DATASTORE_DB_LOCATION;
- for (var i = 0; i < parameters.length; i++) {
- var param = parameters[i];
- launchCommand += ' ';
- var part = param.toString();
- if (part.indexOf(' ') < 0) {
- launchCommand += part;
- }
- else {
- launchCommand += "\"" + part + "\"";
- }
- }
-
- return launchCommand;
-};
-
-var executeDatastoreCommand = function(commandName, parameters, callback) {
- var command = buildDatastoreCommand(commandName, parameters);
- console.log("DataStore: executing command: " + command);
- exec(command,
- function(error, stdout, stderr) {
- callback(error, stdout, stderr);
- });
-}
-
-var createChannel = function(deviceName, name, specs) {
-
- // create the default style
- var style = { "styles" : [ ] };
- if (name == "Sleep_Graph") {
- style.styles.push({
- "type" : "zeo",
- "show" : true
- });
- }
- else {
- style.styles.push({
- "type" : "line",
- "lineWidth" : 1,
- "show" : true
- });
- }
-
- // create the channel
- var channel = {
- "deviceName" : deviceName,
- "name" : name,
- "min" : specs.channel_bounds.min_value,
- "max" : specs.channel_bounds.max_value,
- "min_time" : specs.channel_bounds.min_time,
- "max_time" : specs.channel_bounds.max_time,
- "time_type" : "gmt",
- style : style,
- builtin_default_style : style
- };
-
- // set the channel type, if defined
- if (typeof specs.channelType !== 'undefined' && specs.channelType != null) {
- channel['type'] = specs.channelType;
- }
-
- if (typeof specs.objectTypeName !== 'undefined' && specs.objectTypeName != null) {
- channel['objectTypeName'] = specs.objectTypeName;
- }
-
- // time_type defaults to gmt. It can be overridden to "local" for channels that only know local time
- if (typeof specs.time_type !== 'undefined' && specs.time_type != null) {
- channel['time_type'] = specs.time_type;
- }
-
- return channel;
-};
-
-var createUploadResponse = function(wasSuccessful, payload) {
- var response = {
- "result" : (!!wasSuccessful) ? "OK" : "KO",
- "message" : (!!wasSuccessful) ? "Upload successful!" : "Upload failed"
- };
- if (typeof payload !== 'undefined' && payload != null) {
- response['payload'] = payload;
- }
-
- if (!wasSuccessful) {
- console.log("bodytrack-datastore.importJson: upload failed: " + JSON.stringify(payload));
- }
-
- return response;
-};
-
-//======================================================================================================================
-// PUBLIC
-//======================================================================================================================
-
-var listSources = function(uid, callback) {
- var parameters = ["-r", uid];
-
- executeDatastoreCommand("info", parameters,
- function(error, stdout, stderr) {
- // TODO: add error handling
-
- var infoResponse = JSON.parse(stdout);
- var sourcesMap = {};
-
- for (var fullName in infoResponse.channel_specs) {
- var specs = infoResponse.channel_specs[fullName];
- var split = fullName.split(".");
- // device.objectTypeName._comment should not generate an entry
- if (split.length > 2) {
- continue;
- }
- var deviceName = split[0];
- var objectTypeName = split[1];
-
- // make sure this device exists in the sourcesMap
- if (typeof sourcesMap[deviceName] === 'undefined') {
- sourcesMap[deviceName] = { "name" : deviceName, "channels" : []};
- }
-
- sourcesMap[deviceName].channels.push(createChannel(deviceName, objectTypeName, specs));
- }
-
- // convert the sourcesMap to an array
- var sources = [];
- for (var sourceName in sourcesMap) {
- sources.push(sourcesMap[sourceName]);
- }
-
- callback(sources);
- });
-};
-
-var getTile = function(uid, deviceNickname, channelName, level, offset, callback) {
- var parameters = [uid,
- deviceNickname + "." + channelName,
- level,
- offset];
-
- executeDatastoreCommand("gettile", parameters,
- function(error, stdout, stderr) {
- // TODO: add error handling
- var tileResponse = JSON.parse(stdout);
- if (typeof tileResponse['data'] === 'undefined') {
- // respond with an empty tile
- tileResponse = {"data" : [], "fields" : ["time", "mean", "stddev", "count"], "level" : level, "offset" : offset, "sample_width" : 0};
- }
-
- // Must set the type since the grapher won't render anything if the type is not set
- tileResponse['type'] = "value";
-
- callback(tileResponse);
- });
-};
-
-var importJson = function(uid, deviceNickname, data, callback) {
-
- temp.open('node_datastore_server_uploaded_json_data',
- function(err, info) {
- if (err) {
- callback(createUploadResponse(false, {"reason" : "failed to open file", "error" : err}));
- }
- else {
- fs.writeFile(info.path,
- JSON.stringify(data),
- function(err) {
- if (err) {
- callback(createUploadResponse(false, {"reason" : "failed to write file", "error" : err}));
- }
- else {
- fs.close(info.fd,
- function(err) {
- if (err) {
- callback(createUploadResponse(false, {"reason" : "failed to close file", "error" : err}));
- }
- else {
- var parameters = [uid,
- deviceNickname,
- "--format",
- "json",
- info.path];
-
- executeDatastoreCommand("import",
- parameters,
- function(err, stdout, stderr) {
-
- if (err) {
- callback(createUploadResponse(false, {"reason" : "failed to execute datastore import command", "error" : err}));
- }
- else {
- var datastoreResponse = null;
- var payload = null;
-
- try {
- datastoreResponse = JSON.parse(stdout);
- }
- catch (e) {
- datastoreResponse = null;
- payload = {"reason" : "failed to parse datastore import response as JSON"};
- }
-
- var wasSuccessful = datastoreResponse != null &&
- datastoreResponse.failed_records == 0 &&
- datastoreResponse.successful_records > 0;
-
- if (wasSuccessful) {
- payload = {
- "successful_records" : datastoreResponse.successful_records,
- "failed_records" : datastoreResponse.failed_records
- };
- }
-
- callback(createUploadResponse(wasSuccessful, payload));
-
- }
- });
- }
- });
- }
- });
- }
- });
-};
-
-//======================================================================================================================
-// EXPORTS
-//======================================================================================================================
-
-exports.DEFAULT_USER_ID = DEFAULT_USER_ID;
-exports.listSources = listSources;
-exports.getTile = getTile;
-exports.importJson = importJson;
diff --git a/node_modules/bodytrack-datastore/package.json b/node_modules/bodytrack-datastore/package.json
deleted file mode 100644
index 55ef5ac..0000000
--- a/node_modules/bodytrack-datastore/package.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "name" : "bodytrack-datastore",
- "description" : "BodyTrack Datastore NPM package",
- "version" : "1.0.0",
- "main" : "./lib/bodytrack-datastore",
- "homepage" : "https://github.com/BodyTrack/datastore",
- "author" : {
- "name" : "Chris Bartley",
- "email" : "bartley@cmu.edu"
- },
- "dependencies" : {
- }
-}
diff --git a/package.json b/package.json
index e97f5a1..a0a4499 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name" : "node-datastore-grapher",
- "version" : "1.0.0",
+ "version" : "1.2.0",
"author" : {
"name" : "Chris Bartley",
"email" : "bartley@cmu.edu"
@@ -13,6 +13,6 @@
"express" : "3.3.4",
"hjs" : "*",
"temp" : "*",
- "bodytrack-datastore" : "*"
+ "bodytrack-datastore" : "0.1.0"
}
}
\ No newline at end of file
diff --git a/routes/datastore.js b/routes/datastore.js
deleted file mode 100644
index 77225a8..0000000
--- a/routes/datastore.js
+++ /dev/null
@@ -1,30 +0,0 @@
-var datastore = require('bodytrack-datastore');
-
-exports.listSources = function(req, res) {
- datastore.listSources(req.params.uid,
- function(sources) {
- res.jsonp(sources);
- });
-};
-
-exports.getTile = function(req, res) {
- datastore.getTile(req.params.uid,
- req.params.deviceNickname,
- req.params.channelName,
- req.params.level,
- req.params.offset,
- function(tile) {
- res.jsonp(tile);
- });
-};
-
-exports.uploadJson = function(req, res) {
- datastore.importJson(datastore.DEFAULT_USER_ID, // for now, just always use the default user ID
- req.query.dev_nickname,
- req.body,
- function(response) {
- res.type('application/json');
- res.send(JSON.stringify(response));
- }
- );
-};
\ No newline at end of file
diff --git a/routes/index.js b/routes/index.js
index 441a47b..2803e6d 100644
--- a/routes/index.js
+++ b/routes/index.js
@@ -1,68 +1,223 @@
-var datastore = require('bodytrack-datastore');
+var BodyTrackDatastore = require('bodytrack-datastore');
+var datastore = new BodyTrackDatastore({binDir : "./datastore/datastore", dataDir : "./datastore/db/dev.kvs"});
+
+const DEFAULT_USER_ID = 1;
+
+var createChannel = function(deviceName, name, specs) {
+
+ // create the default style
+ var style = { "styles" : [ ] };
+ if (name == "Sleep_Graph") {
+ style.styles.push({
+ "type" : "zeo",
+ "show" : true
+ });
+ }
+ else {
+ style.styles.push({
+ "type" : "line",
+ "lineWidth" : 1,
+ "show" : true
+ });
+ }
+
+ // create the channel
+ var channel = {
+ "deviceName" : deviceName,
+ "name" : name,
+ "min" : specs.channel_bounds.min_value,
+ "max" : specs.channel_bounds.max_value,
+ "min_time" : specs.channel_bounds.min_time,
+ "max_time" : specs.channel_bounds.max_time,
+ "time_type" : "gmt",
+ style : style,
+ builtin_default_style : style
+ };
+
+ // set the channel type, if defined
+ if (typeof specs.channelType !== 'undefined' && specs.channelType != null) {
+ channel['type'] = specs.channelType;
+ }
+
+ if (typeof specs.objectTypeName !== 'undefined' && specs.objectTypeName != null) {
+ channel['objectTypeName'] = specs.objectTypeName;
+ }
+
+ // time_type defaults to gmt. It can be overridden to "local" for channels that only know local time
+ if (typeof specs.time_type !== 'undefined' && specs.time_type != null) {
+ channel['time_type'] = specs.time_type;
+ }
+
+ return channel;
+};
+
+function createSources(infoResponse) {
+ var sourcesMap = {};
+
+ for (var fullName in infoResponse.channel_specs) {
+ var specs = infoResponse.channel_specs[fullName];
+ var split = fullName.split(".");
+ // device.objectTypeName._comment should not generate an entry
+ if (split.length > 2) {
+ continue;
+ }
+ var deviceName = split[0];
+ var objectTypeName = split[1];
+
+ // make sure this device exists in the sourcesMap
+ if (typeof sourcesMap[deviceName] === 'undefined') {
+ sourcesMap[deviceName] = { "name" : deviceName, "channels" : []};
+ }
+
+ sourcesMap[deviceName].channels.push(createChannel(deviceName, objectTypeName, specs));
+ }
+
+ // convert the sourcesMap to an array
+ var sources = [];
+ for (var sourceName in sourcesMap) {
+ sources.push(sourcesMap[sourceName]);
+ }
+ return sources;
+}
+
+var createUploadResponse = function(wasSuccessful, payload) {
+ var response = {
+ "result" : (!!wasSuccessful) ? "OK" : "KO",
+ "message" : (!!wasSuccessful) ? "Upload successful!" : "Upload failed"
+ };
+ if (typeof payload !== 'undefined' && payload != null) {
+ response['payload'] = payload;
+ }
+
+ if (!wasSuccessful) {
+ console.log("bodytrack-datastore.importJson: upload failed: " + JSON.stringify(payload));
+ }
+
+ return response;
+};
exports.index = function(req, res) {
// Use the default user ID unless a different (valid) one is specified in the URL.
- var userId = datastore.DEFAULT_USER_ID;
+ var userId = DEFAULT_USER_ID;
if (typeof req.params.uid !== 'undefined') {
- var userIdAsInt = parseInt(req.params.uid,10);
+ var userIdAsInt = parseInt(req.params.uid, 10);
if (!isNaN(userIdAsInt) && userIdAsInt >= 0) {
userId = userIdAsInt;
}
}
- datastore.listSources(userId,
- function(sources) {
-
- var selectedDevice = '';
- var selectedChannel = '';
-
- // if the device and channel are specified in the URL, validate them
- // against the sources
- var areDeviceAndChannelValid = true;
- var channel = null;
- if (typeof req.params.deviceNickname !== 'undefined' &&
- typeof req.params.channelName !== 'undefined') {
-
- areDeviceAndChannelValid = false;
-
- for (var i = 0; i < sources.length; i++) {
- if (sources[i]['name'] == req.params.deviceNickname) {
- // we found the device, so try to find the channel
- var channels = sources[i]['channels'];
- for (var j = 0; j < channels.length; j++) {
- if (channels[j]['name'] == req.params.channelName) {
- selectedDevice = req.params.deviceNickname;
- selectedChannel = req.params.channelName;
- console.log("valid device and channel!");
- areDeviceAndChannelValid = true;
- channel = channels[j];
- break;
- }
- }
-
- break;
- }
- }
-
- }
-
- if (areDeviceAndChannelValid) {
- res.render('index',
- {
- title : 'Simple Datastore Server',
- devices : sources,
- channelJson : JSON.stringify(channel),
- userId : userId,
- selectedDevice : selectedDevice,
- selectedChannel : selectedChannel,
- isDeviceAndChannelSelected : selectedDevice != '' && selectedChannel != '',
- isDatastoreEmpty : sources.length == 0
- });
- }
- else {
- console.log("Invalid device and/or channel, redirecting...");
- res.redirect("/");
- }
-
- });
+ console.log("userId: " + userId);
+
+ datastore.getInfo(userId,
+ function(err, infoResponse) {
+
+ console.log("INFO RESPONSE: " + JSON.stringify(infoResponse, null, 3));
+ var sources = createSources(infoResponse);
+
+ var selectedDevice = '';
+ var selectedChannel = '';
+
+ // if the device and channel are specified in the URL, validate them
+ // against the sources
+ var areDeviceAndChannelValid = true;
+ var channel = null;
+ if (typeof req.params.deviceNickname !== 'undefined' &&
+ typeof req.params.channelName !== 'undefined') {
+
+ areDeviceAndChannelValid = false;
+
+ for (var i = 0; i < sources.length; i++) {
+ if (sources[i]['name'] == req.params.deviceNickname) {
+ // we found the device, so try to find the channel
+ var channels = sources[i]['channels'];
+ for (var j = 0; j < channels.length; j++) {
+ if (channels[j]['name'] == req.params.channelName) {
+ selectedDevice = req.params.deviceNickname;
+ selectedChannel = req.params.channelName;
+ console.log("valid device and channel!");
+ areDeviceAndChannelValid = true;
+ channel = channels[j];
+ break;
+ }
+ }
+
+ break;
+ }
+ }
+
+ }
+
+ if (areDeviceAndChannelValid) {
+ res.render('index',
+ {
+ title : 'Simple Datastore Server',
+ devices : sources,
+ channelJson : JSON.stringify(channel),
+ userId : userId,
+ selectedDevice : selectedDevice,
+ selectedChannel : selectedChannel,
+ isDeviceAndChannelSelected : selectedDevice != '' && selectedChannel != '',
+ isDatastoreEmpty : sources.length == 0
+ });
+ }
+ else {
+ console.log("Invalid device and/or channel, redirecting...");
+ res.redirect("/");
+ }
+ });
+};
+
+exports.listSources = function(req, res) {
+ datastore.getInfo(req.params.uid,
+ function(err, infoResponse) {
+ if (err) {
+ // TODO: do something better
+ console.log("Error returned from datastore.getInfo(): " + err);
+ res.jsonp({});
+ }
+ else {
+ res.jsonp(createSources(infoResponse));
+ }
+ });
+};
+
+exports.getTile = function(req, res) {
+ datastore.getTile(req.params.uid,
+ req.params.deviceNickname,
+ req.params.channelName,
+ req.params.level,
+ req.params.offset,
+ function(err, tile) {
+
+ if (err || typeof tile['data'] === 'undefined') {
+ // respond with an empty tile
+ tile = {"data" : [], "fields" : ["time", "mean", "stddev", "count"], "level" : req.params.level, "offset" : req.params.offset, "sample_width" : 0};
+ }
+
+ // Must set the type since the grapher won't render anything if the type is not set
+ tile['type'] = "value";
+
+ res.jsonp(tile);
+ });
+};
+
+exports.uploadJson = function(req, res) {
+ datastore.importJson(DEFAULT_USER_ID, // for now, just always use the default user ID
+ req.query.dev_nickname,
+ req.body,
+ function(err, response) {
+ var uploadResponse = null;
+ if (err) {
+ uploadResponse = createUploadResponse(false, {
+ error : err,
+ reason : err.message
+ });
+ }
+ else {
+ uploadResponse = createUploadResponse(true, response);
+ }
+ res.type('application/json');
+ res.send(JSON.stringify(uploadResponse));
+ }
+ );
};
\ No newline at end of file