LT-61: Record actions that create, modify, delete, associate timepoints, remove associations and login

- Show Last Login Date popup after login successful
(WIP)
This commit is contained in:
Aysel Afsar 2016-04-05 12:16:50 -04:00
parent e277c4b484
commit 83e067b701
12 changed files with 187 additions and 52 deletions

View File

@ -52,3 +52,5 @@ aldeed:template-extension@4.0.0
zuuk:stale-session zuuk:stale-session
gilbertwat:bootstrap3-daterangepicker gilbertwat:bootstrap3-daterangepicker
email email
peppelg:bootstrap-3-modal
simple:reactive-method

View File

@ -83,6 +83,7 @@ npm-bcrypt@0.7.8_2
npm-mongo@1.4.39_1 npm-mongo@1.4.39_1
observe-sequence@1.0.7 observe-sequence@1.0.7
ordered-dict@1.0.4 ordered-dict@1.0.4
peppelg:bootstrap-3-modal@1.0.4
practicalmeteor:chai@2.1.0_1 practicalmeteor:chai@2.1.0_1
practicalmeteor:loglevel@1.2.0_2 practicalmeteor:loglevel@1.2.0_2
promise@0.5.1 promise@0.5.1
@ -98,6 +99,7 @@ service-configuration@1.0.5
session@1.1.1 session@1.1.1
sha@1.0.4 sha@1.0.4
silentcicero:jszip@0.0.4 silentcicero:jszip@0.0.4
simple:reactive-method@1.0.2
spacebars@1.0.7 spacebars@1.0.7
spacebars-compiler@1.0.7 spacebars-compiler@1.0.7
srp@1.0.4 srp@1.0.4

View File

@ -26,7 +26,8 @@ var data = {
'associationModal', 'associationModal',
'optionsModal', 'optionsModal',
'serverInformationModal', 'serverInformationModal',
'confirmRemoveTimepointAssociation' 'confirmRemoveTimepointAssociation',
'lastLoginModal'
] ]
}; };

View File

@ -40,12 +40,8 @@ Meteor.startup(function() {
// Bind events if window is closed // Bind events if window is closed
$(window).bind('beforeunload', function (e) { $(window).bind('beforeunload', function (e) {
closingWindow();
// have to return null, unless you want a chrome popup alert // have to return null, unless you want a chrome popup alert
//return 'If you leave this page then any unsaved changes will be lost.'; // return 'If you leave this page then any unsaved changes will be lost.';
}); });
}); });
closingWindow = function(){
Meteor.call('removeUserFromReviewers', Meteor.userId());
};

View File

@ -123,7 +123,12 @@ Meteor.methods({
}, },
updateLastLoginDate: function () { updateLastLoginDate: function () {
Meteor.users.update({_id: Meteor.userId()}, {$set: {lastLoginDate: new Date()}}); var user = Meteor.users.findOne(Meteor.userId());
if (!user) {
return;
}
// Update priorLoginDate and lastLoginDate
Meteor.users.update({_id: Meteor.userId()}, {$set: {priorLoginDate: user.lastLoginDate, lastLoginDate: new Date()}});
}, },
isAccountInactive: function (inactivityParameters) { isAccountInactive: function (inactivityParameters) {

View File

@ -59,6 +59,10 @@ Template.associationModal.events({
// Sort the study dates, so we can get a range for these values // Sort the study dates, so we can get a range for these values
studyDates = studyDates.sort(); studyDates = studyDates.sort();
// HipaaEventType to log changes in collections
var hipaaEventType;
var hipaaEvent;
// Check if these studies are already associated with an existing Timepoint // Check if these studies are already associated with an existing Timepoint
var existingTimepoint; var existingTimepoint;
if (timepointType === 'baseline') { if (timepointType === 'baseline') {
@ -91,6 +95,7 @@ Template.associationModal.events({
} }
}); });
timepointId = existingTimepoint.timepointId; timepointId = existingTimepoint.timepointId;
hipaaEventType = 'modify';
} else { } else {
// Create a new timepoint to represent the (baseline or follow-up) studies // Create a new timepoint to represent the (baseline or follow-up) studies
var timepoint = { var timepoint = {
@ -105,8 +110,20 @@ Template.associationModal.events({
// Insert this timepoint into the Timepoints Collection // Insert this timepoint into the Timepoints Collection
Timepoints.insert(timepoint); Timepoints.insert(timepoint);
timepointId = timepoint.timepointId; timepointId = timepoint.timepointId;
hipaaEventType = 'create';
} }
// Log
hipaaEvent = {
eventType: hipaaEventType,
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Timepoints",
recordId: timepointId,
patientId: relatedStudies[0].patientId,
patientName: relatedStudies[0].patientName
};
// Loop through these studies to associate them with the newly created timepoint // Loop through these studies to associate them with the newly created timepoint
relatedStudies.forEach(function(study) { relatedStudies.forEach(function(study) {
// Check if a study already exists in the Studies collection // Check if a study already exists in the Studies collection
@ -121,6 +138,8 @@ Template.associationModal.events({
timepointId: timepointId timepointId: timepointId
} }
}); });
// Log modify
hipaaEventType = 'modify';
} else { } else {
// If no such study exists, update the entry with the new timepointId // If no such study exists, update the entry with the new timepointId
@ -130,7 +149,22 @@ Template.associationModal.events({
// Attach the timepointId and insert it into the Studies Collection // Attach the timepointId and insert it into the Studies Collection
study.timepointId = timepointId; study.timepointId = timepointId;
Studies.insert(study); Studies.insert(study);
// Log create
hipaaEventType = 'create';
} }
// Log
hipaaEvent = {
eventType: hipaaEventType,
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Studies",
recordId: study.studyInstanceUid,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
}); });
}); });

View File

@ -0,0 +1,16 @@
<template name="lastLoginModal">
<div class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-body" style="padding: 0;">
<div class="alert alert-info" style="margin: 0 auto;">
<a href="#" class="close" data-dismiss="modal">&times;</a>
{{#if lastLoginDate}}
<h5>You last logged in on <strong>{{lastLoginDate}}</strong></h5>
{{/if}}
</div>
</div>
</div>
</div>
</div>
</template>

View File

@ -0,0 +1,9 @@
Template.lastLoginModal.helpers({
lastLoginDate: function() {
var userLoginDate = ReactiveMethod.call('getPriorLoginDate');
if (userLoginDate && userLoginDate.priorLoginDate) {
return moment(userLoginDate.priorLoginDate).format("MMMM Do YYYY, HH:mm:ss A");
}
return;
}
});

View File

@ -1,42 +1,5 @@
Session.set('defaultSignInMessage', 'Tumor tracking in your browser.'); Session.set('defaultSignInMessage', 'Tumor tracking in your browser.');
Template.lesionTrackerLayout.helpers({
fullName: function() {
return Meteor.user().profile.fullName;
},
showWorklistMenu: function() {
return Template.instance().showWorklistMenu.get();
},
currentUser: function() {
var verifyEmail = Meteor.settings && Meteor.settings.public && Meteor.settings.public.verifyEmail || false;
if (!Meteor.user() || !Meteor.userId()) {
return false;
}
if (!verifyEmail) {
return true;
}
if (Meteor.user().emails[0].verified) {
return true;
}
return false;
}
});
Template.lesionTrackerLayout.onCreated(function() { Template.lesionTrackerLayout.onCreated(function() {
// showViewer to go to viewer from audit
this.showWorklistMenu = new ReactiveVar(true);
// Get url and check worklist
var currentPath = Router.current().route.path(this);
if (currentPath === '/' || currentPath === '/worklist') {
this.showWorklistMenu.set(false);
}
// Show countdown dialog // Show countdown dialog
var handle; var handle;
$(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) { $(document).on('TriggerOpenTimeoutCountdownDialog', function(e, leftTime) {
@ -68,3 +31,54 @@ Template.lesionTrackerLayout.onCreated(function() {
} }
}); });
}); });
Template.lesionTrackerLayout.onRendered(function() {
var oldUserId = undefined;
var userName;
var lastLoginModalInterval;
Tracker.autorun(function() {
// Hook login/logout
var newUserId = Meteor.userId();
if (oldUserId === null && newUserId) {
Session.set('showLastLoginModal', true);
userName = Meteor.user().profile.fullName;
// Log
HipaaLogger.logEvent({
eventType: 'init',
userId: Meteor.userId(),
userName: userName
});
} else if (newUserId === null && oldUserId) {
// Set showLastLoginModal as null
Session.set('showLastLoginModal', null);
// Destroy interval for last login modal
Meteor.clearInterval(lastLoginModalInterval);
console.log('The user logged out');
// Log
// TODO: eventype is not defined for logout in hipaa-audit-log
/*HipaaLogger.logEvent({
eventType: 'logout',
userId: oldUserId,
userName: userName
});*/
// Remove the user from Reviewers
Meteor.call('removeUserFromReviewers', oldUserId);
}
oldUserId = Meteor.userId();
// Trigger last login date popup
if (Session.get('showLastLoginModal')) {
Modal.show('lastLoginModal');
lastLoginModalInterval = Meteor.setInterval( function() {
Modal.hide('lastLoginModal');
Session.set("showLastLoginModal", null);
}, 3000);
return true;
}
});
});

View File

@ -37,7 +37,20 @@ function removeTimepointAssociations() {
Meteor.call('removeAssociatedStudy', study._id, function(error) { Meteor.call('removeAssociatedStudy', study._id, function(error) {
if (error) { if (error) {
log.warn(error); log.warn(error);
return;
} }
// Log
var hipaaEvent = {
eventType: 'delete',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Studies",
recordId: study.studyInstanceUid,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
}); });
// Find the Timepoint that was previously referenced // Find the Timepoint that was previously referenced
@ -64,13 +77,38 @@ function removeTimepointAssociations() {
studyInstanceUids: timepoint.studyInstanceUids studyInstanceUids: timepoint.studyInstanceUids
} }
}); });
// Log
var hipaaEvent = {
eventType: 'modify',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Timepoints",
recordId: study.timepointId,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
} else { } else {
// If no more Studies are associated with this Timepoint, we should remove it // If no more Studies are associated with this Timepoint, we should remove it
// from the Timepoints Collection via a server call // from the Timepoints Collection via a server call
Meteor.call('removeTimepoint', timepoint._id, function(error) { Meteor.call('removeTimepoint', timepoint._id, function(error) {
if (error) { if (error) {
log.warn(error); log.warn(error);
return;
} }
// Log
var hipaaEvent = {
eventType: 'delete',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Timepoints",
recordId: study.timepointId,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
}); });
} }
}); });
@ -81,10 +119,10 @@ function removeTimepointAssociations() {
*/ */
function exportSelectedStudies() { function exportSelectedStudies() {
var selectedStudies = WorklistSelectedStudies.find({}, { var selectedStudies = WorklistSelectedStudies.find({}, {
sort: { sort: {
studyDate: 1 studyDate: 1
} }
}).fetch(); }).fetch();
if (!selectedStudies) { if (!selectedStudies) {
return; return;
@ -98,10 +136,10 @@ function exportSelectedStudies() {
*/ */
function viewStudies() { function viewStudies() {
var selectedStudies = WorklistSelectedStudies.find({}, { var selectedStudies = WorklistSelectedStudies.find({}, {
sort: { sort: {
studyDate: 1 studyDate: 1
} }
}).fetch(); }).fetch();
if (!selectedStudies) { if (!selectedStudies) {
return; return;

View File

@ -156,6 +156,8 @@ Package.onUse(function(api) {
api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.html', 'client'); api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.html', 'client');
api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.js', 'client'); api.addFiles('client/components/confirmRemoveTimepointAssociation/confirmRemoveTimepointAssociation.js', 'client');
api.addFiles('client/components/lastLoginModal/lastLoginModal.html', 'client');
api.addFiles('client/components/lastLoginModal/lastLoginModal.js', 'client');
// Server functions // Server functions
api.addFiles('server/collections.js', 'server'); api.addFiles('server/collections.js', 'server');

View File

@ -45,6 +45,18 @@ Meteor.methods({
} else { } else {
Reviewers.insert({timepointId: timepoint.timepointId, reviewers: [{userId: user._id, userName: user.profile.fullName}]}); Reviewers.insert({timepointId: timepoint.timepointId, reviewers: [{userId: user._id, userName: user.profile.fullName}]});
} }
// Log
var hipaaEvent = {
eventType: 'modify',
userId: Meteor.userId(),
userName: Meteor.user().profile.fullName,
collectionName: "Measurements",
recordId: study.studyInstanceUid,
patientId: study.patientId,
patientName: study.patientName
};
HipaaLogger.logEvent(hipaaEvent);
}, },
removeReviewer: function (timepointId) { removeReviewer: function (timepointId) {
@ -70,6 +82,10 @@ Meteor.methods({
Reviewers.find().map(function (timepoint) { Reviewers.find().map(function (timepoint) {
Reviewers.update({_id: timepoint._id}, {"$pull": {"reviewers": {"userId": userId}}}); Reviewers.update({_id: timepoint._id}, {"$pull": {"reviewers": {"userId": userId}}});
}); });
},
getPriorLoginDate: function() {
return Meteor.users.findOne({_id: Meteor.userId()}, {fields: {priorLoginDate: 1}});
} }
}); });