// $HeadURL$ // $Id$ // // Copyright © 2006, 2010, 2011, 2012 by the President and Fellows of Harvard College. // // Screensaver is an open-source project developed by the ICCB-L and NSRB labs // at Harvard Medical School. This software is distributed under the terms of // the GNU General Public License. package edu.harvard.med.iccbl.screensaver.service.users; import static edu.harvard.med.screensaver.model.screens.ScreenType.RNAI; import static edu.harvard.med.screensaver.model.screens.ScreenType.SMALL_MOLECULE; import java.io.IOException; import java.io.InputStream; import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import org.apache.log4j.Logger; import org.joda.time.LocalDate; import org.springframework.transaction.annotation.Transactional; import edu.harvard.med.iccbl.screensaver.policy.DataSharingLevelMapper; import edu.harvard.med.screensaver.db.GenericEntityDAO; import edu.harvard.med.screensaver.model.BusinessRuleViolationException; import edu.harvard.med.screensaver.model.activities.AdministrativeActivity; import edu.harvard.med.screensaver.model.screens.ScreenType; import edu.harvard.med.screensaver.model.users.AdministratorUser; import edu.harvard.med.screensaver.model.users.ChecklistItem; import edu.harvard.med.screensaver.model.users.ChecklistItemEvent; import edu.harvard.med.screensaver.model.users.ScreeningRoomUser; import edu.harvard.med.screensaver.model.users.ScreensaverUser; import edu.harvard.med.screensaver.model.users.ScreensaverUserRole; import edu.harvard.med.screensaver.model.users.UserAttachedFileType; import edu.harvard.med.screensaver.service.OperationRestrictedException; import edu.harvard.med.screensaver.util.DevelopmentException; import edu.harvard.med.screensaver.util.NullSafeUtils; import edu.harvard.med.screensaver.util.Pair; public class UserAgreementUpdater { static final Map<ScreenType,String> USER_AGREEMENT_ATTACHED_FILE_TYPE = ImmutableMap.of(SMALL_MOLECULE, "2010 ICCB-L/NSRB Small Molecule User Agreement", RNAI, "ICCB-L/NSRB RNAi User Agreement"); static final Map<ScreenType,String> USER_AGREEMENT_CHECKLIST_ITEM_NAME = ImmutableMap.of(SMALL_MOLECULE, "Current Small Molecule User Agreement active", RNAI, "Current RNAi User Agreement active"); private static Logger log = Logger.getLogger(UserAgreementUpdater.class); private GenericEntityDAO _dao; protected UserAgreementUpdater() {} public UserAgreementUpdater(GenericEntityDAO dao) { _dao = dao; } /** * This method locates the<br> * _not yet expired_ users who have a UA with an activation on or before the date given. * * @param showNotifiedItems if set, show items that have already been notified, as indicated by the * sru.lastNotifiedUAChecklistItemEvent being set. * @param screenType TODO */ @Transactional public List<Pair<ScreeningRoomUser,ChecklistItemEvent>> findUsersWithOldUserAgreements(LocalDate date, boolean showNotifiedItems, ScreenType screenType) { String checklistItemName = USER_AGREEMENT_CHECKLIST_ITEM_NAME.get(screenType); ChecklistItem userAgreementChecklistItem = _dao.findEntityByProperty(ChecklistItem.class, "itemName", checklistItemName); if (userAgreementChecklistItem == null) { throw new BusinessRuleViolationException("checklist item '" + checklistItemName + "' does not exist"); } String hql = "select distinct(sru) from ScreeningRoomUser as sru inner join sru.checklistItemEvents cie where " + " cie.expiration != ? " + // do this to limit the set, but not as the final check " and cie.checklistItem = ? " + "order by sru.lastName, sru.firstName"; List<ScreeningRoomUser> users = _dao.findEntitiesByHql(ScreeningRoomUser.class, hql, /* TODO: see if we eliminate this param */Boolean.TRUE, userAgreementChecklistItem); List<Pair<ScreeningRoomUser,ChecklistItemEvent>> expiredSet = Lists.newLinkedList(); for(ScreeningRoomUser user:users) { log.debug("test: " + user.getFullNameFirstLast()); ChecklistItemEvent checklistItemEvent = user.getChecklistItemEvents(userAgreementChecklistItem).last(); if (checklistItemEvent.isExpiration()) { if(log.isDebugEnabled()) log.debug("user is already expired: " + user+ ", date: " + checklistItemEvent.getDatePerformed()); // TODO: evaluate logic: // we will not check that the _role_ has been removed here, since we are only trying to find the _not yet expired_ users } else if (checklistItemEvent.getDatePerformed().compareTo(date) > 0) { if(log.isDebugEnabled()) log.debug("remove: " + user + ", expire date too new: " + checklistItemEvent.getDatePerformed()); } else { if (!showNotifiedItems && checklistItemEvent.equals(getLastNotifiedUserAgreementChecklistItemEvent(user, screenType))) { log.info("Already notified the user for: " + checklistItemEvent); } else { expiredSet.add(Pair.newPair(user,checklistItemEvent)); } } } return expiredSet; } private ChecklistItemEvent getLastNotifiedUserAgreementChecklistItemEvent(ScreeningRoomUser user, ScreenType screenType) { switch (screenType) { case SMALL_MOLECULE: return user.getLastNotifiedSMUAChecklistItemEvent(); case RNAI: return user.getLastNotifiedRNAiUAChecklistItemEvent(); default: throw new DevelopmentException("not implemented for " + screenType + " screen type"); } } public void setLastNotifiedUserAgreementChecklistItemEvent(ScreeningRoomUser sru, ChecklistItemEvent cie, ScreenType screenType) { switch (screenType) { case SMALL_MOLECULE: sru.setLastNotifiedSMUAChecklistItemEvent(cie); break; case RNAI: sru.setLastNotifiedRNAiUAChecklistItemEvent(cie); break; default: throw new DevelopmentException("not implemented for RNAi screen type"); } _dao.saveOrUpdateEntity(sru); } /** * <ul>Expire a user, by * <li>creating an "expiration" ChecklistItemEvent * {@link ChecklistItemEvent#createChecklistItemExpirationEvent(LocalDate, AdministratorUser)} * for the last ChecklistItemEvent for that user (provided it is not already an expiration). * <li>removing the role returned as the {@link DataSharingLevelMapper#getPrimaryDataSharingLevelRoleForUser(ScreenType, ScreensaverUser)} * <li>removing the {@link ScreensaverUserRole#SCREENSAVER_USER} role if the user has no other data sharing level roles in place (i.e. for another {@link ScreenType } ). * </ul> * @return a List of the activities performed. */ @Transactional public List<AdministrativeActivity> expireUser(ScreeningRoomUser user, AdministratorUser recordedBy, ScreenType screenType) { String checklistItemName = USER_AGREEMENT_CHECKLIST_ITEM_NAME.get(screenType); ChecklistItem userAgreementChecklistItem = _dao.findEntityByProperty(ChecklistItem.class, "itemName", checklistItemName); if (userAgreementChecklistItem == null) { throw new BusinessRuleViolationException("checklist item '" + checklistItemName + "' does not exist"); } List<AdministrativeActivity> activitiesPerformed = Lists.newLinkedList(); user = _dao.reloadEntity(user); recordedBy = _dao.reloadEntity(recordedBy); verifyOperationPermitted(user, recordedBy); SortedSet<ChecklistItemEvent> events = user.getChecklistItemEvents(userAgreementChecklistItem); if (events.isEmpty()) { // TODO: remove the data sharing role anyway - if they have no checklist items... however, this could be due to a manual admin override log.debug("User has no checklist item events: " + user + ", so no action will be taken"); return activitiesPerformed; } if (events.last().isExpiration()) { log.debug("User's last checklistItemEvent was already expired"); return activitiesPerformed; } else { ChecklistItemEvent cie = events.last(); cie.createChecklistItemExpirationEvent(new LocalDate(), recordedBy); activitiesPerformed.add(user.createUpdateActivity(recordedBy, "expired '" + USER_AGREEMENT_CHECKLIST_ITEM_NAME + "' " + ", checklist item: " + cie.getChecklistItemEventId() + ", datePerformed: " + cie.getDatePerformed())); removeRole(DataSharingLevelMapper.getPrimaryDataSharingLevelRoleForUser(screenType, user), user, recordedBy, activitiesPerformed); if (screenType == ScreenType.SMALL_MOLECULE && !!!user.getScreensaverUserRoles().contains(ScreensaverUserRole.RNAI_DSL_LEVEL3_SHARED_SCREENS)) { removeRole(ScreensaverUserRole.SCREENSAVER_USER, user, recordedBy, activitiesPerformed); } else if (screenType == ScreenType.RNAI && !!!user.getScreensaverUserRoles().contains(ScreensaverUserRole.SM_DSL_LEVEL3_SHARED_SCREENS)) { removeRole(ScreensaverUserRole.SCREENSAVER_USER, user, recordedBy, activitiesPerformed); } } return activitiesPerformed; } private void removeRole(ScreensaverUserRole role, ScreeningRoomUser user, AdministratorUser recordedBy, List<AdministrativeActivity> activitiesPerformed) { if (user.removeScreensaverUserRole(role)) { activitiesPerformed.add(user.createUpdateActivity(recordedBy, "removed \"" + role.getDisplayableRoleName() + "\" role after expiring the '" + USER_AGREEMENT_CHECKLIST_ITEM_NAME + "' checklist item")); } } /** * Create a User Data Sharing Checklist Item for the User.<br> * Details: * <ul> * <li>ChecklistItemEvent is active now * <li>Remove current Data Sharing role(s) and add the passed in dataSharingRole to the user. * <li>Create the attached file containing the UA, referenced to the User. * </ul> * * @param user * @param dataSharingLevelRole * @param userAgreementFileName * @param userAgreementFileContents * @param recordedBy * @throws IOException */ @Transactional public ScreeningRoomUser updateUser(ScreeningRoomUser user, ScreensaverUserRole dataSharingLevelRole, ScreenType screenType, String userAgreementFileName, InputStream userAgreementFileContents, AdministratorUser recordedBy) throws IOException { user = _dao.reloadEntity(user); recordedBy = _dao.reloadEntity(recordedBy); verifyOperationPermitted(user, recordedBy); if (!!!DataSharingLevelMapper.UserDslRoles.get(screenType).contains(dataSharingLevelRole)) { throw new BusinessRuleViolationException("data sharing level be must one of " + Joiner.on(", ").join(DataSharingLevelMapper.UserDslRoles.get(screenType))); } String checklistItemName = USER_AGREEMENT_CHECKLIST_ITEM_NAME.get(screenType); ChecklistItem userAgreementChecklistItem = _dao.findEntityByProperty(ChecklistItem.class, "itemName", checklistItemName); if (userAgreementChecklistItem == null) { throw new BusinessRuleViolationException("checklist item '" + checklistItemName + "' does not exist"); } SortedSet<ChecklistItemEvent> extantUserAgreementChecklistItemEvents = user.getChecklistItemEvents(userAgreementChecklistItem); if (!!!extantUserAgreementChecklistItemEvents.isEmpty() && !!!extantUserAgreementChecklistItemEvents.last().isExpiration()) { throw new BusinessRuleViolationException("cannot update the user agreement of a user that already has an active user agreement"); } String userAgreementAttachedFileTypeName = USER_AGREEMENT_ATTACHED_FILE_TYPE.get(screenType); UserAttachedFileType userAgreementAttachedFileType = _dao.findEntityByProperty(UserAttachedFileType.class, "value", userAgreementAttachedFileTypeName); if (userAgreementAttachedFileType == null) { throw new BusinessRuleViolationException("attached file type '" + userAgreementAttachedFileTypeName + "' does not exist"); } ChecklistItemEvent cie = user.createChecklistItemActivationEvent(userAgreementChecklistItem, new LocalDate(), recordedBy); ScreensaverUserRole oldDataSharingLevelRole = DataSharingLevelMapper.getPrimaryDataSharingLevelRoleForUser(screenType, user); user.createUpdateActivity(recordedBy, "UserAgreementUpdater Service: activated '" + checklistItemName + "' checklist item with date performed: " + cie.getDatePerformed()); user.removeScreensaverUserRole(oldDataSharingLevelRole); user.addScreensaverUserRole(dataSharingLevelRole); if (oldDataSharingLevelRole != dataSharingLevelRole) { user.createUpdateActivity(recordedBy, "updated data sharing level from '" + (oldDataSharingLevelRole == null ? "<none>" : oldDataSharingLevelRole.getDisplayableRoleName()) + "' to '" + dataSharingLevelRole.getDisplayableRoleName() + "'"); } if (user.addScreensaverUserRole(ScreensaverUserRole.SCREENSAVER_USER)) { user.createUpdateActivity(recordedBy, "added '" + ScreensaverUserRole.SCREENSAVER_USER.getDisplayableRoleName() + "' role"); } user.createAttachedFile(userAgreementFileName, userAgreementAttachedFileType, null, userAgreementFileContents); return user; } private void verifyOperationPermitted(ScreeningRoomUser user, AdministratorUser recordedBy) throws OperationRestrictedException { if (!!!recordedBy.getScreensaverUserRoles().contains(ScreensaverUserRole.USERS_ADMIN)) { throw new OperationRestrictedException("to update a user's user agreement, administrator must have " + ScreensaverUserRole.USERS_ADMIN.getDisplayableRoleName() + " role"); } if (!!!recordedBy.getScreensaverUserRoles().contains(ScreensaverUserRole.USER_ROLES_ADMIN)) { throw new OperationRestrictedException("to update a user's user agreement, administrator must have " + ScreensaverUserRole.USER_ROLES_ADMIN.getDisplayableRoleName() + " role"); } if (user.isHeadOfLab()) { if (!!!recordedBy.getScreensaverUserRoles().contains(ScreensaverUserRole.LAB_HEADS_ADMIN)) { throw new OperationRestrictedException("to update a lab head's user agreement, administrator must have " + ScreensaverUserRole.LAB_HEADS_ADMIN.getDisplayableRoleName() + " role"); } } } /** * This is used to get the set of admins that will be notified of actions taken. * //TODO: [#2175] Screen Data Sharing And User Agreement Expiration Services */ public Set<ScreensaverUser> findUserAgreementAdmins() { String hql = "from ScreensaverUser where ? in elements (screensaverUserRoles)" ; Set<ScreensaverUser> admins = Sets.newHashSet(_dao.findEntitiesByHql(ScreensaverUser.class, hql, ScreensaverUserRole.USER_AGREEMENT_EXPIRATION_NOTIFY.getRoleName())); return admins; } }