source: Dev/trunk/src/server/app.js @ 475

Last change on this file since 475 was 475, checked in by hendrikvanantwerpen, 12 years ago
  • Added CouchDB script to make interacting with couch easier.
  • Use this script in the database configuration.
  • Get CSV export to work.
File size: 5.2 KB
Line 
1var express = require("express")
2  , passport = require("passport")
3  , passportLocal = require("passport-local")
4  , fs = require("fs")
5  , path = require("path")
6  , proxy = require("./util/simple-http-proxy")
7  , CSV = require("ya-csv")
8  , CouchDB = require('./util/couch').CouchDB
9  , _ = require("underscore")
10  ;
11
12function assertSetting(name, settings, validate) {
13    if ( typeof settings[name] === 'undefined' ) {
14        throw new Error("Required setting '"+name+"' undefined.");
15    }
16    if ( _.isFunction(validate) && !validate(settings[name]) ) {
17        throw new Error("Setting '"+name+"' with value '"+settings[name]+"' is invalid.");
18    }
19}
20
21exports.App = function(settings) {
22
23    assertSetting("couchDbURL", settings, _.isString);
24    var couch = new CouchDB(settings.couchDbURL);
25   
26    function clientPath(relativePath) {
27        return path.resolve(__dirname+'/../client/'+relativePath);
28    }
29
30    passport.use(new passportLocal.Strategy(function(username, password, done){
31        if ( username === "igor" && password === "mayer" ) {
32            done(null,{ username: "igor" });
33        } else {
34            done(null,false,{ message: 'Invalid credentials.' });
35        }
36    }));
37    passport.serializeUser(function(user, done) {
38        done(null, user.username);
39    });
40    passport.deserializeUser(function(id, done) {
41        done(null, {username: id});
42    });
43
44    var app = express();
45    app.use(express.logger());
46    app.use(express.compress());
47    app.use(express.favicon());
48
49    // cookies and session
50    app.use('/api/login',express.bodyParser());
51    app.use(express.cookieParser());
52    app.use(express.session({ secret: "quasi experimental design" }));
53
54    // passport
55    app.use(passport.initialize());
56    app.use(passport.session());
57    function ensureAuthenticated(req,res,next){
58        if (!req.user) {
59            return res.send(401,{error:"Login before accessing API."});
60        } else {
61            return next();
62        }
63    }
64    function returnUser(req,res) {
65        res.send(200, req.user);
66    }
67
68    // static resources
69    app.get('/', function(request, response){
70        response.sendfile(clientPath('index.html'));
71    });
72    app.get('/*.html', function(request, response) {
73        response.sendfile(clientPath(request.path));
74    });
75    _.each(['/dojo', '/dijit', '/dojox', '/qed', '/qed-client'], function(dir){
76        app.use(dir, express.static(clientPath(dir)));
77    });
78
79
80    // post to this url to login
81    app.post(
82        '/api/login',
83        passport.authenticate('local'),
84        returnUser);
85
86    // return the info for the current logged in user
87    app.get(
88        '/api/login',
89        ensureAuthenticated,
90        returnUser);
91
92    // explicitly logout this user
93    app.post(
94        '/api/logout',
95        ensureAuthenticated,
96        function(req,res){
97            req.logout();
98            res.send(200,{});
99        });
100
101    // data is proxied to couch
102    app.use('/api/data', ensureAuthenticated);
103    app.use('/api/data', proxy(settings.couchDbURL));
104
105    // generate CSV download of responses
106    app.get(
107        '/api/surveyRuns/:id/responses.csv',
108        ensureAuthenticated,
109        function(req, res) {
110            var id = req.params.id;
111            var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(id);
112            couch.get(url).then(function(result){
113                var responses = _.map(result.rows, function(item) { return item.value; });
114                var flatResponses = responsesToVariables(responses);
115                res.set({
116                    'Content-Type': 'text/csv',
117                    'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv'
118                });
119                res.status(200);
120                writeObjectsToCSVStream(flatResponses, res);
121                res.end();
122            }, function(error){
123                res.send(404, {error: "Cannot find responses for survey run "+id});
124            });
125        });
126   
127    return app;
128
129};
130
131function responsesToVariables(responses) {
132    return _.map(responses, responseToVariables);
133}
134
135function responseToVariables(response) {
136    var result = flattenObject(response.answers);
137    result.respondent = response.id;
138    return result;
139}
140
141function flattenObject(value) {
142    var result = {};
143    (function agg(val,key,res){
144        if ( _.isObject(val) ) {
145            _.forEach(val, function(v,k){
146                agg(v,(key ? key+'.' : '')+k,res);
147            });
148        } else if ( _.isArray(val) ) {
149            _.forEach(val, function(v,i){
150                agg(v,(key ? key+'.' : '')+i,res);
151            });
152        } else {
153            res[key] = val;
154        }
155    })(value,null,result);
156    return result;
157}
158
159function writeObjectsToCSVStream(objects, stream) {
160    var keys = _.chain(objects)
161                .map(_.keys)
162                .flatten()
163                .uniq()
164                .value();
165    var idxs = {};
166    _.forEach(keys, function(key,idx){
167        idxs[key] = idx;
168    });
169    var writer = new CSV.CsvWriter(stream);
170    writer.writeRecord(keys);
171    _.forEach(objects, function(obj){
172        var row = [];
173        _.forEach(obj, function(val,key){
174            row[idxs[key]] = val;
175        });
176        writer.writeRecord(row);
177    });
178}
Note: See TracBrowser for help on using the repository browser.