added aggregating for form analytics

This commit is contained in:
David Baldwynn 2017-11-05 19:40:28 -08:00
parent 43ce60b985
commit a40ecad008
10 changed files with 4527 additions and 914 deletions

View file

@ -83,7 +83,7 @@ exports.listSubmissions = function(req, res) {
FormSubmission.find({ form: _form._id }).sort('created').lean().exec(function(err, _submissions) {
if (err) {
console.error(err);
res.status(500).send({
return res.status(500).send({
message: errorHandler.getErrorMessage(err)
});
}
@ -91,6 +91,135 @@ exports.listSubmissions = function(req, res) {
});
};
/**
* Get Visitor Analytics Data for a given Form
*/
exports.getVisitorData = function(req, res) {
Form.aggregate([
{
$match: {
_id: mongoose.Types.ObjectId(req.params.formIdNoMiddleware),
admin: mongoose.Types.ObjectId(req.user.id)
}
},
{
$facet: {
"deviceStatistics": [
{
$unwind: '$analytics.visitors'
},
{
$project: {
_id: 0,
deviceType: '$analytics.visitors.deviceType',
SubmittedTimeElapsed: {
$cond: [
{
$eq: ['$analytics.visitors.isSubmitted', true]
},
'$analytics.visitors.timeElapsed',
0
]
},
SubmittedResponses: {
$cond: [
{
$eq: ['$analytics.visitors.isSubmitted', true]
},
1,
0
]
}
}
},
{
$group: {
_id: "$deviceType",
total_time: { $sum: "$SubmittedTimeElapsed" },
responses: { $sum: "$SubmittedResponses" },
visits: { $sum: 1 }
}
},
{
$project: {
total_time: "$total_time",
responses: "$responses",
visits: "$visits",
average_time: {
$divide : ["$total_time", "$responses"]
},
conversion_rate: {
$divide : ["$responses", "$visits"]
}
}
}
],
"globalStatistics": [
{
$unwind: '$analytics.visitors'
},
{
$project: {
_id: 0,
deviceType: '$analytics.visitors.deviceType',
SubmittedTimeElapsed: {
$cond: [
{
$eq: ['$analytics.visitors.isSubmitted', true]
},
'$analytics.visitors.timeElapsed',
0
]
},
SubmittedResponses: {
$cond: [
{
$eq: ['$analytics.visitors.isSubmitted', true]
},
1,
0
]
}
}
},
{
$group: {
_id: null,
total_time: { $sum: "$SubmittedTimeElapsed" },
responses: { $sum: "$SubmittedResponses" },
visits: { $sum: 1 }
}
},
{
$project: {
_id: 0,
total_time: "$total_time",
responses: "$responses",
visits: "$visits",
average_time: {
$divide : ["$total_time", "$responses"]
},
conversion_rate: {
$divide : ["$responses", "$visits"]
}
}
}
],
}
}
], function(err, results){
if (err) {
console.error(err);
return res.status(500).send({
message: errorHandler.getErrorMessage(err)
});
}
return res.json(results);
});
};
/**
* Create a new form
*/
@ -132,7 +261,7 @@ exports.read = function(req, res) {
var newForm = req.form.toJSON();
if(newForm.admin._id === req.user._id){
if(newForm.admin === req.user._id){
return res.json(newForm);
}
@ -274,6 +403,7 @@ exports.formByID = function(req, res, next, id) {
}
Form.findById(id)
.select('admin title language form_fields startPage endPage hideFooter isLive design analytics.gaCode')
.populate('admin')
.exec(function(err, form) {
if (err) {

View file

@ -7,12 +7,7 @@ var mongoose = require('mongoose'),
Schema = mongoose.Schema,
_ = require('lodash'),
timeStampPlugin = require('../libs/timestamp.server.plugin'),
async = require('async'),
Random = require('random-js'),
mt = Random.engines.mt19937();
mt.autoSeed();
async = require('async');
//Mongoose Models
var FieldSchema = require('./form_field.server.model.js');
@ -47,8 +42,8 @@ var VisitorDataSchema = new Schema({
referrer: {
type: String
},
lastActiveField: {
type: Schema.Types.ObjectId
filledOutFields: {
type: [Schema.Types.ObjectId]
},
timeElapsed: {
type: Number
@ -110,7 +105,7 @@ var FormSchema = new Schema({
type: Schema.Types.ObjectId,
ref: 'FormSubmission'
}],
dfeault: []
default: []
},
admin: {
type: Schema.Types.ObjectId,
@ -196,98 +191,6 @@ var FormSchema = new Schema({
}
}, formSchemaOptions);
/*
** In-Form Analytics Virtual Attributes
*/
FormSchema.virtual('analytics.views').get(function () {
if(this.analytics && this.analytics.visitors && this.analytics.visitors.length > 0){
return this.analytics.visitors.length;
} else {
return 0;
}
});
FormSchema.virtual('analytics.submissions').get(function () {
return this.submissions.length;
});
FormSchema.virtual('analytics.conversionRate').get(function () {
if(this.analytics && this.analytics.visitors && this.analytics.visitors.length > 0){
return this.submissions.length/this.analytics.visitors.length*100;
} else {
return 0;
}
});
FormSchema.virtual('analytics.fields').get(function () {
var fieldDropoffs = [];
var visitors = this.analytics.visitors;
var that = this;
if(!this.form_fields || this.form_fields.length === 0) {
return null;
}
for(var i=0; i<this.form_fields.length; i++){
var field = this.form_fields[i];
if(field && !field.deletePreserved){
var dropoffViews = _.reduce(visitors, function(sum, visitorObj){
if(visitorObj.lastActiveField+'' === field._id+'' && !visitorObj.isSubmitted){
return sum + 1;
}
return sum;
}, 0);
var continueViews, nextIndex;
if(i !== this.form_fields.length-1){
continueViews = _.reduce(visitors, function(sum, visitorObj){
nextIndex = that.form_fields.indexOf(_.find(that.form_fields, function(o) {
return o._id+'' === visitorObj.lastActiveField+'';
}));
if(nextIndex > i){
return sum + 1;
}
return sum;
}, 0);
} else {
continueViews = _.reduce(visitors, function(sum, visitorObj){
if(visitorObj.lastActiveField+'' === field._id+'' && visitorObj.isSubmitted){
return sum + 1;
}
return sum;
}, 0);
}
var totalViews = dropoffViews+continueViews;
var continueRate = 0;
var dropoffRate = 0;
if(totalViews > 0){
continueRate = (continueViews/totalViews*100).toFixed(0);
dropoffRate = (dropoffViews/totalViews*100).toFixed(0);
}
fieldDropoffs[i] = {
dropoffViews: dropoffViews,
responses: continueViews,
totalViews: totalViews,
continueRate: continueRate,
dropoffRate: dropoffRate,
field: field
};
}
}
return fieldDropoffs;
});
FormSchema.plugin(timeStampPlugin, {
createdPath: 'created',
modifiedPath: 'lastModified',

View file

@ -47,6 +47,8 @@ module.exports = function(app) {
.get(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.listSubmissions)
.delete(auth.isAuthenticatedOrApiKey, forms.hasAuthorization, forms.deleteSubmissions);
app.route('/forms/:formIdNoMiddleware([a-zA-Z0-9]+)/visitors')
.get(auth.isAuthenticatedOrApiKey, forms.getVisitorData);
// Slower formId middleware
app.param('formId', forms.formByID);

View file

@ -22,7 +22,6 @@ module.exports = function (io, socket) {
var newVisitor = {
socketId: data.socketId,
referrer: data.referrer,
lastActiveField: data.lastActiveField,
timeElapsed: data.timeElapsed,
isSubmitted: data.isSubmitted,
language: data.language,

670
package-lock.json generated
View file

@ -442,22 +442,6 @@
"integrity": "sha1-PzQU84AyF0O/wQQvmoP/HVgk1GQ=",
"dev": true
},
"bcrypt": {
"version": "0.8.7",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-0.8.7.tgz",
"integrity": "sha1-vDh1qa/Qp7LNIxpqfyGKXOFWsJM=",
"requires": {
"bindings": "1.2.1",
"nan": "2.3.5"
},
"dependencies": {
"nan": {
"version": "2.3.5",
"resolved": "https://registry.npmjs.org/nan/-/nan-2.3.5.tgz",
"integrity": "sha1-gioNwmYpDOTNOhIoLKPn42Rmigg="
}
}
},
"bcrypt-pbkdf": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz",
@ -485,11 +469,6 @@
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.10.0.tgz",
"integrity": "sha1-muuabF6IY4qtFx4Wf1kAq+JINdA="
},
"bindings": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz",
"integrity": "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
},
"bl": {
"version": "0.9.5",
"resolved": "https://registry.npmjs.org/bl/-/bl-0.9.5.tgz",
@ -2731,16 +2710,16 @@
"resolved": "https://registry.npmjs.org/bson/-/bson-0.4.23.tgz",
"integrity": "sha1-5louPHUH/63kEJvHV1p25Q+NqRU="
},
"buffer-shims": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/buffer-shims/-/buffer-shims-1.0.0.tgz",
"integrity": "sha1-mXjOMXOIxkmth5MCjDR37wRKi1E="
},
"builtin-modules": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz",
"integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8="
},
"byt": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/byt/-/byt-0.1.0.tgz",
"integrity": "sha1-tyVTl3uMTciQ0mL9XiDzMAOdFUs="
},
"bytes": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/bytes/-/bytes-2.2.0.tgz",
@ -4086,6 +4065,36 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
"integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY="
},
"envfile": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/envfile/-/envfile-2.1.1.tgz",
"integrity": "sha1-jlrmacHyRZ5YkJWdn4obHAXmj50=",
"requires": {
"ambi": "2.5.0",
"eachr": "3.2.0",
"editions": "1.3.3",
"typechecker": "4.4.1"
},
"dependencies": {
"eachr": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/eachr/-/eachr-3.2.0.tgz",
"integrity": "sha1-LDXkPqCGUW95l8+At6pk1VpKRIQ=",
"requires": {
"editions": "1.3.3",
"typechecker": "4.4.1"
}
},
"typechecker": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/typechecker/-/typechecker-4.4.1.tgz",
"integrity": "sha1-+XuV9RsDhBchLWd9RaNz7nvO1+Y=",
"requires": {
"editions": "1.3.3"
}
}
}
},
"error-ex": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.1.tgz",
@ -6260,14 +6269,6 @@
"jade": "1.11.0"
}
},
"grunt-i18nlint": {
"version": "github:jwarby/grunt-i18nlint#6b398f775c8ca881a16f45a3cb94c5b79ee11e20",
"dev": true,
"requires": {
"chalk": "1.1.3",
"i18n-lint": "git://github.com/jwarby/i18n-lint.git#0a06373c0d880047ad680239c103d60ed414efc1"
}
},
"grunt-karma": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/grunt-karma/-/grunt-karma-0.12.2.tgz",
@ -6700,9 +6701,9 @@
"integrity": "sha1-D1kbGzRL3LPfWXc/Yvu6+Fv0Aos="
},
"hooks-fixed": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-1.1.0.tgz",
"integrity": "sha1-DowVM2cI5mERhf45C0RofdUjDbs="
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hooks-fixed/-/hooks-fixed-2.0.2.tgz",
"integrity": "sha512-YurCM4gQSetcrhwEtpQHhQ4M7Zo7poNGqY4kQGeBS6eZtOcT3tnNs01ThFa0jYBByAiYt1MjMjP/YApG0EnAvQ=="
},
"hosted-git-info": {
"version": "2.5.0",
@ -6888,48 +6889,6 @@
"sprintf-js": "1.1.1"
}
},
"i18n-lint": {
"version": "git://github.com/jwarby/i18n-lint.git#0a06373c0d880047ad680239c103d60ed414efc1",
"dev": true,
"requires": {
"chalk": "1.1.3",
"commander": "2.8.1",
"glob": "5.0.15",
"htmlparser2": "3.8.3",
"lodash": "3.10.1",
"node.extend": "1.1.6"
},
"dependencies": {
"glob": {
"version": "5.0.15",
"resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz",
"integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E=",
"dev": true,
"requires": {
"inflight": "1.0.6",
"inherits": "2.0.3",
"minimatch": "3.0.4",
"once": "1.4.0",
"path-is-absolute": "1.0.1"
}
},
"lodash": {
"version": "3.10.1",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz",
"integrity": "sha1-W/Rejkm6QYnhfUgnid/RW9FAt7Y=",
"dev": true
},
"minimatch": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
"integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
"dev": true,
"requires": {
"brace-expansion": "1.1.8"
}
}
}
},
"iconv-lite": {
"version": "0.2.11",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz",
@ -7114,12 +7073,6 @@
"resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.0.5.tgz",
"integrity": "sha1-X6eM8wG4JceKvDBC2BJyMEnqI8c="
},
"is": {
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/is/-/is-3.2.1.tgz",
"integrity": "sha1-0Kwq1V63sL7JJqUmb2xmKqqD3KU=",
"dev": true
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@ -7662,6 +7615,7 @@
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
"integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
"dev": true,
"requires": {
"graceful-fs": "4.1.11"
},
@ -7670,6 +7624,7 @@
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true,
"optional": true
}
}
@ -7707,9 +7662,9 @@
}
},
"kareem": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-1.0.1.tgz",
"integrity": "sha1-eAXSFbtTIU7Dr5aaHQsfF+PnuVw="
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/kareem/-/kareem-1.5.0.tgz",
"integrity": "sha1-4+QQHZ3P3imXadr0tNtk2JXRdEg="
},
"karma": {
"version": "0.13.22",
@ -8207,6 +8162,11 @@
}
}
},
"lodash.get": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
"integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk="
},
"lodash.isarguments": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz",
@ -8268,11 +8228,6 @@
"integrity": "sha1-euTsJXMC/XkNVXyxDJcQDYV7AFY=",
"dev": true
},
"log-rotate": {
"version": "0.2.7",
"resolved": "https://registry.npmjs.org/log-rotate/-/log-rotate-0.2.7.tgz",
"integrity": "sha1-qttkJZ7qSeVmiEI0LIZNCxQujC0="
},
"log4js": {
"version": "0.6.38",
"resolved": "https://registry.npmjs.org/log4js/-/log4js-0.6.38.tgz",
@ -8315,27 +8270,6 @@
}
}
},
"logrotate-stream": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/logrotate-stream/-/logrotate-stream-0.2.5.tgz",
"integrity": "sha1-/dQueYKn7i64a1X18j3L/8ZuaZA=",
"requires": {
"byt": "0.1.0",
"log-rotate": "0.2.7",
"optimist": "0.6.1"
},
"dependencies": {
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "0.0.8",
"wordwrap": "0.0.2"
}
}
}
},
"longest": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/longest/-/longest-1.0.1.tgz",
@ -8902,70 +8836,79 @@
}
},
"mongoose": {
"version": "4.4.20",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.4.20.tgz",
"integrity": "sha1-6XT/tq6MUPQJgBqEl6mOnztR8t0=",
"version": "4.13.0",
"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-4.13.0.tgz",
"integrity": "sha512-PVUEQ4eS1Bh0Q4IqWRph+li8VMwBxHetdJ1O/P/vE8DktOtBOM1G1G0QOrtQSW1FDrLFSVYkzK4IfI7vJeihQg==",
"requires": {
"async": "1.5.2",
"bson": "0.4.23",
"hooks-fixed": "1.1.0",
"kareem": "1.0.1",
"mongodb": "2.1.18",
"mpath": "0.2.1",
"async": "2.1.4",
"bson": "1.0.4",
"hooks-fixed": "2.0.2",
"kareem": "1.5.0",
"lodash.get": "4.4.2",
"mongodb": "2.2.33",
"mpath": "0.3.0",
"mpromise": "0.5.5",
"mquery": "1.11.0",
"ms": "0.7.1",
"muri": "1.1.0",
"mquery": "2.3.2",
"ms": "2.0.0",
"muri": "1.3.0",
"regexp-clone": "0.0.1",
"sliced": "1.0.1"
},
"dependencies": {
"async": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/async/-/async-2.1.4.tgz",
"integrity": "sha1-LSFgx3iAMuTdbL4lAvH5osj2zeQ=",
"requires": {
"lodash": "4.17.4"
}
},
"bluebird": {
"version": "2.10.2",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.10.2.tgz",
"integrity": "sha1-AkpVFylTCIV/FPkfEQb8O1VfRGs="
"version": "3.5.1",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.1.tgz",
"integrity": "sha512-MKiLiV+I1AA596t9w1sQJ8jkiSr5+ZKi0WKrYGUn6d1Fx+Ij4tIj+m2WMQSGczs5jZVxV339chE8iwk6F64wjA=="
},
"bson": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/bson/-/bson-1.0.4.tgz",
"integrity": "sha1-k8ENOeqltYQVy8QFLz5T5WKwtyw="
},
"debug": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "0.7.1"
"ms": "2.0.0"
}
},
"es6-promise": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.0.2.tgz",
"integrity": "sha1-AQ1YWEI6XxGJeWZfRkhqlcbuK7Y="
},
"isarray": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
"integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
"version": "3.2.1",
"resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-3.2.1.tgz",
"integrity": "sha1-7FYjOGgDKQkgcXDDlEjiREndH8Q="
},
"mongodb": {
"version": "2.1.18",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.1.18.tgz",
"integrity": "sha1-KNQLUVsr5NWmn/3UxTXw30MuQJc=",
"version": "2.2.33",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-2.2.33.tgz",
"integrity": "sha1-tTfEcdNKZlG0jzb9vyl1A0Dgi1A=",
"requires": {
"es6-promise": "3.0.2",
"mongodb-core": "1.3.18",
"readable-stream": "1.0.31"
"es6-promise": "3.2.1",
"mongodb-core": "2.1.17",
"readable-stream": "2.2.7"
}
},
"mongodb-core": {
"version": "1.3.18",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-1.3.18.tgz",
"integrity": "sha1-kGhLO3xzVtZa41Y5HTCw8kiATHo=",
"version": "2.1.17",
"resolved": "https://registry.npmjs.org/mongodb-core/-/mongodb-core-2.1.17.tgz",
"integrity": "sha1-pBizN6FKFJkPtRC5I97mqBMXPfg=",
"requires": {
"bson": "0.4.23",
"bson": "1.0.4",
"require_optional": "1.0.1"
}
},
"mpath": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.2.1.tgz",
"integrity": "sha1-Ok6Ck1mAHeljCcJ6ay4QLon56W4="
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/mpath/-/mpath-0.3.0.tgz",
"integrity": "sha1-elj3iem1/TyUUgY0FXlg8mvV70Q="
},
"mpromise": {
"version": "0.5.5",
@ -8973,12 +8916,12 @@
"integrity": "sha1-9bJCWddjrMIlewoMjG2Gb9UXMuY="
},
"mquery": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-1.11.0.tgz",
"integrity": "sha1-4MZd7bEDftv2z7iCYud3/uI1Udk=",
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/mquery/-/mquery-2.3.2.tgz",
"integrity": "sha512-KXWMypZSvhCuqRtza+HMQZdYw7PfFBjBTFvP31NNAq0OX0/NTIgpcDpkWQ2uTxk6vGQtwQ2elhwhs+ZvCA8OaA==",
"requires": {
"bluebird": "2.10.2",
"debug": "2.2.0",
"bluebird": "3.5.1",
"debug": "2.6.9",
"regexp-clone": "0.0.1",
"sliced": "0.0.5"
},
@ -8990,31 +8933,29 @@
}
}
},
"ms": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg="
"muri": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/muri/-/muri-1.3.0.tgz",
"integrity": "sha512-FiaFwKl864onHFFUV/a2szAl7X0fxVlSKNdhTf+BM8i8goEgYut8u5P9MqQqIYwvaMxjzVESsoEm/2kfkFH1rg=="
},
"readable-stream": {
"version": "1.0.31",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.31.tgz",
"integrity": "sha1-jyUC4LyeOw2huUUgqrtOJgPsr64=",
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.2.7.tgz",
"integrity": "sha1-BwV6y+JGeyIELTb5jFrVBwVOlbE=",
"requires": {
"buffer-shims": "1.0.0",
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "0.0.1",
"string_decoder": "0.10.31"
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "1.0.3",
"util-deprecate": "1.0.2"
}
},
"sliced": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/sliced/-/sliced-1.0.1.tgz",
"integrity": "sha1-CzpmK10Ewxd7GSa+qCsD+Dei70E="
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
}
}
},
@ -9253,15 +9194,6 @@
"resolved": "https://registry.npmjs.org/nocache/-/nocache-2.0.0.tgz",
"integrity": "sha1-ICtIAhoMTL3i34DeFaF0Q8i0OYA="
},
"node.extend": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.1.6.tgz",
"integrity": "sha1-p7iCyC1sk6SGOlUEvV3o7IYli5Y=",
"dev": true,
"requires": {
"is": "3.2.1"
}
},
"nodemailer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-4.0.1.tgz",
@ -9789,283 +9721,6 @@
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
},
"phantomjs": {
"version": "1.9.20",
"resolved": "https://registry.npmjs.org/phantomjs/-/phantomjs-1.9.20.tgz",
"integrity": "sha1-RCSsog4U0lXAsIia9va4lz2hDg0=",
"dev": true,
"requires": {
"extract-zip": "1.5.0",
"fs-extra": "0.26.7",
"hasha": "2.2.0",
"kew": "0.7.0",
"progress": "1.1.8",
"request": "2.67.0",
"request-progress": "2.0.1",
"which": "1.2.14"
},
"dependencies": {
"assert-plus": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz",
"integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=",
"dev": true
},
"async": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/async/-/async-2.5.0.tgz",
"integrity": "sha512-e+lJAJeNWuPCNyxZKOBdaJGyLGHugXVQtrAwtuAe2vhxTYxFTKE73p8JuTmdH0qdQZtDvI4dhJwjZc5zsfIsYw==",
"dev": true,
"requires": {
"lodash": "4.17.4"
}
},
"aws-sign2": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz",
"integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=",
"dev": true
},
"bl": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz",
"integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=",
"dev": true,
"requires": {
"readable-stream": "2.0.6"
}
},
"boom": {
"version": "2.10.1",
"resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz",
"integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=",
"dev": true,
"requires": {
"hoek": "2.16.3"
}
},
"caseless": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz",
"integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=",
"dev": true
},
"commander": {
"version": "2.11.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.11.0.tgz",
"integrity": "sha512-b0553uYA5YAEGgyYIGYROzKQ7X5RAqedkfjiZxwi0kL1g3bOaBNNZfYkzt/CL0umgD5wc9Jec2FbB98CjkMRvQ==",
"dev": true
},
"concat-stream": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz",
"integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=",
"dev": true,
"requires": {
"inherits": "2.0.3",
"readable-stream": "2.0.6",
"typedarray": "0.0.6"
}
},
"cryptiles": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz",
"integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=",
"dev": true,
"requires": {
"boom": "2.10.1"
}
},
"debug": {
"version": "0.7.4",
"resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz",
"integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=",
"dev": true
},
"extract-zip": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz",
"integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=",
"dev": true,
"requires": {
"concat-stream": "1.5.0",
"debug": "0.7.4",
"mkdirp": "0.5.0",
"yauzl": "2.4.1"
}
},
"form-data": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz",
"integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=",
"dev": true,
"requires": {
"async": "2.5.0",
"combined-stream": "1.0.5",
"mime-types": "2.1.17"
}
},
"fs-extra": {
"version": "0.26.7",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz",
"integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=",
"dev": true,
"requires": {
"graceful-fs": "4.1.11",
"jsonfile": "2.4.0",
"klaw": "1.3.1",
"path-is-absolute": "1.0.1",
"rimraf": "2.2.8"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=",
"dev": true
},
"har-validator": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz",
"integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=",
"dev": true,
"requires": {
"chalk": "1.1.3",
"commander": "2.11.0",
"is-my-json-valid": "2.16.1",
"pinkie-promise": "2.0.1"
}
},
"hawk": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz",
"integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=",
"dev": true,
"requires": {
"boom": "2.10.1",
"cryptiles": "2.0.5",
"hoek": "2.16.3",
"sntp": "1.0.9"
}
},
"hoek": {
"version": "2.16.3",
"resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz",
"integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=",
"dev": true
},
"http-signature": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz",
"integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=",
"dev": true,
"requires": {
"assert-plus": "0.2.0",
"jsprim": "1.4.1",
"sshpk": "1.13.1"
}
},
"mkdirp": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz",
"integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=",
"dev": true,
"requires": {
"minimist": "0.0.8"
}
},
"node-uuid": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz",
"integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=",
"dev": true
},
"qs": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz",
"integrity": "sha1-gB/uAw4LlFDWOFrcSKTMVbRK7fw=",
"dev": true
},
"readable-stream": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz",
"integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=",
"dev": true,
"requires": {
"core-util-is": "1.0.2",
"inherits": "2.0.3",
"isarray": "1.0.0",
"process-nextick-args": "1.0.7",
"string_decoder": "0.10.31",
"util-deprecate": "1.0.2"
}
},
"request": {
"version": "2.67.0",
"resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz",
"integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=",
"dev": true,
"requires": {
"aws-sign2": "0.6.0",
"bl": "1.0.3",
"caseless": "0.11.0",
"combined-stream": "1.0.5",
"extend": "3.0.1",
"forever-agent": "0.6.1",
"form-data": "1.0.1",
"har-validator": "2.0.6",
"hawk": "3.1.3",
"http-signature": "1.1.1",
"is-typedarray": "1.0.0",
"isstream": "0.1.2",
"json-stringify-safe": "5.0.1",
"mime-types": "2.1.17",
"node-uuid": "1.4.8",
"oauth-sign": "0.8.2",
"qs": "5.2.1",
"stringstream": "0.0.5",
"tough-cookie": "2.2.2",
"tunnel-agent": "0.4.3"
}
},
"sntp": {
"version": "1.0.9",
"resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz",
"integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=",
"dev": true,
"requires": {
"hoek": "2.16.3"
}
},
"string_decoder": {
"version": "0.10.31",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
"integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=",
"dev": true
},
"tough-cookie": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz",
"integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=",
"dev": true
},
"tunnel-agent": {
"version": "0.4.3",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz",
"integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=",
"dev": true
},
"which": {
"version": "1.2.14",
"resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz",
"integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=",
"dev": true,
"requires": {
"isexe": "2.0.0"
}
}
}
},
"phantomjs-prebuilt": {
"version": "2.1.15",
"resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.15.tgz",
@ -11928,66 +11583,6 @@
"upper-case": "1.1.3"
}
},
"swig": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/swig/-/swig-1.4.2.tgz",
"integrity": "sha1-QIXKBFM2kQS11IPihBs5t64aq6U=",
"requires": {
"optimist": "0.6.1",
"uglify-js": "2.4.24"
},
"dependencies": {
"async": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E="
},
"camelcase": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz",
"integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk="
},
"optimist": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
"integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=",
"requires": {
"minimist": "0.0.8",
"wordwrap": "0.0.2"
}
},
"source-map": {
"version": "0.1.34",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz",
"integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=",
"requires": {
"amdefine": "1.0.1"
}
},
"uglify-js": {
"version": "2.4.24",
"resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz",
"integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=",
"requires": {
"async": "0.2.10",
"source-map": "0.1.34",
"uglify-to-browserify": "1.0.2",
"yargs": "3.5.4"
}
},
"yargs": {
"version": "3.5.4",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz",
"integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=",
"requires": {
"camelcase": "1.2.1",
"decamelize": "1.2.0",
"window-size": "0.1.0",
"wordwrap": "0.0.2"
}
}
}
},
"taskgroup": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/taskgroup/-/taskgroup-4.3.1.tgz",
@ -12566,9 +12161,6 @@
"string-width": "1.0.2"
}
},
"wildcard-subdomains": {
"version": "github:tellform/wildcard-subdomains#af33bb3e040da9f11387f920f3801c4d080c0423"
},
"window-size": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz",
@ -12599,40 +12191,6 @@
}
}
},
"winston-logrotate": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/winston-logrotate/-/winston-logrotate-1.3.0.tgz",
"integrity": "sha1-Sy9cLqrvSS9bdcccpzd48wXI/4A=",
"requires": {
"bluebird": "2.9.30",
"fs-extra": "0.24.0",
"logrotate-stream": "0.2.5",
"winston": "2.4.0"
},
"dependencies": {
"bluebird": {
"version": "2.9.30",
"resolved": "https://registry.npmjs.org/bluebird/-/bluebird-2.9.30.tgz",
"integrity": "sha1-7dqHXsmq0fKc8fVtboL7qysN9VY="
},
"fs-extra": {
"version": "0.24.0",
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.24.0.tgz",
"integrity": "sha1-1OQ0KpZnXLeEZjOmCZJJMytTmVI=",
"requires": {
"graceful-fs": "4.1.11",
"jsonfile": "2.4.0",
"path-is-absolute": "1.0.1",
"rimraf": "2.2.8"
}
},
"graceful-fs": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz",
"integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg="
}
}
},
"wiredep": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/wiredep/-/wiredep-4.0.0.tgz",

View file

@ -38,6 +38,7 @@
"deep-diff": "^0.3.4",
"dotenv": "^2.0.0",
"email-verification": "github:tellform/node-email-verification",
"envfile": "^2.1.1",
"express": "~4.13.3",
"express-session": "~1.12.1",
"glob": "^7.0.3",
@ -57,7 +58,7 @@
"main-bower-files": "~2.9.0",
"method-override": "~2.3.0",
"mkdirp": "^0.5.1",
"mongoose": "~4.4.19",
"mongoose": "^4.13.0",
"morgan": "~1.8.1",
"nodemailer": "~4.0.0",
"passport": "~0.3.0",

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -14,6 +14,10 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
masterChecker: false,
rows: []
};
$scope.analyticsData = {
deviceStatistics: [],
globalStatistics: []
};
$scope.deletionInProgress = false;
$scope.waitingForDeletion = false;
@ -73,11 +77,11 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
method: 'GET',
url: '/forms/'+$scope.myform._id+'/visitors'
}).then(function successCallback(response) {
var defaultFormFields = _.cloneDeep($scope.myform.form_fields);
var data = response.data || [];
var visitors = response.data || [];
$scope.visitors = visitors;
$scope.analyticsData = data[0];
$scope.analyticsData.globalStatistics = $scope.analyticsData.globalStatistics[0];
$scope.analyticsData.deviceStatistics = formatDeviceStatistics($scope.analyticsData.deviceStatistics);
});
};
@ -102,26 +106,12 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
/*
** Analytics Functions
*/
$scope.AverageTimeElapsed = (function(){
var totalTime = 0;
var numSubmissions = $scope.table.rows.length;
for(var i=0; i<$scope.table.rows.length; i++){
totalTime += $scope.table.rows[i].timeElapsed;
}
if(numSubmissions === 0) {
return 0;
}
return (totalTime/numSubmissions).toFixed(0);
})();
$scope.DeviceStatistics = (function(){
var formatDeviceStatistics = function(deviceStatData){
var newStatItem = function(){
return {
visits: 0,
responses: 0,
completion: 0,
conversion_rate: 0,
average_time: 0,
total_time: 0
};
@ -134,30 +124,16 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
other: newStatItem()
};
if($scope.myform.analytics && $scope.myform.analytics.visitors) {
var visitors = $scope.myform.analytics.visitors;
for (var i = 0; i < visitors.length; i++) {
var visitor = visitors[i];
var deviceType = visitor.deviceType;
stats[deviceType].visits++;
if (visitor.isSubmitted) {
stats[deviceType].total_time = stats[deviceType].total_time + visitor.timeElapsed;
stats[deviceType].responses++;
}
if(stats[deviceType].visits) {
stats[deviceType].completion = 100*(stats[deviceType].responses / stats[deviceType].visits).toFixed(2);
}
if(stats[deviceType].responses){
stats[deviceType].average_time = (stats[deviceType].total_time / stats[deviceType].responses).toFixed(0);
if(deviceStatData.length){
for(var i=0; i<deviceStatData.length; i++){
var currDevice = deviceStatData[i];
if(stats[currDevice._id]){
stats[currDevice._id] = currDevice;
}
}
}
return stats;
})();
};
/*
** Table Functions

View file

@ -19,19 +19,19 @@
</div>
<div class="col-xs-12 header-numbers">
<div class="col-xs-3">
{{myform.analytics.visitors.length}}
{{analyticsData.globalStatistics.visits | number:0}}
</div>
<div class="col-xs-3">
{{myform.analytics.submissions}}
{{analyticsData.globalStatistics.responses | number:0}}
</div>
<div class="col-xs-3">
{{myform.analytics.conversionRate | number:0}}%
{{analyticsData.globalStatistics.conversion_rate | number:2}}
</div>
<div class="col-xs-3">
{{ AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}}
{{analyticsData.globalStatistics.average_time | secondsToDateTime | date:'mm:ss'}}
</div>
</div>
<div class="col-xs-12 detailed-title">
@ -58,7 +58,7 @@
{{ 'UNIQUE_VISITS' | translate }}
</div>
<div class="row">
{{DeviceStatistics.desktop.visits}}
{{analyticsData.deviceStatistics.desktop.visits | number:0}}
</div>
</div>
@ -67,7 +67,7 @@
{{ 'UNIQUE_VISITS' | translate }}
</div>
<div class="row">
{{DeviceStatistics.tablet.visits}}
{{analyticsData.deviceStatistics.tablet.visits | number:0}}
</div>
</div>
@ -76,7 +76,7 @@
{{ 'UNIQUE_VISITS' | translate }}
</div>
<div class="row">
{{DeviceStatistics.tablet.visits}}
{{analyticsData.deviceStatistics.tablet.visits | number:0}}
</div>
</div>
@ -85,7 +85,7 @@
{{ 'UNIQUE_VISITS' | translate }}
</div>
<div class="row">
{{DeviceStatistics.other.visits}}
{{analyticsData.deviceStatistics.other.visits | number:0}}
</div>
</div>
</div>
@ -96,7 +96,7 @@
{{ 'RESPONSES' | translate }}
</div>
<div class="row">
{{DeviceStatistics.desktop.responses}}
{{analyticsData.deviceStatistics.desktop.responses | number:0}}
</div>
</div>
@ -105,7 +105,7 @@
{{ 'RESPONSES' | translate }}
</div>
<div class="row">
{{DeviceStatistics.tablet.responses}}
{{analyticsData.deviceStatistics.tablet.responses | number:0}}
</div>
</div>
@ -114,7 +114,7 @@
{{ 'RESPONSES' | translate }}
</div>
<div class="row">
{{DeviceStatistics.phone.responses}}
{{analyticsData.deviceStatistics.phone.responses | number:0}}
</div>
</div>
@ -123,7 +123,7 @@
{{ 'RESPONSES' | translate }}
</div>
<div class="row">
{{DeviceStatistics.other.responses}}
{{analyticsData.deviceStatistics.other.responses | number:0}}
</div>
</div>
</div>
@ -134,7 +134,7 @@
{{ 'COMPLETION_RATE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.desktop.completion}}%
{{analyticsData.deviceStatistics.desktop.conversion_rate | number:2}}%
</div>
</div>
@ -143,7 +143,7 @@
{{ 'COMPLETION_RATE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.tablet.completion}}%
{{analyticsData.deviceStatistics.tablet.conversion_rate | number:2}}%
</div>
</div>
@ -152,7 +152,7 @@
{{ 'COMPLETION_RATE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.phone.completion}}%
{{analyticsData.deviceStatistics.phone.conversion_rate | number:2}}%
</div>
</div>
@ -161,7 +161,7 @@
{{ 'COMPLETION_RATE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.other.completion}}%
{{analyticsData.deviceStatistics.other.conversion_rate | number:2}}%
</div>
</div>
</div>
@ -172,7 +172,7 @@
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
{{analyticsData.deviceStatistics.desktop.average_time | secondsToDateTime | date:'mm:ss'}}
</div>
</div>
@ -181,7 +181,7 @@
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
{{analyticsData.deviceStatistics.tablet.average_time | secondsToDateTime | date:'mm:ss'}}
</div>
</div>
@ -190,7 +190,7 @@
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
{{analyticsData.deviceStatistics.phone.average_time | secondsToDateTime | date:'mm:ss'}}
</div>
</div>
@ -199,11 +199,12 @@
{{ 'AVERAGE_TIME_TO_COMPLETE' | translate }}
</div>
<div class="row">
{{DeviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}
{{analyticsData.deviceStatistics.other.average_time | secondsToDateTime | date:'mm:ss'}}
</div>
</div>
</div>
<!--
<div class="col-xs-12 field-title-row">
<div class="col-xs-3">
@ -222,7 +223,6 @@
</div>
<div class="col-xs-12 field-detailed-row" ng-repeat="fieldStats in myform.analytics.fields">
<div class="col-xs-3">
{{fieldStats.field.title}}
</div>
@ -236,7 +236,10 @@
{{fieldStats.continueRate}}%
</div>
</div>
-->
</div>
<br>
<div class="row table-tools">
<div class="col-xs-2">