fixed analytics service
This commit is contained in:
parent
faa306302b
commit
f464deb959
|
@ -151,7 +151,8 @@ exports.read = function(req, res) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var newForm = req.form.toJSON({virtuals : true});
|
var newForm = req.form.toJSON();
|
||||||
|
console.log(newForm.analytics);
|
||||||
newForm.submissions = _submissions;
|
newForm.submissions = _submissions;
|
||||||
|
|
||||||
if (req.userId) {
|
if (req.userId) {
|
||||||
|
|
|
@ -270,17 +270,6 @@ FormSchema.plugin(mUtilities.timestamp, {
|
||||||
useVirtual: false
|
useVirtual: false
|
||||||
});
|
});
|
||||||
|
|
||||||
//Delete template PDF of current Form
|
|
||||||
FormSchema.pre('remove', function (next) {
|
|
||||||
if(this.pdf && process.env.NODE_ENV === 'development'){
|
|
||||||
//Delete template form
|
|
||||||
fs.unlink(this.pdf.path, function(err){
|
|
||||||
if (err) throw err;
|
|
||||||
console.log('successfully deleted', this.pdf.path);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var _original;
|
var _original;
|
||||||
|
|
||||||
function getDeletedIndexes(needle, haystack){
|
function getDeletedIndexes(needle, haystack){
|
||||||
|
@ -296,7 +285,6 @@ function getDeletedIndexes(needle, haystack){
|
||||||
return deletedIndexes;
|
return deletedIndexes;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Move PDF to permanent location after new template is uploaded
|
|
||||||
FormSchema.pre('save', function (next) {
|
FormSchema.pre('save', function (next) {
|
||||||
var that = this;
|
var that = this;
|
||||||
|
|
||||||
|
@ -316,120 +304,6 @@ FormSchema.pre('save', function (next) {
|
||||||
}, function(cb) {
|
}, function(cb) {
|
||||||
return cb(null);
|
return cb(null);
|
||||||
},
|
},
|
||||||
function(cb) {
|
|
||||||
if (that.pdf) {
|
|
||||||
async.series([
|
|
||||||
function (callback) {
|
|
||||||
if (that.isModified('pdf') && that.pdf.path) {
|
|
||||||
|
|
||||||
var new_filename = that.title.replace(/ /g, '') + '_template.pdf';
|
|
||||||
|
|
||||||
var newDestination = path.join(config.pdfUploadPath, that.admin.username.replace(/ /g, ''), that.title.replace(/ /g, '')),
|
|
||||||
stat = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
stat = fs.statSync(newDestination);
|
|
||||||
} catch (err) {
|
|
||||||
mkdirp.sync(newDestination);
|
|
||||||
}
|
|
||||||
if (stat && !stat.isDirectory()) {
|
|
||||||
return callback(new Error('Directory cannot be created because an inode of a different type exists at "' + config.pdfUploadPath + '"'), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
var old_path = that.pdf.path;
|
|
||||||
fs.move(old_path, path.join(newDestination, new_filename), {clobber: true}, function (err) {
|
|
||||||
if (err) {
|
|
||||||
console.error(err);
|
|
||||||
return callback(new Error(err.message), 'task1');
|
|
||||||
} else {
|
|
||||||
that.pdf.path = path.join(newDestination, new_filename);
|
|
||||||
that.pdf.name = new_filename;
|
|
||||||
|
|
||||||
return callback(null, 'task1');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return callback(null, 'task1');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
function (callback) {
|
|
||||||
if (that.isGenerated) {
|
|
||||||
that.pdf.path = config.pdfUploadPath + that.admin.username.replace(/ /g, '') + '/' + that.title.replace(/ /g, '') + '/' + that.title.replace(/ /g, '') + '_template.pdf';
|
|
||||||
that.pdf.name = that.title.replace(/ /g, '') + '_template.pdf';
|
|
||||||
var _typeConvMap = {
|
|
||||||
'Multiline': 'textarea',
|
|
||||||
'Text': 'textfield',
|
|
||||||
'Button': 'checkbox',
|
|
||||||
'Choice': 'radio',
|
|
||||||
'Password': 'password',
|
|
||||||
'FileSelect': 'filefield',
|
|
||||||
'Radio': 'radio'
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
pdfFiller.generateFieldJson(that.pdf.path, '', function (err, _form_fields) {
|
|
||||||
|
|
||||||
//console.log(that.pdf.path);
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
return callback(new Error(err.message), null);
|
|
||||||
} else if (!_form_fields.length || _form_fields === undefined || _form_fields === null) {
|
|
||||||
return callback(new Error('Generated formfields is empty'), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('autogenerating form');
|
|
||||||
|
|
||||||
//Map PDF field names to FormField field names
|
|
||||||
for (var i = 0; i < _form_fields.length; i++) {
|
|
||||||
var _field = _form_fields[i];
|
|
||||||
|
|
||||||
//Convert types from FDF to 'FormField' types
|
|
||||||
if (_typeConvMap[_field.fieldType + '']) {
|
|
||||||
_field.fieldType = _typeConvMap[_field.fieldType + ''];
|
|
||||||
}
|
|
||||||
|
|
||||||
var new_field = {};
|
|
||||||
new_field.title = _field.fieldType + ' ' + Math.abs(mt());
|
|
||||||
new_field.fieldValue = '';
|
|
||||||
new_field.disabled = false;
|
|
||||||
new_field.fieldType = _field.fieldType;
|
|
||||||
new_field.deletePreserved = false;
|
|
||||||
new_field.required = false;
|
|
||||||
_form_fields[i] = new_field;
|
|
||||||
}
|
|
||||||
|
|
||||||
that.form_fields = _form_fields;
|
|
||||||
|
|
||||||
that.isGenerated = false;
|
|
||||||
return callback(null, 'task2');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return callback(null, 'task2');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
], function (err, results) {
|
|
||||||
if (err) {
|
|
||||||
return cb(new Error({
|
|
||||||
message: err.message
|
|
||||||
}));
|
|
||||||
} else {
|
|
||||||
//console.log('ending form save1');
|
|
||||||
return cb();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if (_original) {
|
|
||||||
if (_original.hasOwnProperty('pdf')) {
|
|
||||||
fs.remove(_original.pdf.path, function (err) {
|
|
||||||
if (err) return cb(err);
|
|
||||||
console.log('file at ' + _original.pdf.path + ' successfully deleted');
|
|
||||||
return cb();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else return cb();
|
|
||||||
}
|
|
||||||
else return cb();
|
|
||||||
},
|
|
||||||
function(cb) {
|
function(cb) {
|
||||||
var hasIds = true;
|
var hasIds = true;
|
||||||
for(var i=0; i<that.form_fields.length; i++){
|
for(var i=0; i<that.form_fields.length; i++){
|
||||||
|
|
|
@ -31,6 +31,7 @@ module.exports = function (io, socket) {
|
||||||
deviceType: data.deviceType
|
deviceType: data.deviceType
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
form.analytics.visitors.push(newVisitor);
|
form.analytics.visitors.push(newVisitor);
|
||||||
|
|
||||||
form.save(function (err) {
|
form.save(function (err) {
|
||||||
|
@ -39,6 +40,7 @@ module.exports = function (io, socket) {
|
||||||
throw new Error(errorHandler.getErrorMessage(err));
|
throw new Error(errorHandler.getErrorMessage(err));
|
||||||
}
|
}
|
||||||
console.log('\n\nVisitor data successfully added!');
|
console.log('\n\nVisitor data successfully added!');
|
||||||
|
console.log(newVisitor);
|
||||||
|
|
||||||
delete visitorsData[socket.id];
|
delete visitorsData[socket.id];
|
||||||
|
|
||||||
|
|
|
@ -41,7 +41,8 @@
|
||||||
"deep-diff": "^0.3.4",
|
"deep-diff": "^0.3.4",
|
||||||
"mathjs": "^3.4.1",
|
"mathjs": "^3.4.1",
|
||||||
"jsep": "^0.3.1",
|
"jsep": "^0.3.1",
|
||||||
"ngclipboard": "^1.1.1"
|
"ngclipboard": "^1.1.1",
|
||||||
|
"mobile-detect": "^1.3.3"
|
||||||
},
|
},
|
||||||
"resolutions": {
|
"resolutions": {
|
||||||
"angular-bootstrap": "^0.14.0",
|
"angular-bootstrap": "^0.14.0",
|
||||||
|
|
170
public/dist/application.js
vendored
170
public/dist/application.js
vendored
File diff suppressed because one or more lines are too long
6
public/dist/application.min.js
vendored
6
public/dist/application.min.js
vendored
File diff suppressed because one or more lines are too long
27
public/dist/form-application.js
vendored
27
public/dist/form-application.js
vendored
File diff suppressed because one or more lines are too long
6
public/dist/form-application.min.js
vendored
6
public/dist/form-application.min.js
vendored
File diff suppressed because one or more lines are too long
|
@ -13,13 +13,36 @@
|
||||||
// Create a controller method for sending visitor data
|
// Create a controller method for sending visitor data
|
||||||
function send(form, lastActiveIndex, timeElapsed) {
|
function send(form, lastActiveIndex, timeElapsed) {
|
||||||
|
|
||||||
|
var lang = window.navigator.userLanguage || window.navigator.language;
|
||||||
|
lang = lang.slice(0,2);
|
||||||
|
|
||||||
|
var userAgentString = navigator.userAgent;
|
||||||
|
var md = new MobileDetect(userAgentString);
|
||||||
|
var deviceType = 'other';
|
||||||
|
|
||||||
|
if (md.tablet()){
|
||||||
|
deviceType = 'tablet';
|
||||||
|
} else if (md.mobile()) {
|
||||||
|
deviceType = 'mobile';
|
||||||
|
} else if (window.screenX != 0) {
|
||||||
|
deviceType = 'desktop';
|
||||||
|
}
|
||||||
|
|
||||||
|
$.ajaxSetup( { "async": false } );
|
||||||
|
var result = $.getJSON('myUrl');
|
||||||
|
var geoData = $.getJSON('//freegeoip.net/json/?callback=?');
|
||||||
|
$.ajaxSetup( { "async": true } );
|
||||||
|
|
||||||
// Create a new message object
|
// Create a new message object
|
||||||
var visitorData = {
|
var visitorData = {
|
||||||
referrer: document.referrer,
|
referrer: document.referrer,
|
||||||
isSubmitted: form.submitted,
|
isSubmitted: form.submitted,
|
||||||
formId: form._id,
|
formId: form._id,
|
||||||
lastActiveField: form.form_fields[lastActiveIndex]._id,
|
lastActiveField: form.form_fields[lastActiveIndex]._id,
|
||||||
timeElapsed: timeElapsed
|
timeElapsed: timeElapsed,
|
||||||
|
language: lang,
|
||||||
|
deviceType: deviceType,
|
||||||
|
ipAddr: geoData.ip
|
||||||
};
|
};
|
||||||
Socket.emit('form-visitor-data', visitorData);
|
Socket.emit('form-visitor-data', visitorData);
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
scope: {
|
scope: {
|
||||||
user:'=',
|
user:'=',
|
||||||
myform: '@'
|
myform: '='
|
||||||
},
|
},
|
||||||
controller: function($scope){
|
controller: function($scope){
|
||||||
|
|
||||||
|
@ -23,8 +23,6 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
|
||||||
Forms.get({
|
Forms.get({
|
||||||
formId: $stateParams.formId
|
formId: $stateParams.formId
|
||||||
}, function(form){
|
}, function(form){
|
||||||
console.log('running init controller');
|
|
||||||
console.log(form);
|
|
||||||
$scope.myform = form;
|
$scope.myform = form;
|
||||||
var defaultFormFields = _.cloneDeep($scope.myform.form_fields);
|
var defaultFormFields = _.cloneDeep($scope.myform.form_fields);
|
||||||
|
|
||||||
|
@ -42,12 +40,69 @@ angular.module('forms').directive('editSubmissionsFormDirective', ['$rootScope',
|
||||||
|
|
||||||
$scope.table.rows = submissions;
|
$scope.table.rows = submissions;
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Analytics Functions
|
||||||
|
*/
|
||||||
|
|
||||||
|
$scope.AverageTimeElapsed = (function(){
|
||||||
|
var totalTime = 0;
|
||||||
|
var numSubmissions = $scope.table.rows.length;
|
||||||
|
|
||||||
|
console.log("AverageTimeElapsed");
|
||||||
|
console.log($scope.table.rows);
|
||||||
|
|
||||||
|
for(var i=0; i<$scope.table.rows.length; i++){
|
||||||
|
totalTime += $scope.table.rows[i].timeElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalTime/numSubmissions;
|
||||||
|
})();
|
||||||
|
|
||||||
|
$scope.DeviceStatistics = (function(){
|
||||||
|
var newStatItem = function(){
|
||||||
|
return {
|
||||||
|
visits: 0,
|
||||||
|
responses: 0,
|
||||||
|
completion: 0,
|
||||||
|
average_time: 0,
|
||||||
|
total_time: 0
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
var stats = {
|
||||||
|
desktop: newStatItem(),
|
||||||
|
tablet: newStatItem(),
|
||||||
|
phone: newStatItem(),
|
||||||
|
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++;
|
||||||
|
|
||||||
|
stats[deviceType].total_time = stats[deviceType].total_time + visitor.timeElapsed;
|
||||||
|
stats[deviceType].average_time = stats[deviceType].total_time / stats[deviceType].visits || 0;
|
||||||
|
|
||||||
|
if (visitor.isSubmitted) stats[deviceType].responses++;
|
||||||
|
|
||||||
|
stats[deviceType].completion = stats[deviceType].response / stats[deviceType].visits || 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats;
|
||||||
|
})();
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
initController();
|
initController();
|
||||||
|
|
||||||
var updateFields = $interval(initController, 500);
|
var updateFields = $interval(initController, 2000);
|
||||||
|
|
||||||
$scope.$on("$destroy", function() {
|
$scope.$on("$destroy", function() {
|
||||||
if (updateFields) {
|
if (updateFields) {
|
||||||
|
@ -55,57 +110,6 @@ 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return totalTime/numSubmissions;
|
|
||||||
})();
|
|
||||||
|
|
||||||
$scope.DeviceStatistics = (function(){
|
|
||||||
var newStatItem = function(){
|
|
||||||
return {
|
|
||||||
visits: 0,
|
|
||||||
responses: 0,
|
|
||||||
completion: 0,
|
|
||||||
average_time: 0,
|
|
||||||
total_time: 0
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
var stats = {
|
|
||||||
desktop: newStatItem(),
|
|
||||||
tablet: newStatItem(),
|
|
||||||
phone: newStatItem(),
|
|
||||||
other: newStatItem()
|
|
||||||
};
|
|
||||||
|
|
||||||
if($scope.myform.analytics && $scope.myform.analytics.visitors) {
|
|
||||||
for (var i = 0; i < visitors.length; i++) {
|
|
||||||
var visitor = visitors[i];
|
|
||||||
var deviceType = visitor.deviceType;
|
|
||||||
|
|
||||||
stats[deviceType].visits++;
|
|
||||||
|
|
||||||
stats[deviceType].total_time = +visitor.timeElapsed;
|
|
||||||
stats[deviceType].average_time = stats[deviceType].total_time / stats[deviceType].visits || 0;
|
|
||||||
|
|
||||||
if (visitor.isSubmitted) stats[deviceType].responses++;
|
|
||||||
|
|
||||||
stats[deviceType].completion = stats[deviceType].response / stats[deviceType].visits || 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return stats;
|
|
||||||
})();
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Table Functions
|
** Table Functions
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-xs-3">
|
<div class="col-xs-3">
|
||||||
{{AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}}
|
{{ AverageTimeElapsed | secondsToDateTime | date:'mm:ss'}}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-xs-12 detailed-title">
|
<div class="col-xs-12 detailed-title">
|
||||||
|
|
|
@ -9,16 +9,6 @@ angular.module('forms').factory('Forms', ['$resource', 'FORM_URL',
|
||||||
'query' : {
|
'query' : {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
isArray: true
|
isArray: true
|
||||||
//DAVID: TODO: Do we really need to get visible_form_fields for a Query?
|
|
||||||
// transformResponse: function(data, header) {
|
|
||||||
// var forms = angular.fromJson(data);
|
|
||||||
// angular.forEach(forms, function(form, idx) {
|
|
||||||
// form.visible_form_fields = _.filter(form.form_fields, function(field){
|
|
||||||
// return (field.deletePreserved === false);
|
|
||||||
// });
|
|
||||||
// });
|
|
||||||
// return forms;
|
|
||||||
// }
|
|
||||||
},
|
},
|
||||||
'get' : {
|
'get' : {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
|
|
Loading…
Reference in a new issue