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

Last change on this file since 478 was 478, checked in by hendrikvanantwerpen, 12 years ago

Changes for response submission & deletion.

File size: 7.6 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(express.cookieParser());
51    app.use(express.session({ secret: "quasi experimental design" }));
52    app.use('/api',express.bodyParser());
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    // data is proxied to couch
80    app.use('/data', ensureAuthenticated);
81    app.use('/data', proxy(settings.couchDbURL));
82
83    // post to this url to login
84    app.post(
85        '/api/login',
86        passport.authenticate('local'),
87        returnUser);
88
89    // return the info for the current logged in user
90    app.get(
91        '/api/login',
92        ensureAuthenticated,
93        returnUser);
94
95    // explicitly logout this user
96    app.post(
97        '/api/logout',
98        ensureAuthenticated,
99        function(req,res){
100            req.logout();
101            res.send(200,{});
102        });
103
104    app.get(
105        '/api/surveyRuns/:id',
106        function(req,res){
107            var id = req.params.id;
108            couch.get(id).then(function(result){
109                if ( result.type === 'SurveyRun') {
110                    res.send(200, result);
111                } else {
112                    res.send(404, {error: "Cannot find survey run "+id});
113                }
114            }, function(error){
115                res.send(404, {error: "Cannot find survey run "+id});
116            });
117        });
118    app.get(
119        '/api/responses/:id',
120        function(req,res){
121            var id = req.params.id;
122            couch.get(id).then(function(result){
123                if ( result.type === 'Response') {
124                    res.send(200, result);
125                } else {
126                    res.send(404, {error: "Cannot find response "+id});
127                }
128            }, function(error){
129                res.send(404, {error: "Cannot find response "+id});
130            });
131        });
132    app.post(
133        '/api/responses',
134        function(req,res){
135            console.log(req.body);
136            couch.post(req.body).then(function(result){
137                res.send(200, result);
138            }, function(error){
139                res.send(500, error);
140            });
141        });
142    app.put(
143        '/api/responses/:id',
144        function(req,res){
145            var id = req.params.id;
146            couch.put(id,req.body).then(function(result){
147                res.send(200, result);
148            }, function(error){
149                res.send(500, error);
150            });
151        });
152    app.delete(
153        '/api/responses/:id',
154        function(req,res){
155            var id = req.params.id;
156            var rev = req.query.rev;
157            couch.delete(id,rev).then(function(result){
158                res.send(200, result);
159            }, function(error){
160                res.send(500, error);
161            });
162        });
163   
164    // generate CSV download of responses
165    app.get(
166        '/api/surveyRuns/:id/responses.csv',
167        ensureAuthenticated,
168        function(req, res) {
169            var id = req.params.id;
170            var url = '_design/responses/_view/by_surveyrun?key='+JSON.stringify(id);
171            couch.get(url).then(function(result){
172                var responses = _.map(result.rows, function(item) { return item.value; });
173                var flatResponses = responsesToVariables(responses);
174                res.set({
175                    'Content-Type': 'text/csv',
176                    'Content-Disposition': 'attachment; filename=surveyRun-'+id+'-responses.csv'
177                });
178                res.status(200);
179                writeObjectsToCSVStream(flatResponses, res);
180                res.end();
181            }, function(error){
182                res.send(404, {error: "Cannot find responses for survey run "+id});
183            });
184        });
185   
186    return app;
187
188};
189
190function responsesToVariables(responses) {
191    return _.map(responses, responseToVariables);
192}
193
194function responseToVariables(response) {
195    var result = flattenObject(response.answers);
196    return result;
197}
198
199function flattenObject(value) {
200    var result = {};
201    (function agg(val,key,res){
202        if ( _.isObject(val) ) {
203            var keys = _.keys(val);
204            // FIXME : dirty hack for questions with only one input
205            if ( keys.length === 1 ) {
206                agg(val[keys[0]],key,res);
207            } else {
208                _.forEach(val, function(v,k){
209                    agg(v,(key ? key+'.' : '')+k,res);
210                });
211            }
212        } else if ( _.isArray(val) ) {
213            // FIXME : dirty hack for questions with only one input
214            if ( val.length === 1 ) {
215                agg(val[0],key,res);
216            } else {
217                _.forEach(val, function(v,i){
218                    agg(v,(key ? key+'.' : '')+i,res);
219                });
220            }
221        } else {
222            res[key] = val;
223        }
224    })(value,null,result);
225    return result;
226}
227
228function writeObjectsToCSVStream(objects, stream, prelude) {
229    var keys = _.chain(objects)
230                .map(_.keys)
231                .flatten()
232                .uniq()
233                .value();
234    var idxs = {};
235    _.forEach(keys, function(key,idx){
236        idxs[key] = idx;
237    });
238    var writer = new CSV.CsvWriter(stream);
239    if ( prelude ) {
240        _.forEach(prelude, function(val,key){
241            writer.writeRecord([key,val]);
242        });
243    }
244    writer.writeRecord(keys);
245    _.forEach(objects, function(obj){
246        var row = [];
247        _.forEach(obj, function(val,key){
248            row[idxs[key]] = val;
249        });
250        writer.writeRecord(row);
251    });
252}
Note: See TracBrowser for help on using the repository browser.