From d9e1ebab2f840d3edc1592446e3c3eb91b331c45 Mon Sep 17 00:00:00 2001 From: Simon Diemert Date: Thu, 25 Jun 2015 14:41:07 -0700 Subject: [PATCH] reformats on some files; updated demographicsPyramidVis to use groups instead of older clinic. --- lib/auth.js | 144 ++-- lib/middleware.js | 640 +++++++++--------- lib/routes.js | 295 +++++---- visualizations/BarVis.html | 560 ++++++++-------- visualizations/DemographicsPyramidVis.html | 736 +++++++++++---------- 5 files changed, 1246 insertions(+), 1129 deletions(-) diff --git a/lib/auth.js b/lib/auth.js index f8e9018..020c813 100644 --- a/lib/auth.js +++ b/lib/auth.js @@ -1,9 +1,9 @@ 'use strict'; -var async = require('async'), - _ = require('lodash'), - logger = require('./logger'), +var async = require('async'), + _ = require('lodash'), + logger = require('./logger'), request = require('request'), - util = require('util'); + util = require('util'); function getBakedCookie(juri, user, pass, next) { @@ -11,27 +11,27 @@ function getBakedCookie(juri, user, pass, next) { request .post({ - url: process.env.AUTH_CONTROL_URL + '/auth/login', + url : process.env.AUTH_CONTROL_URL + '/auth/login', form: { - juri: juri, - user: user, - pass: pass, + juri : juri, + user : user, + pass : pass, respond: "1" }, json: true - }, function(err, res, body){ + }, function (err, res, body) { - if(err){ + if (err) { handleAuthResponse(404, null, null, next) - }else if ( res && body && body.data ){ + } else if (res && body && body.data) { handleAuthResponse(err, res, body, next) - }else{ + } else { handleAuthResponse(err, res, body, next) @@ -40,22 +40,22 @@ function getBakedCookie(juri, user, pass, next) { }); } -/** -* Determines whether a cookie is valid by passing it to -* the auth component for verification. -* -* -* @param bakedCookie {String} - the cookie that was passed from the browser -* @param next {Function} - The function to call after we are done the work here. -* Function should have prototype like: Function foo(err, data) -* - Where err is the status code returned from the auth component and data is the data object. -* - If there was an error the data field will be set to null. -* - If there was no error (a valid response) then the error field will be null. -*/ +/** + * Determines whether a cookie is valid by passing it to + * the auth component for verification. + * + * + * @param bakedCookie {String} - the cookie that was passed from the browser + * @param next {Function} - The function to call after we are done the work here. + * Function should have prototype like: Function foo(err, data) + * - Where err is the status code returned from the auth component and data is the data object. + * - If there was an error the data field will be set to null. + * - If there was no error (a valid response) then the error field will be null. + */ function verifyAuth(bakedCookie, next) { request.post({ - url: process.env.AUTH_CONTROL_URL + '/verify', + url : process.env.AUTH_CONTROL_URL + '/verify', form: { bakedCookie: bakedCookie }, @@ -65,46 +65,46 @@ function verifyAuth(bakedCookie, next) { //an error here probably means we couldn't contact //auth - if( err ){ + if (err) { - return handleAuthResponse(404, null, null, next); + return handleAuthResponse(404, null, null, next); - }else if ( body && body.data ){ + } else if (body && body.data) { - return handleAuthResponse(err, res, body.data, next); + return handleAuthResponse(err, res, body.data, next); - }else{ + } else { - return handleAuthResponse(err, res, body, next); + return handleAuthResponse(err, res, body, next); } }); } /** -* Handles the response from the auth component. This is set up -* to handle the response based on the status code that auth returns. -* -* This function then invokes the next that is passed in. -* -* @param err {Object} - An error object from auth. -* @param res {Object} - Response object from the request library. -* @param body {Object} - The data returned from auth. -* @param next {Funciton} - the next function to call. Has prototype like: -* Function foo(err, data) -* - Where err is the status code returned from the auth component and data is the data object. -* - If there was an error the data field will be set to null. -* - If there was no error (a valid response) then the error field will be null. -*/ -function handleAuthResponse (err, res, body, next){ - - var x = null; - - if ( res ){ - - x = res.statusCode || err; - - }else{ + * Handles the response from the auth component. This is set up + * to handle the response based on the status code that auth returns. + * + * This function then invokes the next that is passed in. + * + * @param err {Object} - An error object from auth. + * @param res {Object} - Response object from the request library. + * @param body {Object} - The data returned from auth. + * @param next {Funciton} - the next function to call. Has prototype like: + * Function foo(err, data) + * - Where err is the status code returned from the auth component and data is the data object. + * - If there was an error the data field will be set to null. + * - If there was no error (a valid response) then the error field will be null. + */ +function handleAuthResponse(err, res, body, next) { + + var x = null; + + if (res) { + + x = res.statusCode || err; + + } else { x = err; } @@ -113,32 +113,32 @@ function handleAuthResponse (err, res, body, next){ //using this we can handle various response codes. //currently all error/failures are handled the same way, // but we might want to change this in the future. - switch( x ){ + switch (x) { - case 200: + case 200: - if( body ){ + if (body) { - next(null, body); + next(null, body); - }else{ + } else { - logger.warn("auth.handleAuthResponse() : body returned from auth was not valid : "+util.inspect(body, false ,null)); - next(500, null); + logger.warn("auth.handleAuthResponse() : body returned from auth was not valid : " + util.inspect(body, false, null)); + next(500, null); } - break; + break; case 400: - logger.warn("auth.handleAuthResponse() : request to auth component was not well formed: "+util.inspect(err, false ,null)); - next(400, null); - break; + logger.warn("auth.handleAuthResponse() : request to auth component was not well formed: " + util.inspect(err, false, null)); + next(400, null); + break; case 401: - logger.warn("auth.handleAuthResponse() : Invalid credentials provided. error was: "+util.inspect(err, false, null)); - next(401, null); - break; + logger.warn("auth.handleAuthResponse() : Invalid credentials provided. error was: " + util.inspect(err, false, null)); + next(401, null); + break; case 404: logger.warn("auth.handleAuthResopnse() : Could not contact auth server."); @@ -146,12 +146,12 @@ function handleAuthResponse (err, res, body, next){ break; case 500: - logger.error("auth.handleAuthResponse() : received a server error from auth : "+util.inspect(err, false , null)); - next(500, null); + logger.error("auth.handleAuthResponse() : received a server error from auth : " + util.inspect(err, false, null)); + next(500, null); break; default: - logger.warn("auth.handleAuthResponse() : Not sure how to handle statusCode: "+res.statusCode); + logger.warn("auth.handleAuthResponse() : Not sure how to handle statusCode: " + res.statusCode); next(res.statusCode, null); break; @@ -161,5 +161,5 @@ function handleAuthResponse (err, res, body, next){ module.exports = { getBakedCookie: getBakedCookie, - verifyAuth: verifyAuth + verifyAuth : verifyAuth }; diff --git a/lib/middleware.js b/lib/middleware.js index 072db65..15648d2 100644 --- a/lib/middleware.js +++ b/lib/middleware.js @@ -12,7 +12,7 @@ var util = require('util'), */ function middleware(next, data) { - var default_months = 36; + var default_months = 36; /** * Make a GET request to the hubapi. @@ -22,142 +22,141 @@ function middleware(next, data) { */ function hubapiGet(req, path, next) { - //wrap everything in a try-catch to make sure that we don't crash the visualizer if - //something breaks. - logger.warn("hubapiGet: "+ path); + //wrap everything in a try-catch to make sure that we don't crash the visualizer if + //something breaks. + logger.warn("hubapiGet: " + path); - try{ + try { - auth.verifyAuth( - req.session.bakedCookie, - function (err, body){ + auth.verifyAuth( + req.session.bakedCookie, + function (err, body) { - if( err ){ + if (err) { - //pass the error onward. - next(err, null); + //pass the error onward. + next(err, null); - }else{ + } else { - if(!(body.federation && body.jurisdiction && body.username)){ - logger.warn("middleware.hubapiGet() : User verification failed."); - next(401, null); - return; + if (!(body.federation && body.jurisdiction && body.username)) { - }else if(!(body.clinic && body.clinician)){ + logger.warn("middleware.hubapiGet() : User verification failed."); + next(401, null); + return; - logger.warn("middleware.hubapiGet() : User does not exist in auth."); - next(401, null); - return; + } else if (!(body.clinic && body.clinician)) { - } + logger.warn("middleware.hubapiGet() : User does not exist in auth."); + next(401, null); + return; - logger.success(process.env.HUBAPI_URL + path + '?cookie=' + req.session.bakedCookie); - require('request').get( - { url: process.env.HUBAPI_URL + path + '?cookie=' + req.session.bakedCookie, json: true }, - function (error, response, body) { + } - try{ + logger.success(process.env.HUBAPI_URL + path + '?cookie=' + req.session.bakedCookie); + require('request').get( + {url: process.env.HUBAPI_URL + path + '?cookie=' + req.session.bakedCookie, json: true}, + function (error, response, body) { - if ( !response || !response.statusCode ){ + try { - logger.warn("Failed to get response from HAPI for request to path: "+ path); - next(404, null); - return; + if (!response || !response.statusCode) { - } + logger.warn("Failed to get response from HAPI for request to path: " + path); + next(404, null); + return; - logger.warn("Response from HAPI for request to "+ path +" : "+ response.statusCode); + } - //using this we can handle various response codes. - //currently all error/failures are handled the same way, - // but we might want to change this in the future. + logger.warn("Response from HAPI for request to " + path + " : " + response.statusCode); - switch( response.statusCode ){ + //using this we can handle various response codes. + //currently all error/failures are handled the same way, + // but we might want to change this in the future. - case 200: + switch (response.statusCode) { - if( body && response){ + case 200: - return next(null, response); + if (body && response) { + return next(null, response); - }else{ - logger.warn("middleware.hubapiGet() : body returned from auth was not valid : "+util.inspect(body, false ,null)); - return next(500, null); + } else { - } + logger.warn("middleware.hubapiGet() : body returned from auth was not valid : " + util.inspect(body, false, null)); + return next(500, null); - break; + } - case 204: - logger.warn("middleware.hubapiGet(): received 204, request was successful but no data was returned.") - return next(204, null); - break; + break; - case 400: - logger.warn("middleware.hubapiGet() : request to auth component was not well formed: "+util.inspect(error, false ,null)); - return next(400, null); - break; + case 204: + logger.warn("middleware.hubapiGet(): received 204, request was successful but no data was returned.") + return next(204, null); + break; - case 401: - logger.warn("middleware.hubapiGet() : Invalid credentials provided. error was: "+util.inspect(error, false, null)); - return next(401, null); - break; + case 400: + logger.warn("middleware.hubapiGet() : request to auth component was not well formed: " + util.inspect(error, false, null)); + return next(400, null); + break; - case 404: - logger.warn("middleware.hubapiGet() : 404 from hubapi, indicates query does not exist in the database. error: "+util.inspect(error, false, null)); - return next(404, null); - break; + case 401: + logger.warn("middleware.hubapiGet() : Invalid credentials provided. error was: " + util.inspect(error, false, null)); + return next(401, null); + break; - case 500: - logger.error("middleware.hubapiGet() : Generated a server error : "+util.inspect(error, false , null)); - return next(500, null); - break; - - default: - logger.warn("middleware.hubapiGet() : Not sure how to handle statusCode: "+response.statusCode); - return next(response.statusCode, null); - break; + case 404: + logger.warn("middleware.hubapiGet() : 404 from hubapi, indicates query does not exist in the database. error: " + util.inspect(error, false, null)); + return next(404, null); + break; - } + case 500: + logger.error("middleware.hubapiGet() : Generated a server error : " + util.inspect(error, false, null)); + return next(500, null); + break; - return; + default: + logger.warn("middleware.hubapiGet() : Not sure how to handle statusCode: " + response.statusCode); + return next(response.statusCode, null); + break; - }catch(e){ + } - logger.log(e.stack); - logger.error("middleware.hubapiGet() caught an exception: "+ util.inspect(e, false, null) ); - return next(500, null); + return; - } - } + } catch (e) { - ); + logger.log(e.stack); + logger.error("middleware.hubapiGet() caught an exception: " + util.inspect(e, false, null)); + return next(500, null); - } + } + } + ); - } + } - ); + } + ); - }catch(e){ + } catch (e) { - logger.error("middleware.hubapiGet() caught an exception: "+ util.inspect(e, false, null) ); - return next(500, null); + logger.error("middleware.hubapiGet() caught an exception: " + util.inspect(e, false, null)); + return next(500, null); - } + } } /** * Makes a request to the hupapi for a list of visualizations and attaches it to `req.visualizations`. - * + * * If the request to the hubapi or auth components fail, this function will not call the next middleware, - * instead it will cause a failure and load the default error page. - * + * instead it will cause a failure and load the default error page. + * * @param {Object} req The Express req object. * @param {Object} res The Express res object. * @param {Function} callback The next middleware to invoke. @@ -166,96 +165,104 @@ function middleware(next, data) { // TODO: Cache this per user. - try{ + try { hubapiGet(req, '/api/queries', function validation(error, response) { - try{ + try { - if ( error ) { //an error code 204, 400, 401, 500... + if (error) { //an error code 204, 400, 401, 500... - switch( error ){ + switch (error) { - case 204: - //force query lists to empty and continue - req.pdc_queries = []; - return callback(); - break; + case 204: + //force query lists to empty and continue + req.pdc_queries = []; + return callback(); + break; - case 401: + case 401: - delete req.session.user; - delete req.session.bakedCookie; - res.status(401); - res.render('error', { message : "middleware.populateVisualizationList failed due to failure to authenticate.", redirect : '/auth'}); - break; + delete req.session.user; + delete req.session.bakedCookie; + res.status(401); + res.render('error', { + message : "middleware.populateVisualizationList failed due to failure to authenticate.", + redirect: '/auth' + }); + break; - case 500: + case 500: - res.status(500); - res.render('error', {message : 'middleware.populateVisualizationList failed due to a server failure.', redirect : '/'}); - break; + res.status(500); + res.render('error', { + message : 'middleware.populateVisualizationList failed due to a server failure.', + redirect: '/' + }); + break; - case 404: + case 404: - res.status(404); - res.render('error', {message : 'middleware.populateVisualizationList failed due auth or hubapi being unreachable', redirect : '/'}); - break; + res.status(404); + res.render('error', { + message : 'middleware.populateVisualizationList failed due auth or hubapi being unreachable', + redirect: '/' + }); + break; - default: + default: - //in any other case, we can just populate the query list as empty: - req.pdc_queries = []; - return callback(); - break; + //in any other case, we can just populate the query list as empty: + req.pdc_queries = []; + return callback(); + break; } - }else{ + } else { req.pdc_queries = response.body.queries.filter( - function removeNonPDC(x) { - if (x && x.title && x.executions ){ + if (x && x.title && x.executions) { - return x.title.indexOf("PDC")!=-1 && x.executions.length > 0; + return x.title.indexOf("PDC") != -1 && x.executions.length > 0; - }else{ + } else { - return false; + return false; } } - ); callback(); } - }catch(e){ + } catch (e) { - logger.error( "middleware.populateVisualizationList caught an exception: "+ util.inspect(e, false, null) ); + logger.error("middleware.populateVisualizationList caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : 'An exception was thrown by the server: ' + util.inspect(e, false, null) }); - return; + res.render('error', {message: 'An exception was thrown by the server: ' + util.inspect(e, false, null)}); + return; } }); - }catch(e){ + } catch (e) { - logger.error( "middleware.populateVisualizationList caught an exception: "+ util.inspect(e, false, null) ); + logger.error("middleware.populateVisualizationList caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : 'An exception was thrown by the server: ' + util.inspect(e, false, null) }); - return; + res.render('error', {message: 'An exception was thrown by the server: ' + util.inspect(e, false, null)}); + return; } } + /** * Makes a request to the hubapi API for a single visualization and attaches it to `req.visualization`. * @param {Object} req The Express req object. @@ -264,12 +271,15 @@ function middleware(next, data) { */ function populateVisualization(req, res, callback) { - try{ + try { - if (!req.params.title) { + if (!req.params.title) { - res.render( 'error' , { message : 'please provide a title for the visualization in the URL.', redirect:'/' }); - return; + res.render('error', { + message : 'please provide a title for the visualization in the URL.', + redirect: '/' + }); + return; } @@ -277,39 +287,42 @@ function middleware(next, data) { var address; - if(req.params.title==='PDC-055'){ + if (req.params.title === 'PDC-055') { - address='/medclass'; + address = '/medclass'; - }else if(req.params.title==='PDC-001'){ + } else if (req.params.title === 'PDC-001') { address = '/demographics/'; - }else if(req.params.title.indexOf('PDC')===0){ + } else if (req.params.title.indexOf('PDC') === 0) { address = '/retro/' + req.params.title + '/'; - }else{ + } else { logger.error('middleware.populateVisualization(): unrecognized query: ' + req.params.title); res.status(404); - res.render('error', { message : 'Did not recognize the query: '+ req.params.title, redirect : '/' } ); + res.render('error', {message: 'Did not recognize the query: ' + req.params.title, redirect: '/'}); return; } hubapiGet(req, address, function validation(error, request) { - try{ + try { - if ( error ){ + if (error) { - switch(error){ + switch (error) { case 204: - logger.warn("middleware.populateVisualization could not find executions for query: "+ req.params.title+ " returning 404."); + logger.warn("middleware.populateVisualization could not find executions for query: " + req.params.title + " returning 404."); res.status(404); - res.render('error', { message : "middleware.populateVisualization could not find executions for query: "+req.params.title, redirect : '/'}); + res.render('error', { + message : "middleware.populateVisualization could not find executions for query: " + req.params.title, + redirect: '/' + }); return; break; @@ -317,150 +330,171 @@ function middleware(next, data) { delete req.session.user; delete req.session.bakedCookie; - logger.warn("middleware.populateVisualization failed to get visualization for: "+req.params.title+" due to failure to authenticate user"); + logger.warn("middleware.populateVisualization failed to get visualization for: " + req.params.title + " due to failure to authenticate user"); res.status(401); - res.render('error', { message : "middleware.populateVisualization failed due to failure to authenticate.", redirect : '/auth'}); - return; + res.render('error', { + message : "middleware.populateVisualization failed due to failure to authenticate.", + redirect: '/auth' + }); + return; break; - case 404: - - logger.warn("Hubapi did not find query: "+ req.params.title+" in the database."); + case 404: + + logger.warn("Hubapi did not find query: " + req.params.title + " in the database."); res.status(404); - res.render( 'error', { message : 'the query: '+req.params.title+' does not exist in the database. ', redirect: '/' } ); - return; - break; + res.render('error', { + message : 'the query: ' + req.params.title + ' does not exist in the database. ', + redirect: '/' + }); + return; + break; case 500: - logger.warn("Hubapi failed to fetch data for query: "+req.params.title+", returning 500"); + logger.warn("Hubapi failed to fetch data for query: " + req.params.title + ", returning 500"); res.status(500); - res.render('error', {message : 'middleware.populateVisualization failed due to a server failure.', redirect : '/'}); - return; + res.render('error', { + message : 'middleware.populateVisualization failed due to a server failure.', + redirect: '/' + }); + return; break; default: - logger.warn("Unknown response from hubapi: "+ error+ " returning 500 to client."); + logger.warn("Unknown response from hubapi: " + error + " returning 500 to client."); res.status(500); - res.render('error', { message : "middleware.populateVisualization failed with status code:" + error, redirect : '/'}); - return; - break; + res.render('error', { + message : "middleware.populateVisualization failed with status code:" + error, + redirect: '/' + }); + return; + break; } } - if( request && request.body ){ + if (request && request.body) { req.visualization = request.body; callback(); - }else{ + } else { res.status(500); - res.render('error', { message : "middleware.populateVisualization failed", redirect : '/'}); + res.render('error', {message: "middleware.populateVisualization failed", redirect: '/'}); return; } - }catch(e){ + } catch (e) { - logger.error("Caught exception in middleware.populateVisualization() : "+ util.inspect( e, false, null ) ); + logger.error("Caught exception in middleware.populateVisualization() : " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : "middleware.populateVisualization failed wth an exception", redirect : '/'}); + res.render('error', { + message : "middleware.populateVisualization failed wth an exception", + redirect: '/' + }); - } + } }); - }catch(e){ + } catch (e) { - logger.error("Caught exception in middleware.populateVisualization() : "+ util.inspect( e, false, null ) ); + logger.error("Caught exception in middleware.populateVisualization() : " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : "middleware.populateVisualization failed wth an exception", redirect : '/'}); + res.render('error', {message: "middleware.populateVisualization failed wth an exception", redirect: '/'}); } } /** - * Stores the reports that are provided by the hubapi in a list stored in req.reports - * - * @param {Object} req The Express req object. - * @param {Object} res The Express res object. - * @param {Function} callback The next middleware to invoke. - */ - function populateReportsList(req, res, callback){ + * Stores the reports that are provided by the hubapi in a list stored in req.reports + * + * @param {Object} req The Express req object. + * @param {Object} res The Express res object. + * @param {Function} callback The next middleware to invoke. + */ + function populateReportsList(req, res, callback) { + - - try{ + try { - hubapiGet(req, '/reports', + hubapiGet(req, '/reports', function validation(error, response) { - try{ + try { - var toReturn = []; + var toReturn = []; - if ( error ) { + if (error) { - switch( error ){ + switch (error) { - case 401: + case 401: //401 occurs if invalid credentials were provided. //in this case we would like to redirect them to auth. delete req.session.bakedCookie; - delete req.session.user; + delete req.session.user; logger.warn("middleware.populateReportsList failed to authenticate user while requesting reports from hubapi."); res.status(401); - res.render( 'error', { message : 'unable to authenticate user.', redirect : '/auth'} ); - return; + res.render('error', {message: 'unable to authenticate user.', redirect: '/auth'}); + return; break; - case 404: + case 404: //could not communicate with hubapi or auth. logger.warn("middleware.populateReportsList failed to communicate with hubapi or auth components."); res.status(404); - res.render('error', { message : 'Could not communicate with hubapi or auth.', redirect : '/'} ); + res.render('error', { + message : 'Could not communicate with hubapi or auth.', + redirect: '/' + }); break; default: //in every other case we shouldn't redirect to an error page, instead just //return an empty array for reports. - logger.warn("middleware.populateReportsList caused an error ("+error+"), returning empty list."); - req.reports = []; + logger.warn("middleware.populateReportsList caused an error (" + error + "), returning empty list."); + req.reports = []; return callback() break; } - }else{ + } else { - for( var i = 0; i < response.body.length; i++ ){ + for (var i = 0; i < response.body.length; i++) { - if ( response.body[i] && response.body[i].shortTitle ){ + if (response.body[i] && response.body[i].shortTitle) { - toReturn.push({ shortTitle: response.body[i].shortTitle +=".csv", title : response.body[i].title} ); + toReturn.push({ + shortTitle: response.body[i].shortTitle += ".csv", + title : response.body[i].title + }); } } - req.reports = toReturn; + req.reports = toReturn; - return callback(); + return callback(); } - }catch(e){ + } catch (e) { - logger.error("middleware.populateReportsList() caught an exception: "+ util.inspect(e, false, null)); + logger.error("middleware.populateReportsList() caught an exception: " + util.inspect(e, false, null)); //if there was an error, we can just not return the report list. req.reports = []; - return callback(); + return callback(); } @@ -468,150 +502,166 @@ function middleware(next, data) { } ); - }catch(e){ + } catch (e) { - logger.error("middleware.populateReportsList() caught an exception: "+ util.insepct(e, false, null)); + logger.error("middleware.populateReportsList() caught an exception: " + util.insepct(e, false, null)); //if there was an error, we can just not return the report list. req.reports = []; - return callback(); + return callback(); } - + } /** - * Attempts to authenticate the user with the auth component. - * + * Attempts to authenticate the user with the auth component. + * * This function will redirect to the appropriate page if authentication - * fails. In which case the callback will not be called. - * + * fails. In which case the callback will not be called. + * * @param {Object} req The Express req object. * @param {Object} res The Express res object. * @param {Function} callback The next middleware to invoke. */ function authenticateUser(req, res, callback) { - try{ + try { - if (!req.body.jur && !req.body.user && !req.body.password) { + if (!req.body.jur && !req.body.user && !req.body.password) { - res.statusCode = 401; - res.redirect('/auth', {message : "Please provide username, password, and jurisdiction"}); + res.statusCode = 401; + res.redirect('/auth', {message: "Please provide username, password, and jurisdiction"}); - }else{ + } else { - auth.getBakedCookie(req.body.juri, req.body.user, req.body.password, function (err, authResult) { + auth.getBakedCookie(req.body.juri, req.body.user, req.body.password, function (err, authResult) { - try{ + try { - if ( err ) { + if (err) { - switch( err ){ + switch (err) { - case 400: + case 400: - res.statusCode = 400; - res.render('error', {message : "Malformed request object."}); - break; + res.statusCode = 400; + res.render('error', {message: "Malformed request object."}); + break; - case 401: + case 401: - res.statusCode = 401; - res.render('auth', {message : 'Invalid username and password combination, please try again. ', redirect: '/auth'}); - break; + res.statusCode = 401; + res.render('auth', { + message : 'Invalid username and password combination, please try again. ', + redirect: '/auth' + }); + break; - case 404: + case 404: - res.statusCode = 404; - res.render('error', {message : 'Could not contact auth server.', redirect : '/auth'}); - break; + res.statusCode = 404; + res.render('error', {message: 'Could not contact auth server.', redirect: '/auth'}); + break; - case 500: + case 500: - res.statusCode = 500; - res.render('error', {message : 'An internal server error occurred. Check logs for failure.'}); - break; + res.statusCode = 500; + res.render('error', {message: 'An internal server error occurred. Check logs for failure.'}); + break; - default: + default: - res.statusCode = 500; - res.render('error', {message : 'An internal server error occurred. Check logs for failure.'}); - break; + res.statusCode = 500; + res.render('error', {message: 'An internal server error occurred. Check logs for failure.'}); + break; - } + } - } else { + } else { - if( !authResult || !authResult.cookie ){ + if (!authResult || !authResult.cookie) { - delete req.session.bakedCookie; - delete req.session.user; - res.statusCode = 500; - res.render('error', {message : "Could not obtain a session from auth.", redirect : '/auth'}); - return; + delete req.session.bakedCookie; + delete req.session.user; + res.statusCode = 500; + res.render('error', { + message : "Could not obtain a session from auth.", + redirect: '/auth' + }); + return; - }else{ + } else { - auth.verifyAuth(authResult.cookie, function (err, user) { + auth.verifyAuth(authResult.cookie, function (err, user) { - try{ + try { - if ( err ){ + if (err) { - delete req.session.user; - delete req.session.bakedCookie; - res.statusCode = 500; - res.render('error', {message : "Could not verify session with auth.", redirect : '/auth'}); - callback(err) + delete req.session.user; + delete req.session.bakedCookie; + res.statusCode = 500; + res.render('error', { + message : "Could not verify session with auth.", + redirect: '/auth' + }); + callback(err) - }else{ + } else { - //if we get here we have a valid session. - req.session.bakedCookie = authResult.cookie; - req.session.user = user; - callback(); + //if we get here we have a valid session. + req.session.bakedCookie = authResult.cookie; + req.session.user = user; + callback(); - } + } - }catch(e){ + } catch (e) { - logger.error("middleware.authenticateUser caught an exception: "+ util.inspect(e, false, null)); - res.statusCode = 500; - res.render('error', {message : "An exception occured: "+ util.inspect(e, false, null), redirect : '/auth'}); - return; + logger.error("middleware.authenticateUser caught an exception: " + util.inspect(e, false, null)); + res.statusCode = 500; + res.render('error', { + message : "An exception occured: " + util.inspect(e, false, null), + redirect: '/auth' + }); + return; - } + } - }); + }); - } + } - } + } - }catch(e){ + } catch (e) { - logger.error(util.inspect(e, false, null)); - res.statusCode = 500; - res.render('error', {message : "An exception occured: "+ util.inspect(e, false, null), redirect : '/auth'}); - return; + logger.error(util.inspect(e, false, null)); + res.statusCode = 500; + res.render('error', { + message : "An exception occured: " + util.inspect(e, false, null), + redirect: '/auth' + }); + return; - } + } - }); + }); - } + } - }catch(e){ + } catch (e) { - logger.error("middleware.authenticateUser caught an exception: "+ util.inspect(e, false, null)); - res.statusCode = 500; - res.render('error', {message : "An exception occured: "+ util.inspect(e, false, null), redirect : '/auth'}); - return; + logger.error("middleware.authenticateUser caught an exception: " + util.inspect(e, false, null)); + res.statusCode = 500; + res.render('error', {message: "An exception occured: " + util.inspect(e, false, null), redirect: '/auth'}); + return; - } + } } + /** * Checks the authentication of the user. (Does not talk to the hubapi or auth) * @param {Object} req The Express req object. @@ -620,7 +670,7 @@ function middleware(next, data) { */ function checkAuthentication(req, res, callback) { - try{ + try { if (!req.session || !req.session.user) { // No session on record or user not logged in @@ -634,12 +684,12 @@ function middleware(next, data) { } - }catch(e){ + } catch (e) { logger.error(util.inspect(e, false, null)); res.status(500); - res.render('error', {message : "An exception occurred: "+ util.inspect(e, false, null), redirect : '/auth'}); - return; + res.render('error', {message: "An exception occurred: " + util.inspect(e, false, null), redirect: '/auth'}); + return; } @@ -648,11 +698,11 @@ function middleware(next, data) { return next(null, { populateVisualizationList: populateVisualizationList, - populateVisualization: populateVisualization, - populateReportsList: populateReportsList, - hubapiGet: hubapiGet, - authenticateUser: authenticateUser, - checkAuthentication: checkAuthentication, + populateVisualization : populateVisualization, + populateReportsList : populateReportsList, + hubapiGet : hubapiGet, + authenticateUser : authenticateUser, + checkAuthentication : checkAuthentication, }); } diff --git a/lib/routes.js b/lib/routes.js index a0b9109..fe260fd 100644 --- a/lib/routes.js +++ b/lib/routes.js @@ -1,7 +1,7 @@ 'use strict'; -var async = require('async'), - _ = require('lodash'), - fs = require('fs'), +var async = require('async'), + _ = require('lodash'), + fs = require('fs'), logger = require('./logger'); /** @@ -11,39 +11,38 @@ var async = require('async'), */ function routes(next, data) { var router = new require('express').Router(); - var util = new require('util'); + var util = new require('util'); /* Index */ router.get('/', data.middleware.checkAuthentication, data.middleware.populateVisualizationList, - data.middleware.populateReportsList, + data.middleware.populateReportsList, function render(req, res) { - try{ + try { res.status(200); res.render('index', { - title: 'Welcome', - user: req.session.user, + title : 'Welcome', + user : req.session.user, pdc_queries: req.pdc_queries, - reports: req.reports + reports : req.reports } ); - }catch(e){ + } catch (e) { - logger.error("Route: / caught an exception: "+ util.inspect(e, false, null)); + logger.error("Route: / caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render("error", { message : 'An error occurred while loading the home page.', redirect : '/' } ); - return; + res.render("error", {message: 'An error occurred while loading the home page.', redirect: '/'}); + return; } } ); - /* A Visualization */ router.get('/visualization/:title/:endpoint/:provider', @@ -55,24 +54,26 @@ function routes(next, data) { try { - var errorMessage = null; - var query = null; - var visualizationKeys = []; + var errorMessage = null; + var query = null; + var visualizationKeys = []; var filteredQueries = req.pdc_queries.filter( - - function(x) { + function (x) { return x.title === req.params.title; } ); - if( filteredQueries.length !== 1 ){ + if (filteredQueries.length !== 1) { - logger.error("visualization/:title/:endpoint/:provider: Number of queries in database with title "+req.params.title+" was not exactly 1."); + logger.error("visualization/:title/:endpoint/:provider: Number of queries in database with title " + req.params.title + " was not exactly 1."); res.status(500) - res.render( "error", {message : "Number of queries in database with title "+req.params.title+" was not exactly 1", redirect: '/' } ); + res.render("error", { + message : "Number of queries in database with title " + req.params.title + " was not exactly 1", + redirect: '/' + }); return; } @@ -80,227 +81,253 @@ function routes(next, data) { //the first and only query in the list should be the one we are looking at. query = filteredQueries[0]; - if( !query.executions ){ + if (!query.executions) { errorMessage = 'query - ' + req.params.title + ' has undefined executions'; logger.error('visualization/:title/:endpoint/:provider:' + errorMessage); res.status(500) - res.render('error', { message : errorMessage, redirect : '/' } ); + res.render('error', {message: errorMessage, redirect: '/'}); return; } //check that we actually have executions on the query to return. - if( query.executions.length < 1 ){ + if (query.executions.length < 1) { //no content provided, so we return with 404 and redirect to an error page. errorMessage = 'no executions for: ' + req.params.title + ' on EP: ' + req.params.endpoint; logger.error('visualization/:title/:endpoint/:provider:' + errorMessage); res.status(404) - res.render( "error", { message : "There were not executions found for query: "+req.params.title , redirect : '/'} ); + res.render("error", { + message : "There were not executions found for query: " + req.params.title, + redirect: '/' + }); return; } - var lineVisQueries = [ - 'PDC-008', 'PDC-009', 'PDC-014', - 'PDC-020', 'PDC-022', 'PDC-025', - 'PDC-026', 'PDC-027', 'PDC-028', 'PDC-1738' - ], + var lineVisQueries = [ + 'PDC-008', 'PDC-009', 'PDC-014', + 'PDC-020', 'PDC-022', 'PDC-025', + 'PDC-026', 'PDC-027', 'PDC-028', + ], - clusterBarVisQueries = ['PDC-055'], - barVisQueries = ['PDC-053', 'PDC-054','PDC-056', 'PDC-057', 'PDC-058', 'PDC-1178'], - demoPyramidQueries = ['PDC-001']; + clusterBarVisQueries = ['PDC-055'], + barVisQueries = ['PDC-053', 'PDC-054', 'PDC-056', 'PDC-057', 'PDC-058', 'PDC-1178', 'PDC-1738'], + demoPyramidQueries = ['PDC-001']; - if( demoPyramidQueries.indexOf( req.params.title )> -1 ){ + if (demoPyramidQueries.indexOf(req.params.title) > -1) { req.visualization.script = 'DemographicsPyramidVis'; - }else if( clusterBarVisQueries.indexOf( req.params.title )> -1 ){ + } else if (clusterBarVisQueries.indexOf(req.params.title) > -1) { - req.visualization.script = 'ClusterBarVis'; + req.visualization.script = 'ClusterBarVis'; - }else if( barVisQueries.indexOf( req.params.title )> -1 ){ + } else if (barVisQueries.indexOf(req.params.title) > -1) { req.visualization.script = 'BarVis'; - }else if(lineVisQueries.indexOf(req.params.title) > -1){ + } else if (lineVisQueries.indexOf(req.params.title) > -1) { req.visualization.script = 'LineVis'; - }else{ + } else { - req.visualization.script = null; + req.visualization.script = null; } - if(req.visualization.script === null || req.visualization.script === undefined ){ + if (req.visualization.script === null || req.visualization.script === undefined) { - logger.error('visualization/:title/:endpoint/:provider: no visualization specified for query : '+ req.params.title); - res.status(500); - res.render('error', { message : "Could not find a visualization for query: "+ req.params.title, redirect : '/' } ); + logger.error('visualization/:title/:endpoint/:provider: no visualization specified for query : ' + req.params.title); + res.status(500); + res.render('error', { + message : "Could not find a visualization for query: " + req.params.title, + redirect: '/' + }); return; } - fs.readFile('./visualizations/' + req.visualization.script + '.html', + fs.readFile('./visualizations/' + req.visualization.script + '.html', function callback(err, script) { - try{ + try { if (err) { - logger.error('visualization/:title/:endpoint/:provider: tried to read visualization script: '+script+', failed with error: ' + util.inspect(err, false, null)); + logger.error('visualization/:title/:endpoint/:provider: tried to read visualization script: ' + script + ', failed with error: ' + util.inspect(err, false, null)); res.status(500); - res.render( 'error', { message : 'Could not load visualization for query : '+req.params.title, redirect : '/'} ); - return; + res.render('error', { + message : 'Could not load visualization for query : ' + req.params.title, + redirect: '/' + }); + return; } else { res.render('visualization', { - title: req.params.title, - user: req.session.user, - pdc_queries: req.pdc_queries, - reports : req.reports, + title : req.params.title, + user : req.session.user, + pdc_queries : req.pdc_queries, + reports : req.reports, visualization: req.visualization, - script: script + script : script } ); } - }catch(e){ + } catch (e) { - logger.error("/visualization/:title/:endpoint/:provider caught an exception: "+ util.inspect(e, false, null)); + logger.error("/visualization/:title/:endpoint/:provider caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : "An exception occurred while trying load query: "+ req.params.title+".", redirect : '/' } ); - return; + res.render('error', { + message : "An exception occurred while trying load query: " + req.params.title + ".", + redirect: '/' + }); + return; } } - ); - }catch(e){ + } catch (e) { - logger.error("/visualization/:title/:endpoint/:provider caught an exception: "+ util.inspect(e, false, null)); + logger.error("/visualization/:title/:endpoint/:provider caught an exception: " + util.inspect(e, false, null)); logger.error(e.stack); res.status(500); - res.render('error', { message : "An exception occurred while trying load query: "+ req.params.title+".", redirect : '/' } ); - return; + res.render('error', { + message : "An exception occurred while trying load query: " + req.params.title + ".", + redirect: '/' + }); + return; } } - ); /* - * Route that handles requests for tabular reports - */ + * Route that handles requests for tabular reports + */ router.route('/report/:title').get( data.middleware.checkAuthentication, data.middleware.populateVisualizationList, data.middleware.populateReportsList, - function render(req, res){ + function render(req, res) { - try{ + try { - logger.warn("req for report: "+req.params.title); + logger.warn("req for report: " + req.params.title); var t = req.params.title.replace(".csv", ""); - data.middleware.hubapiGet(req, "/reports/"+t+"/", + data.middleware.hubapiGet(req, "/reports/" + t + "/", - function complete(error, response){ + function complete(error, response) { - try{ + try { - if( error ){ + if (error) { - switch( error ){ + switch (error) { case 204: - logger.warn("Successfully fetched report: "+t+" from hubapi, but it contained no data, nothing to return."); + logger.warn("Successfully fetched report: " + t + " from hubapi, but it contained no data, nothing to return."); res.status(404); - res.render('error', {message : 'Successfully fetched report '+t+', but it had no data.', redirect : '/'}); - return; - break; + res.render('error', { + message : 'Successfully fetched report ' + t + ', but it had no data.', + redirect: '/' + }); + return; + break; case 400: - logger.warn("Failed to fetch report "+t+" from hubapi due to a malformed request.") + logger.warn("Failed to fetch report " + t + " from hubapi due to a malformed request.") res.status(400); - res.render("error", {message : "routes: Failed to fetch report"+t, redirect : '/'}) - return; - break; + res.render("error", { + message : "routes: Failed to fetch report" + t, + redirect: '/' + }) + return; + break; - case 401: + case 401: - logger.warn("Could not fetch report "+ t + " from server as the user's identity could not verified."); + logger.warn("Could not fetch report " + t + " from server as the user's identity could not verified."); res.status(401); - res.render("error", {message : "routes: Unable to verify identity, either the session expired or invalid there are credentials.", redirect : '/auth'}) - return; + res.render("error", { + message : "routes: Unable to verify identity, either the session expired or invalid there are credentials.", + redirect: '/auth' + }) + return; break; - case 500: + case 500: - logger.error("Failed to fetch report: "+ t + " from HubAPI."); + logger.error("Failed to fetch report: " + t + " from HubAPI."); res.status(500); - res.render("error", {message : "routes: Failure to get report data from hubapi.", redirect : '/'}) - return; - break; + res.render("error", { + message : "routes: Failure to get report data from hubapi.", + redirect: '/' + }) + return; + break; - default: + default: - logger.error("Failed to fetch report: "+ t + " from HubAPI, error code was: "+ error); + logger.error("Failed to fetch report: " + t + " from HubAPI, error code was: " + error); res.status(500); - res.render("error", {message : "routes: Failure to get report data from hubapi.", redirect : '/'}) + res.render("error", { + message : "routes: Failure to get report data from hubapi.", + redirect: '/' + }) return; - break; + break; } - }else if( !response || !response.body ){ + } else if (!response || !response.body) { logger.error("Invalid response body returned from HubAPI."); res.status(404); - res.render("error", {message : "Failure to get report data from hubapi.", redirect : '/'}) - return; + res.render("error", {message: "Failure to get report data from hubapi.", redirect: '/'}) + return; - }else{ + } else { res.status(200); - return res.send( new Buffer(response.body) ); + return res.send(new Buffer(response.body)); } - }catch(e){ + } catch (e) { - logger.error("/report/title caught an exception: "+ util.inspect(e, false, null)); + logger.error("/report/title caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : 'Failed to get report data from hubapi.', redirect : '/'}); - return; + res.render('error', {message: 'Failed to get report data from hubapi.', redirect: '/'}); + return; } } - ); - }catch(e){ + } catch (e) { - logger.error("/report/title caught an exception: "+ util.inspect(e, false, null)); + logger.error("/report/title caught an exception: " + util.inspect(e, false, null)); res.status(500); - res.render('error', { message : 'Failed to get report data from hubapi.', redirect : '/'}); - return; + res.render('error', {message: 'Failed to get report data from hubapi.', redirect: '/'}); + return; } } - ); @@ -309,13 +336,13 @@ function routes(next, data) { .get(function (req, res) { - try{ + try { var message; if (req.session.message) { - message = req.session.message; + message = req.session.message; logger.warn(message); req.session.message = null; @@ -323,54 +350,58 @@ function routes(next, data) { res.render('auth', {message: message, user: req.session.user}); - }catch(e){ + } catch (e) { logger.error(util.inspect(e, false, null)); res.status(500); - res.render('error', {message : 'Internal server error: '+util.inspect(e, false, null), redirect:'/auth'}); + res.render('error', { + message : 'Internal server error: ' + util.inspect(e, false, null), + redirect: '/auth' + }); } }).post( + data.middleware.authenticateUser, + function (req, res) { - data.middleware.authenticateUser, - function (req, res) { + try { - try{ + res.redirect('/'); - res.redirect('/'); + } catch (e) { - }catch(e){ + logger.error(util.inspect(e, false, null)); + res.status(500); + res.render('error', { + message : 'Internal server error: ' + util.inspect(e, false, null), + redirect: '/auth' + }); - logger.error(util.inspect(e, false, null)); - res.status(500); - res.render('error', {message : 'Internal server error: '+util.inspect(e, false, null), redirect:'/auth'}); + } - } + } + ); - } - ); - /* Log Out */ router.get('/logout', function (req, res) { - try{ + try { delete req.session.user; res.redirect('/'); - }catch(e){ + } catch (e) { - logger.error("/logout caught an exception: "+ util.inspect(e, false, null)); + logger.error("/logout caught an exception: " + util.inspect(e, false, null)); res.redirect('/'); } } - ); // Attach the router. @@ -378,4 +409,4 @@ function routes(next, data) { next(null, router); } -module.exports = ['httpd', 'middleware', routes ]; +module.exports = ['httpd', 'middleware', routes]; diff --git a/visualizations/BarVis.html b/visualizations/BarVis.html index 71299af..c790e00 100644 --- a/visualizations/BarVis.html +++ b/visualizations/BarVis.html @@ -1,274 +1,298 @@ diff --git a/visualizations/DemographicsPyramidVis.html b/visualizations/DemographicsPyramidVis.html index 91534eb..64e8c18 100644 --- a/visualizations/DemographicsPyramidVis.html +++ b/visualizations/DemographicsPyramidVis.html @@ -1,366 +1,378 @@ + var ageRanges = [ + "90+", + "80-89", + "70-79", + "60-69", + "50-59", + "40-49", + "30-39", + "20-29", + "10-19", + "0-9"]; + + var toolTipColorMap = {male: '#8888FF', female: '#FF8888', undifferentiated: '#CCCCFF', undefined: '#FFCCCC'}; + //@WiP create the tooltip div + var div = d3.select("#chart").append("div") + .attr("class", "tooltip") + .style({"opacity": 0, "width": "150"}); + + var formatTime = d3.time.format("%a %b %d %Y"); + + var tempData; + + function toolTipMouseOver(d, src, tempData) { + console.log(d, src); + div.transition() + .duration(200) + .style({"opacity": 0.9, "background": toolTipColorMap[src]}); + + div.html( + "Date: " + formatTime(new Date(tempData.time)) + "
" + + "Num of Patients:" + tempData[src][d] + "
" + + "Total Patients:" + tempData.totalPatients) + .style({ + "left": (d3.event.pageX) + "px", + "top" : (d3.event.pageY - 28) + "px" + }); + } + + function toolTipMouseOut(d) { + div.transition() + .duration(500) + .style("opacity", 0); + } + + function showVis(tempData) { + + tempData.totalPatients = reduceTempData(); + + function reduceTempData() { + var totalPatients = 0; + var countKeys = ['male', 'female', 'undifferentiated', 'undefined']; + for (var outerKey in tempData) { + if (countKeys.indexOf(outerKey) < 0) { + continue; + } + + for (var innerKey in tempData[outerKey]) { + totalPatients += tempData[outerKey][innerKey]; + } + } + + return totalPatients; + } + + //find max data value for scaling both x axes + var maxData = 0; + + + //top level sorted by group (male,female,undefferentiated) + for (var group in tempData) { + //make sure prototyping doesn't find extra keys and exclude time stamp + if (tempData.hasOwnProperty(group) && group != "time") { + + //data at this level set by ranges eg 0-9 + for (var range in tempData[group]) { + //Take the max of the dataset + if (tempData[group][range] > maxData) { + maxData = tempData[group][range]; + } + } + } + } + + //define container constraints + var margin = {top: 50, right: 50, bottom: 75, left: -75, inner: 80}, + //This is half width due to the two sides of a pop pyramid + width = 800 - margin.left - margin.right, + height = 500 - margin.top - margin.bottom, + yScale = d3.scale.ordinal().domain(ageRanges).rangeBands([0, height], 0.25), + //Scale for right pyramid + x1Scale = d3.scale.linear().domain([0, Math.floor(maxData * 1.5)]).range([0, width / 2]), + //scale for left pyramid + x2Scale = d3.scale.linear().domain([0, Math.floor(maxData * 1.5)]).range([width / 2, 0]); + + var canvas = d3.select("#chart") + .append("svg:svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .append("svg:g") + .attr("transform", "translate(0,50) scale(0.5, 0.5)"); + + //append a title + canvas.append("g") + .attr("class", "title") + .append("text") + .attr("x", width / 2) + .attr("y", 1) + .style({"text-anchor": "middle", "text-decoration": "underline", "font-size": "18pt"}) + .text("Population Pyramid"); + + //text on y axis for age ranges + canvas.append("g") + .attr("class", "yTicks") + .selectAll("text.ages") + .data(ageRanges) + .enter("svg:text").append("text") + .attr("class", "ages") + .attr("x", width / 2) + .attr("y", function (d) { + return yScale(d) + yScale.rangeBand() - 10; + }) + .attr("fill", "#000") + .attr("text-anchor", "middle") + .attr("font-size", "12px") + .style({"text-anchor": "middle", "font-size": "14pt"}) + .text(function (d) { + return d; + }); + + //append the word age to the bot mid of graph + canvas.append("svg:text") + .attr("x", width / 2) + .attr("y", height + 40) + .attr("dy", ".71em") + .attr("fill", "#000") + .attr("text-anchor", "middle") + .attr("letter-spacing", 1) + .style({"text-anchor": "middle", "text-decoration": "underline", "font-size": "18pt"}) + .text("Age"); + + + //bars for females on graph + var fbars = canvas.append("g") + .attr("class", "fbars") + .selectAll("rect.female") + .data(ageRanges)//iterates over the age ranges not the data for completeness + .enter("svg:rect").append("rect") + .attr("class", "female") + .attr("fill", "red") + .attr("transform", + function (d, i) { + return "translate(" + ( width / 2 + margin.inner / 2 ) + "," + yScale(d) + ")"; + }) + .attr("width", + function (d) { + return x1Scale(tempData.female[d]); + }) + .attr("height", yScale.rangeBand()) + .attr("y", 0) + .attr("opacity", 1) + .on("mouseover", function (d) { + toolTipMouseOver(d, 'female', tempData); + }) + .on("mouseout", toolTipMouseOut); + + //bars for undefined on graph + var dbars = canvas.append("g") + .attr("class", "dbars") + .selectAll("rect.undef") + .data(ageRanges)//iterates over the age ranges not the data for completeness + .enter("svg:rect").append("rect") + .attr("class", "undef") + .attr("fill", "#FFAAAA") + .attr("transform", + function (d, i) { + return "translate(" + ( width / 2 + margin.inner / 2 ) + "," + yScale(d) + ")"; + }) + .attr("width", + function (d) { + return x1Scale(tempData.undefined[d]); + }) + .attr("height", yScale.rangeBand() / 3) + .attr("y", yScale.rangeBand() / 3) + .attr("opacity", 1) + .on("mouseover", function (d) { + toolTipMouseOver(d, 'undefined', tempData); + }) + .on("mouseout", toolTipMouseOut); + + //bars for males on graph + var mbars = canvas.append("g") + .attr("class", "mbars") + .selectAll("rect.male") + .data(ageRanges) + .enter("svg:rect").append("rect") + .attr("class", "male") + .attr("fill", "blue") + .attr("opacity", 1) + .attr("transform", + function (d, i) { + return "translate(" + ( x2Scale(tempData.male[d]) - margin.inner / 2 ) + "," + yScale(d) + ")"; + }) + .attr("width", function (d) { + return x1Scale(tempData.male[d]); + }) + .attr("height", yScale.rangeBand()) + .attr("y", 0) + .attr("opacity", 1) + .on("mouseover", function (d) { + toolTipMouseOver(d, 'male', tempData); + }) + .on("mouseout", toolTipMouseOut); + + //bars for undifferentiated on graph + var ubars = canvas.append("g") + .attr("class", "ubars") + .selectAll("rect.undifferentiated") + .data(ageRanges) + .enter("svg:rect").append("rect") + .attr("class", "undifferentiated") + .attr("fill", "#AAAAFF") + .attr("opacity", 1) + .attr("transform", + function (d, i) { + return "translate(" + ( x2Scale(tempData.undifferentiated[d]) - margin.inner / 2 ) + "," + yScale(d) + ")"; + }) + .attr("width", + function (d) { + return x1Scale(tempData.undifferentiated[d]); + }) + .attr("height", yScale.rangeBand() / 3) + .attr("y", yScale.rangeBand() / 3) + .attr("opacity", 1) + .on("mouseover", function (d) { + toolTipMouseOver(d, 'undifferentiated', tempData); + }) + .on("mouseout", toolTipMouseOut); + + //right side (female) x axis + var x1Axis = d3.svg.axis() + .scale(x1Scale) + .orient("bottom") + .tickFormat(d3.format("d")); + + //add right X Axis to SVG + canvas.append("g") + .attr("class", "x axis") + .attr("transform", "translate(" + (width / 2 + margin.inner / 2) + "," + height + ")") + .style({"text-anchor": "middle", "font-size": "14pt"}) + .call(x1Axis) + .append("text") + .attr("x", width / 4) + .attr("y", margin.bottom) + .style({"text-anchor": "middle", "text-decoration": "underline", "font-size": "18pt"}) + .text("Number of Patients"); + + //left side (male) x axis def + var x2Axis = d3.svg.axis() + .scale(x2Scale) + .orient("bottom") + .tickFormat(d3.format("d")); + + //add left X Axis to SVG + canvas.append("g") + .attr("class", "x axis") + .attr("transform", "translate(" + (-1 * margin.inner / 2) + "," + height + ")") + .style({"text-anchor": "middle", "font-size": "14pt"}) + .call(x2Axis) + .append("text") + .attr("x", width / 4) + .attr("y", margin.bottom) + .style({"text-anchor": "middle", "text-decoration": "underline", "font-size": "18pt"}) + .text("Number of Patients"); + + //add vertical lines to boarder y Axis on both sides + // + canvas.append("svg:line") + .attr("y1", 9) + .attr("y2", height + 6) + .attr("x1", width / 2 - margin.inner / 2) + .attr("x2", width / 2 - margin.inner / 2) + .attr("stroke", "black") + .attr("stroke-width", 5); + + canvas.append("svg:line") + .attr("y1", 9) + .attr("y2", height + 6) + .attr("x1", width / 2 + margin.inner / 2) + .attr("x2", width / 2 + margin.inner / 2) + .attr("stroke", "black") + .attr("stroke-width", 5); + + + //mapping for colour selection and legend + var colorMap = {male: "blue", female: "red", undifferentiated: "#8888FF", undefined: "#FF8888"}; + + //size for colour suqares in legend + var legendRectSize = 15; + //shift value for each legend entry + var legendSpacing = 20; + + //add a container for the legend + var legend = canvas.append('g') + .attr('class', 'legend'); + + //append a background for the legend + legend.append('rect') + //transform neg-ive so the left and top are around the legend item + .attr("transform", "translate(-5,-5)") + .attr('width', 180) //values chosen based on info being displayed + .attr('height', 85) + .style('fill', "lightgrey"); + + //create legend entires and for each add a colour block and text + legend.selectAll('.legendEntry') + .data(Object.keys(colorMap)) + .enter().append("g") + .attr('class', 'legendEntry') + .attr("transform", function (d, i) { + return "translate(0," + legendSpacing * i + ")"; + }) + .call(function (d) { + + //append entry text + d.append('text') + .style({"text-anchor": "start", "text-decoration": "underline", "font-size": "14pt"}) + .text(function (d) { + return d; + }) + .attr("transform", function (d, i) { + return "translate(" + (legendRectSize * 1.5 + 10) + ",12)"; + }); + + //append colour square + d.append('rect') + .attr('width', legendRectSize) + .attr('height', legendRectSize) + .style('fill', function (d) { + return colorMap[d]; + }) + .style('stroke', function (d) { + return colorMap[d]; + }); + }); + } + + function vis(data) { + //use only last point + data.clinician = data.clinician.slice(-1); + data.group = data.group.slice(-1); + data.network = data.network.slice(-1); + + //The age ranges for the y axis + //cannot be programtically determined thus hardcoded and + //subject to change if HubAPI changes served info + + //putting network info into tempdata, once this is working @TODO turn eveything below + //into some sort of function and run for all three datasets + showVis(data.clinician[0]); + showVis(data.group[0]); + showVis(data.network[0]); + } +