diff --git a/auth/comparePassword.js b/auth/comparePassword.js index f2976d2..adeb464 100644 --- a/auth/comparePassword.js +++ b/auth/comparePassword.js @@ -7,7 +7,7 @@ const findUser = ( User, email, password ) => { if( ! user ) { reject({}) } - + bcrypt.compare( password, user.password, (error, result ) => { if( result ) { resolve( user ) diff --git a/migrations/20161101213307-create-audit.js b/migrations/20161101213307-create-audit.js new file mode 100644 index 0000000..a1d09b8 --- /dev/null +++ b/migrations/20161101213307-create-audit.js @@ -0,0 +1,45 @@ +'use strict'; +module.exports = { + up: function(queryInterface, Sequelize) { + return queryInterface.createTable('Audits', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + table_name: { + type: Sequelize.STRING + }, + field_id: { + type: Sequelize.STRING + }, + field_name: { + type: Sequelize.STRING + }, + old_value: { + type: Sequelize.STRING + }, + new_value: { + type: Sequelize.STRING + }, + field_type: { + type: Sequelize.STRING + }, + user_id: { + type: Sequelize.INTEGER + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + down: function(queryInterface, Sequelize) { + return queryInterface.dropTable('Audits'); + } +}; \ No newline at end of file diff --git a/models/audit.js b/models/audit.js new file mode 100644 index 0000000..e4a65e2 --- /dev/null +++ b/models/audit.js @@ -0,0 +1,22 @@ +const returnAuditOptions = require('../src/Auditor') + +'use strict'; +module.exports = function(sequelize, DataTypes) { + var Audit = sequelize.define('Audit', { + table_name: DataTypes.STRING, + element_id: DataTypes.STRING, + element_name: DataTypes.STRING, + old_value: DataTypes.STRING, + new_value: DataTypes.STRING, + field_type: DataTypes.STRING, + user_id: DataTypes.INTEGER + }, { + classMethods: { + associate: function(models) { + // associations can be defined here + }, + + } + }); + return Audit; +}; diff --git a/models/item.js b/models/item.js index dca2d75..ec27e85 100644 --- a/models/item.js +++ b/models/item.js @@ -1,29 +1,51 @@ 'use strict'; - const VALID_PARAMETERS = [ 'completed', 'title', 'description' ] +const returnAuditOptions = require('../src/Auditor') module.exports = function(sequelize, DataTypes) { - var Item = sequelize.define('Item', { - title: DataTypes.STRING, - description: DataTypes.TEXT, - completed: DataTypes.BOOLEAN, - parent_id: DataTypes.INTEGER, - user_id: DataTypes.INTEGER - }, { - classMethods: { - associate: function(models) { - // associations can be defined here + const Audit = sequelize.models.Audit + const Item = sequelize.define('Item', + { + title: DataTypes.STRING, + description: DataTypes.TEXT, + completed: DataTypes.BOOLEAN, + parent_id: DataTypes.INTEGER, + user_id: DataTypes.INTEGER + }, + + + + { + hooks: { + afterCreate: function(item, options) { + let {updateType, data_type} = options + let auditOptions = returnAuditOptions(updateType, item, data_type) + Audit.create(auditOptions[0], {success: true}) + }, + afterUpdate: function(item, options) { + + let {updateType, data_type} = options + let auditOptions = returnAuditOptions(updateType,item, data_type) + Audit.create(auditOptions[0], {success: true}) + } }, - filterParameters: params => { - return VALID_PARAMETERS.reduce( (memo, key) => { - if( params[ key ] !== undefined ) { - memo[ key ] = params[ key ] - } - - return memo - }, {} ) + classMethods: { + associate: function(models) { + // associations can be defined here + }, + + filterParameters: params => { + return VALID_PARAMETERS.reduce( (memo, key) => { + if( params[ key ] !== undefined ) { + memo[ key ] = params[ key ] + } + + return memo + }, {} ) + } } } - }); + ); + return Item; }; diff --git a/models/user.js b/models/user.js index 6dfa113..b573a8b 100644 --- a/models/user.js +++ b/models/user.js @@ -12,4 +12,4 @@ module.exports = function(sequelize, DataTypes) { } }); return User; -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 45a7591..1445ebe 100644 --- a/package.json +++ b/package.json @@ -28,7 +28,7 @@ "foreman": "^2.0.0", "jsonwebtoken": "^7.1.9", "legit": "0.0.5", - "moment": "^2.15.1", + "moment": "^2.15.2", "morgan": "~1.7.0", "nodemailer": "^2.6.4", "passport": "^0.3.2", diff --git a/public/javascripts/items.js b/public/javascripts/items.js index 5fd9cbb..e20c323 100644 --- a/public/javascripts/items.js +++ b/public/javascripts/items.js @@ -14,8 +14,10 @@ const params = data => const checkJsonForSuccessField = json => { if( json.success ) { + console.log('checking for json success'); Promise.resolve( json ) } else { + console.log('rejecting for json failure'); Promise.reject( json.message ) } } @@ -74,7 +76,6 @@ const completedClicked = event => { const element = $( event.target ) const id = element.data( 'id' ) const completed = ! element.data( 'completed' ) - fetch( `/items/${id}`, params({ completed: completed } ) ) .then( result => result.json() ) .then( checkJsonForSuccessField ) @@ -90,6 +91,8 @@ const completedClicked = event => { ) } + + $(document).ready( () => { $( '.edit-title' ).keypress( titleEdited ) $( '.title > span' ).click( clickToUpdate( 'title' )) diff --git a/routes/accounts.js b/routes/accounts.js index 1612ab9..6a8fc9a 100644 --- a/routes/accounts.js +++ b/routes/accounts.js @@ -8,7 +8,7 @@ const validateEmail = require( '../src/mail/validate_email' ) const AUTH_OPTIONS = { - successRedirect: '/items', + successRedirect: '/items/weekly', failureRedirect: '/accounts/login' } diff --git a/routes/accounts/authenticate.js b/routes/accounts/authenticate.js index b9b3ac5..e8b7816 100644 --- a/routes/accounts/authenticate.js +++ b/routes/accounts/authenticate.js @@ -1,3 +1,4 @@ + const token = require( 'jsonwebtoken' ) const authenticate = user => { diff --git a/routes/items.js b/routes/items.js index d505c34..61e8244 100644 --- a/routes/items.js +++ b/routes/items.js @@ -4,6 +4,7 @@ const router = express.Router() const { allItemsQuery, filteredItemsQuery, respondWithItems } = require( './items/item_response' ) const { buildTree } = require( './items/tree_creation' ) const findAllItems = require('./items/find_all_items') +const grabUserStats = require('../src/weekly_report') router.get( '/', ( request, response ) => { const { Item } = request.app.get( 'models' ) @@ -19,20 +20,37 @@ router.post( '/', ( request, response ) => { const { title, description, parent_id } = request.body - Item.create({ title, description, parent_id, user_id: request.user.id }) + Item.create({ title, description, parent_id, user_id: request.user.id }, {updateType: ['create'], individualHooks: true, data_type: 'JSON' }) .then( result => response.redirect( '/items' )) }) +router.get( '/weekly', ( request, response ) => { + const {User, Audit, Item} = request.app.get('models') + + const user_id = request.user.id + // console.log('uid: ', request.user.id); + grabUserStats( User, user_id, Audit, Item) + .then( userStats => { + console.log('>>>>>>>>>', userStats) + response.render('items/weekly_review', {userStats}) + }) +}) + router.post( '/:id', ( request, response ) => { - const Item = request.app.get( 'models' ).Item + const {Item, Audit} = request.app.get( 'models' ) const { id } = request.params const where = { id, user_id: request.user.id } + const valid_params = Item.filterParameters( request.body ) + const fields = Object.keys(valid_params) + let data_type_string = `Item.tableAttributes.${fields}.type.constructor.key` + const data_type = eval(data_type_string) - Item.update( Item.filterParameters( request.body ), { where }) + + Item.update( valid_params, { where, data_type, updateType: fields, individualHooks: true}) .then( result => response.json({ success: true, id })) .catch( error => response.json({ success: false, id, message: error.message }) ) -}) + }) module.exports = router diff --git a/src/Auditor.js b/src/Auditor.js new file mode 100644 index 0000000..f99aacb --- /dev/null +++ b/src/Auditor.js @@ -0,0 +1,67 @@ +const returnAuditOptions = (updateType, item, data_type) => { + console.log('returning Audit Options', updateType, data_type); + const auditOptions = updateType.map(type => { + console.log('type',type, 'data_type', data_type); + if(type === 'create'){ + console.log('in create'); + let options = { + table_name: 'Items', + element_id: item.id, + element_name: 'row', + old_value: 'n/a', + new_value: JSON.stringify(item), + field_type: data_type, + user_id: item.user_id + } + console.log('>>>>>>',item); + return options + } + if(type === 'completed'){ + console.log('in completed'); + let options = { + table_name: 'Items', + element_id: item.id, + element_name: type, + old_value: ! item.completed, + new_value: item.completed, + field_type: data_type, + user_id: item.user_id + } + console.log('>>>>>>',item._previousDataValues.title); + return options + } + + if(type === 'title' ){ + console.log('type: ', item._previousDataValues.title ); + let options = { + table_name: 'Items', + element_id: item.id, + element_name: type, + old_value: item._previousDataValues.title, + new_value: item.title, + field_type: data_type, + user_id: item.user_id + } + console.log('title: ',options); + return options + } + + if(type === 'description'){ + let options = { + table_name: 'Items', + element_id: item.id, + element_name: 'description', + old_value: item._previousDataValues.description, + new_value: item.description, + field_type: data_type, + user_id: item.user_id + } + console.log('description', options); + return options + } + }) + console.log('auditOptions',auditOptions); + return auditOptions +} + +module.exports = returnAuditOptions diff --git a/src/weekly_report.js b/src/weekly_report.js new file mode 100644 index 0000000..85d47b1 --- /dev/null +++ b/src/weekly_report.js @@ -0,0 +1,68 @@ +const moment = require('moment') +const today = moment() +const todaysDate = moment() +const minusSevenDays = todaysDate.subtract(7, 'days') +console.log('-7: ', minusSevenDays.format("dddd, MMMM Do YYYY, h:mm:ss a"), '\n'); +console.log('todaysDate: ', today.format("dddd, MMMM Do YYYY, h:mm:ss a")); + + +const bulletsCreated = (auditRecords) => { + const createdThisWeek = auditRecords.filter(record => record.createdAt >= minusSevenDays) + return createdThisWeek.length + // ([all items w/ user_id && createdAt(<= today’s date - 7) ].length) + } + +const bulletsChanged = (auditRecords) => { + const changedThisWeek = auditRecords.filter(record => record.updatedAt >= minusSevenDays) + return changedThisWeek.length + // ([all items w/ user_id && updatedAt(<= today’s date - 7) ].length) +} + +const totalBullets = (auditRecords) => { + const totalBullets = auditRecords.filter(record => { + return record.element_name === 'row' && record.createdAt >= minusSevenDays + }) + return totalBullets.length +} + +const bulletsCompleted = (auditRecords) => { + const completedItems = auditRecords.filter(record => { + return record.element_name === 'completed' && + record.new_value == true && + record.createdAt >= minusSevenDays + }) + console.log(); + return completedItems.length +} + +const calculateStats = (Audit, user_id) => { + const stats =Audit.findAll({where: {user_id: user_id}}) + .then(auditRecords => { + let created = bulletsCreated(auditRecords) + let changed = bulletsChanged(auditRecords) + let completed = bulletsCompleted(auditRecords) + let total = totalBullets(auditRecords) + console.log('raw Stats: ',created, changed, completed, total); + const userStats = {created, changed, completed, total} + console.log('userStats: ', userStats); + return userStats + }) + + return stats +} +const grabUserStats = (User, user_id, Audit, Item) => { + const where = {id: user_id} + const userStats = User.findOne({where}) + .then(user => { + return Math.abs(todaysDate.diff(user.createdAt, 'days')) + console.log('user: ', user); + }) + .then(lengthOfUse => { + console.log('lengthOfUse', lengthOfUse); + return calculateStats(Audit, user_id) + }) + + return userStats +} + +module.exports = grabUserStats diff --git a/views/items/weekly_review.pug b/views/items/weekly_review.pug new file mode 100644 index 0000000..bfd05d3 --- /dev/null +++ b/views/items/weekly_review.pug @@ -0,0 +1,6 @@ + +block content + h1 Your weekly review + for record in userStats + p=record + a.btn.btn-success(href="/items") Go to items