mirror of
https://github.com/HabitRPG/habitica.git
synced 2025-12-19 15:48:04 +01:00
data export commit, supports exporting task history via csv
*adds the following new dependencies: moment, express-csv *recommend replacing functionality of 'relative-date' with features from 'moment' *supports retrieval of CSV history via the API and in browser **/api/v1/export/history (requires typical API authorization) **/export/history.csv (requires session authorization) *adds new routes for data export
This commit is contained in:
@@ -42,7 +42,9 @@
|
|||||||
"validator": "~1.5.1",
|
"validator": "~1.5.1",
|
||||||
"nodemailer": "~0.5.2",
|
"nodemailer": "~0.5.2",
|
||||||
"grunt-cli": "~0.1.9",
|
"grunt-cli": "~0.1.9",
|
||||||
"paypal-ipn": "~1.0.1"
|
"paypal-ipn": "~1.0.1",
|
||||||
|
"express-csv": "~0.6.0",
|
||||||
|
"moment": "2.4.0"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"subdomain": "habitrpg",
|
"subdomain": "habitrpg",
|
||||||
|
|||||||
@@ -61,6 +61,10 @@ window.habitrpg = angular.module('habitrpg',
|
|||||||
url: "/profile",
|
url: "/profile",
|
||||||
templateUrl: "partials/options.profile.profile.html"
|
templateUrl: "partials/options.profile.profile.html"
|
||||||
})
|
})
|
||||||
|
.state('options.profile.data', {
|
||||||
|
url: "/profile/data",
|
||||||
|
templateUrl: "partials/options.profile.data.html"
|
||||||
|
})
|
||||||
|
|
||||||
// Options > Groups
|
// Options > Groups
|
||||||
.state('options.social', {
|
.state('options.social', {
|
||||||
|
|||||||
51
src/controllers/dataexport.js
Normal file
51
src/controllers/dataexport.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
var _ = require('lodash');
|
||||||
|
var csv = require('express-csv');
|
||||||
|
var User = require('../models/user').model;
|
||||||
|
var nconf = require('nconf');
|
||||||
|
var moment = require('moment');
|
||||||
|
var dataexport = module.exports;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
Data export
|
||||||
|
------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
dataexport.history = function(req, res) {
|
||||||
|
var user = res.locals.user;
|
||||||
|
var output = [
|
||||||
|
["Task Name", "Task ID", "Task Type", "Date", "Value"]
|
||||||
|
];
|
||||||
|
_.each(user.tasks, function(task) {
|
||||||
|
_.each(task.history, function(history) {
|
||||||
|
output.push(
|
||||||
|
[task.text, task.id, task.type, moment(history.date).format("MM-DD-YYYY HH:mm:ss"), history.value]
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return res.csv(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
dataexport.auth = function(req, res, next) { //[todo] there is probably a more elegant way of doing this...
|
||||||
|
var uid;
|
||||||
|
uid = req.session.userId;
|
||||||
|
if (!(req.session && req.session.userId)) {
|
||||||
|
return res.json(401, "You must be logged in.");
|
||||||
|
}
|
||||||
|
return User.findOne({
|
||||||
|
_id: uid,
|
||||||
|
}, function(err, user) {
|
||||||
|
if (err) {
|
||||||
|
return res.json(500, {
|
||||||
|
err: err
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (_.isEmpty(user)) {
|
||||||
|
return res.json(401, "No user found.");
|
||||||
|
}
|
||||||
|
|
||||||
|
res.locals.user = user;
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
};
|
||||||
@@ -4,6 +4,7 @@ var user = require('../controllers/user');
|
|||||||
var groups = require('../controllers/groups');
|
var groups = require('../controllers/groups');
|
||||||
var auth = require('../controllers/auth');
|
var auth = require('../controllers/auth');
|
||||||
var challenges = require('../controllers/challenges');
|
var challenges = require('../controllers/challenges');
|
||||||
|
var dataexport = require('../controllers/dataexport');
|
||||||
var nconf = require('nconf');
|
var nconf = require('nconf');
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -25,6 +26,9 @@ router.get('/status', function(req, res) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/* Data export */
|
||||||
|
router.get('/export/history',auth.auth,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
|
||||||
|
|
||||||
/* Scoring*/
|
/* Scoring*/
|
||||||
router.post('/user/task/:id/:direction', auth.auth, cron, user.scoreTask);
|
router.post('/user/task/:id/:direction', auth.auth, cron, user.scoreTask);
|
||||||
router.post('/user/tasks/:id/:direction', auth.auth, cron, user.scoreTask);
|
router.post('/user/tasks/:id/:direction', auth.auth, cron, user.scoreTask);
|
||||||
|
|||||||
9
src/routes/dataexport.js
Normal file
9
src/routes/dataexport.js
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
var express = require('express');
|
||||||
|
var router = new express.Router();
|
||||||
|
var dataexport = require('../controllers/dataexport');
|
||||||
|
var nconf = require('nconf');
|
||||||
|
|
||||||
|
/* Data export */
|
||||||
|
router.get('/history.csv',dataexport.auth,dataexport.history); //[todo] encode data output options in the data controller and use these to build routes
|
||||||
|
|
||||||
|
module.exports = router;
|
||||||
@@ -116,6 +116,7 @@ if ("development" === app.get("env")) {
|
|||||||
app.use(require('./routes/pages').middleware);
|
app.use(require('./routes/pages').middleware);
|
||||||
app.use(require('./routes/auth').middleware);
|
app.use(require('./routes/auth').middleware);
|
||||||
app.use('/api/v1', require('./routes/api').middleware);
|
app.use('/api/v1', require('./routes/api').middleware);
|
||||||
|
app.use('/export', require('./routes/dataexport').middleware);
|
||||||
app.use(require('./controllers/deprecated').middleware);
|
app.use(require('./controllers/deprecated').middleware);
|
||||||
server = http.createServer(app).listen(app.get("port"), function() {
|
server = http.createServer(app).listen(app.get("port"), function() {
|
||||||
return console.log("Express server listening on port " + app.get("port"));
|
return console.log("Express server listening on port " + app.get("port"));
|
||||||
|
|||||||
@@ -121,6 +121,9 @@ script(id='partials/options.profile.profile.html', type='text/ng-template')
|
|||||||
a(ng-click='removeWebsite($index)')
|
a(ng-click='removeWebsite($index)')
|
||||||
i.icon-remove
|
i.icon-remove
|
||||||
|
|
||||||
|
script(id='partials/options.profile.data.html', type="text/ng-template")
|
||||||
|
a(href="/export/history.csv") Export History (CSV)
|
||||||
|
|
||||||
script(id='partials/options.profile.html', type="text/ng-template")
|
script(id='partials/options.profile.html', type="text/ng-template")
|
||||||
ul.nav.nav-tabs
|
ul.nav.nav-tabs
|
||||||
li(ng-class="{ active: $state.includes('options.profile.avatar') }")
|
li(ng-class="{ active: $state.includes('options.profile.avatar') }")
|
||||||
@@ -132,6 +135,9 @@ script(id='partials/options.profile.html', type="text/ng-template")
|
|||||||
li(ng-class="{ active: $state.includes('options.profile.profile') }")
|
li(ng-class="{ active: $state.includes('options.profile.profile') }")
|
||||||
a(ui-sref='options.profile.profile')
|
a(ui-sref='options.profile.profile')
|
||||||
| Profile
|
| Profile
|
||||||
|
li(ng-class="{ active: $state.includes('options.profile.data') }")
|
||||||
|
a(ui-sref='options.profile.data')
|
||||||
|
| My Data
|
||||||
|
|
||||||
.tab-content
|
.tab-content
|
||||||
.tab-pane.active
|
.tab-pane.active
|
||||||
|
|||||||
Reference in New Issue
Block a user