Java tutorial
/******************************************************************************* * Copyright (c) 2009 David Harrison. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl-3.0.html * * Contributors: * David Harrison - initial API and implementation ******************************************************************************/ package com.sfs.whichdoctor.dao; import com.sfs.beans.BuilderBean; import com.sfs.beans.FieldMapBean; import com.sfs.beans.ObjectTypeBean; import com.sfs.beans.PrivilegesBean; import com.sfs.beans.UserBean; import com.sfs.dao.FieldMapDAO; import com.sfs.dao.PrivilegesDAO; import com.sfs.dao.SFSDaoException; import com.sfs.whichdoctor.beans.AccreditationBean; import com.sfs.whichdoctor.beans.ExamBean; import com.sfs.whichdoctor.beans.IsbTransactionBean; import com.sfs.whichdoctor.beans.MembershipBean; import com.sfs.whichdoctor.beans.PersonBean; import com.sfs.whichdoctor.beans.PreferencesBean; import com.sfs.whichdoctor.beans.RotationBean; import com.sfs.whichdoctor.beans.SearchBean; import com.sfs.whichdoctor.beans.SearchResultsBean; import com.sfs.whichdoctor.beans.SpecialtyBean; import com.sfs.whichdoctor.search.SearchDAO; import com.sfs.whichdoctor.search.WhichDoctorSearchDaoException; import java.sql.Date; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Random; import java.util.TreeMap; import java.util.Properties; import javax.annotation.Resource; import org.apache.log4j.Logger; import org.apache.commons.lang.StringUtils; import org.springframework.dao.DataAccessException; import org.springframework.dao.IncorrectResultSizeDataAccessException; import org.springframework.jdbc.core.RowMapper; import org.springframework.beans.factory.annotation.Required; /** * The Class MembershipDAOImpl. */ public class MembershipDAOImpl extends WhichDoctorBaseDAOImpl implements MembershipDAO { /** The Constant MAX_INTEGERS. */ private static final int MAX_INTEGERS = 2; /** The Constant MAX_OBJECTTYPES. */ private static final int MAX_OBJECTTYPES = 5; /** The Constant MAX_CHARS. */ private static final int MAX_CHARS = 2; /** The Constant MAX_DATES. */ private static final int MAX_DATES = 2; /** The data logger. */ private static Logger dataLogger = Logger.getLogger(MembershipDAOImpl.class); /** The membership hash. */ private Map<String, MembershipBean> membershipHash; /** The training limits. */ private Properties trainingLimits; /** The default membership class. */ private String defaultMembershipClass; /** The default membership type. */ private String defaultMembershipType; /** The person dao. */ @Resource private PersonDAO personDAO; /** The search dao. */ @Resource private SearchDAO searchDAO; /** The preferences dao. */ @Resource private PreferencesDAO preferencesDAO; /** The privileges dao. */ @Resource private PrivilegesDAO privilegesDAO; /** The specialty dao. */ @Resource private SpecialtyDAO specialtyDAO; /** The field map dao. */ @Resource private FieldMapDAO fieldMapDAO; /** The isb transaction dao. */ @Resource private IsbTransactionDAO isbTransactionDAO; /** The Constant NOT_IN_TRAINING. */ private final static int NOT_IN_TRAINING = 1; /** The Constant STANDARD_TRAINING. */ private final static int STANDARD_TRAINING = 2; /** The Constant STANDARD_POST_FRACP. */ private final static int STANDARD_POST_FRACP = 3; /** The Constant STANDARD_NO_CURRENT. */ private final static int STANDARD_NO_CURRENT = 4; /** The Constant CONDITIONAL_UNMET. */ private final static int CONDITIONAL_UNMET = 5; /** The Constant CONDITIONAL_REQUIRES_UPGRADE. */ private final static int CONDITIONAL_REQUIRES_UPGRADE = 6; /** The Constant CONDITIONAL_NO_CURRENT. */ private final static int CONDITIONAL_NO_CURRENT = 7; /** The Constant CONDITIONAL_CONTINUING. */ private final static int CONDITIONAL_CONTINUING = 8; /** * Sets the training limits. * * @param trainingLimitsVal the new training limits */ @Required public final void setTrainingLimits(final Properties trainingLimitsVal) { this.trainingLimits = trainingLimitsVal; } /** * Gets the training types. * * @return the training types */ public final Collection<String> getTrainingTypes() { Collection<String> types = new ArrayList<String>(); if (this.trainingLimits != null) { for (Object key : this.trainingLimits.keySet()) { types.add((String) key); } } return types; } /** * Gets the training limit. * * @param type the type * * @return the training limit */ public final int getTrainingLimit(final String type) { int value = 0; if (this.trainingLimits != null) { if (this.trainingLimits.containsKey(type)) { String strValue = this.trainingLimits.getProperty(type); try { value = Integer.parseInt(strValue); } catch (NumberFormatException nfe) { dataLogger.debug("Error parsing integer: " + nfe.getMessage()); } } } return value; } /** * Sets the default membership class. * * @param defaultMembershipClassVal the new default membership class */ @Required public final void setDefaultMembershipClass(final String defaultMembershipClassVal) { this.defaultMembershipClass = defaultMembershipClassVal; } /** * Gets the default membership class. * * @return the default membership class */ public final String getDefaultMembershipClass() { return this.defaultMembershipClass; } /** * Sets the default membership type. * * @param defaultMembershipTypeVal the new default membership type */ @Required public final void setDefaultMembershipType(final String defaultMembershipTypeVal) { this.defaultMembershipType = defaultMembershipTypeVal; } /** * Gets the default membership type. * * @return the default membership type */ public final String getDefaultMembershipType() { return this.defaultMembershipType; } /** * Gets the all instances. * * @return the all instances */ public final Collection<MembershipBean> getAllInstances() { if (this.membershipHash == null) { loadMembershipHash(); } final Collection<MembershipBean> memberships = new ArrayList<MembershipBean>(); memberships.addAll(this.membershipHash.values()); return memberships; } /** * Gets the default instance. * * @return the default instance */ public final MembershipBean getDefaultInstance() { return this.getInstance(this.defaultMembershipClass, this.defaultMembershipType); } /** * Gets the single instance of MembershipDAO. * * @param membershipClass the membership class * @param membershipType the membership type * * @return single instance of MembershipDAO */ public final MembershipBean getInstance(final String membershipClass, final String membershipType) { MembershipBean membership = null; if (this.membershipHash == null) { loadMembershipHash(); } final String key = membershipClass + "__" + membershipType; if (this.membershipHash != null) { if (this.membershipHash.containsKey(key)) { membership = this.membershipHash.get(key).clone(); } } if (membership == null) { membership = new MembershipBean(); } return membership; } /** * Used to get an ArrayList of MembershipBean details for a specified GUID * number. * * @param guid the guid * @param allMembership the all membership * @param membershipClass the membership class * @param membershipType the membership type * * @return the collection< membership bean> * * @throws WhichDoctorDaoException the which doctor dao exception */ @SuppressWarnings("unchecked") public final Collection<MembershipBean> load(final int guid, final boolean allMembership, final String membershipClass, final String membershipType) throws WhichDoctorDaoException { dataLogger.info("Membership details for GUID: " + guid + " requested"); // Do check whether required results are light (only primary) // or full (all memberships) boolean specificMembership = false; StringBuffer sql = new StringBuffer(); sql.append(getSQL().getValue("membership/load")); sql.append(" WHERE membership.Active = true" + " AND membership.ReferenceGUID = ?"); Collection<Object> parameters = new ArrayList<Object>(); parameters.add(guid); if (StringUtils.isNotBlank(membershipClass)) { if (!StringUtils.equals(membershipClass, "Preferred")) { sql.append(" AND membershiptype.Class = ?"); parameters.add(membershipClass); specificMembership = true; } } if (StringUtils.isNotBlank(membershipType)) { if (!StringUtils.equals(membershipType, "Preferred")) { sql.append(" AND membershiptype.Name = ?"); parameters.add(membershipType); specificMembership = true; } } sql.append(" ORDER BY PrimaryMembership DESC, membership.GUID ASC"); if (!allMembership) { sql.append(" LIMIT 1"); } Collection<MembershipBean> memberships = new ArrayList<MembershipBean>(); try { memberships = this.getJdbcTemplateReader().query(sql.toString(), parameters.toArray(), new RowMapper() { public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException { return loadMembership(rs); } }); } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for search: " + ie.getMessage()); } if (specificMembership) { // Specific membership field set, check to see if result // If not load more generic if (memberships.size() == 0) { if (StringUtils.isNotBlank(membershipType)) { /* MembershipType set, load just membership class */ memberships = load(guid, allMembership, membershipClass, null); } if (memberships.size() == 0) { if (StringUtils.isNotBlank(membershipClass)) { /* MembershipClass set, load just membership */ memberships = load(guid, allMembership, null, null); } } } } return memberships; } /** * Load the MembershipBean based on the supplied membership id. * * @param membershipId the membership id * * @return the membership bean * * @throws WhichDoctorDaoException the which doctor dao exception */ public final MembershipBean load(final int membershipId) throws WhichDoctorDaoException { dataLogger.info("Getting membershipId:" + membershipId); final String loadSQL = getSQL().getValue("membership/load") + " WHERE membership.Id = ?"; MembershipBean membership = null; try { membership = (MembershipBean) this.getJdbcTemplateReader().queryForObject(loadSQL, new Object[] { membershipId }, new RowMapper() { public Object mapRow(final ResultSet rs, final int rowNum) throws SQLException { return loadMembership(rs); } }); } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for search: " + ie.getMessage()); } return membership; } /** * Creates the MembershipBean. * * @param membership the membership * @param checkUser the check user * @param privileges the privileges * * @return the int * * @throws WhichDoctorDaoException the which doctor dao exception */ public final int create(final MembershipBean membership, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { membership.setActive(true); return save(membership, checkUser, privileges, "create"); } /** * Modify the MembershipBean. * * @param membership the membership * @param checkUser the check user * @param privileges the privileges * * @return the int * * @throws WhichDoctorDaoException the which doctor dao exception */ public final int modify(final MembershipBean membership, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { membership.setActive(true); return save(membership, checkUser, privileges, "modify"); } /** * Delete the MembershipBean. * * @param membership the membership * @param checkUser the check user * @param privileges the privileges * * @return true, if successful * * @throws WhichDoctorDaoException the which doctor dao exception */ public final boolean delete(final MembershipBean membership, final UserBean checkUser, final PrivilegesBean privileges) throws WhichDoctorDaoException { boolean success = false; membership.setActive(false); int deleted = save(membership, checkUser, privileges, "delete"); if (deleted > 0) { success = true; } return success; } /** * Save the updated MembershipBean. * * @param membership the membership * @param checkUser the check user * @param privileges the privileges * @param action the action * * @return the int * * @throws WhichDoctorDaoException the which doctor dao exception */ private int save(final MembershipBean membership, final UserBean checkUser, final PrivilegesBean privileges, final String action) throws WhichDoctorDaoException { /* Check required information within membership bean is present */ if (membership.getMembershipClass() == null) { throw new WhichDoctorDaoException("Membership class cannot be null"); } if (StringUtils.isBlank(membership.getMembershipClass())) { throw new WhichDoctorDaoException("Membership class cannot be an empty string"); } if (membership.getMembershipType() == null) { throw new WhichDoctorDaoException("Membership type cannot be null"); } if (membership.getReferenceGUID() == 0) { throw new WhichDoctorDaoException("Membership requires a valid Reference GUID number"); } if (!privileges.getPrivilege(checkUser, "membership", action)) { throw new WhichDoctorDaoException("Insufficient user credentials to " + action + " membership entry"); } int membershipTypeId = 0; try { ObjectTypeBean object = this.getObjectTypeDAO().load("Membership", membership.getMembershipType(), membership.getMembershipClass()); membershipTypeId = object.getObjectTypeId(); } catch (Exception e) { dataLogger.error("Error loading objecttype for membership type: " + e.getMessage()); } if (membershipTypeId == 0) { throw new WhichDoctorDaoException("Membership type not found"); } int membershipId = 0; /* Load objecttypeIds */ HashMap<Integer, Integer> objectTypeIds = new HashMap<Integer, Integer>(); for (int i = 1; i <= MAX_OBJECTTYPES; i++) { /* By default set the objectTypeId value to 0 */ objectTypeIds.put(new Integer(i), new Integer(0)); if (membership.getObjectTypeField(i) != null) { try { ObjectTypeBean object = membership.getObjectTypeField(i); ObjectTypeBean loadedObject = this.getObjectTypeDAO().load(object.getObject(), object.getName(), object.getClassName()); objectTypeIds.put(new Integer(i), new Integer(loadedObject.getObjectTypeId())); } catch (Exception e) { dataLogger.error("Error loading objecttype Id: " + e.getMessage()); } } } ArrayList<Object> parameters = new ArrayList<Object>(); parameters.add(membership.getReferenceGUID()); parameters.add(membershipTypeId); parameters.add(membership.getMemo()); parameters.add(membership.getJoinedDate()); parameters.add(membership.getLeftDate()); parameters.add(membership.getPrimary()); /* Set integer fields */ for (int i = 1; i <= MAX_INTEGERS; i++) { parameters.add(membership.getIntField(i)); } /* Set objecttype fields */ for (int i = 1; i <= MAX_OBJECTTYPES; i++) { parameters.add(objectTypeIds.get(i)); } /* Set String fields */ for (int i = 1; i <= MAX_CHARS; i++) { parameters.add(membership.getCharField(i)); } /* Set date fields */ for (int i = 1; i <= MAX_DATES; i++) { Date membershipDate = null; if (membership.getDateField(i) != null) { membershipDate = new Date(membership.getDateField(i).getTime()); } parameters.add(membershipDate); } /* The general details about this membership field */ Timestamp sqlTimeStamp = new Timestamp(Calendar.getInstance().getTimeInMillis()); parameters.add(membership.getActive()); parameters.add(sqlTimeStamp); parameters.add(checkUser.getDN()); parameters.add(membership.getLogMessage(action)); /* Begin the ISB transaction */ IsbTransactionBean isbTransaction = this.isbTransactionDAO.begin(membership.getReferenceGUID()); try { Integer[] result = this.performUpdate("membership", membership.getGUID(), parameters, "Membership", checkUser, action); /* Set the returned guid and id values */ membership.setGUID(result[0]); membershipId = result[1]; } catch (Exception e) { dataLogger.error("Error processing membership record: " + e.getMessage()); throw new WhichDoctorDaoException("Error processing membership record: " + e.getMessage()); } if (membershipId > 0) { /* Update primary flag to reflect modification */ updatePrimary(membership, action); /* Commit the ISB transaction */ this.isbTransactionDAO.commit(isbTransaction); } return membershipId; } /** * Load membership hash. */ private void loadMembershipHash() { this.membershipHash = new HashMap<String, MembershipBean>(); // Load all the membership object types Collection<ObjectTypeBean> objects = null; try { objects = this.getObjectTypeDAO().load("Membership"); } catch (SFSDaoException sde) { dataLogger.error("Error loading Membership object types: " + sde.getMessage()); } if (objects != null) { for (ObjectTypeBean object : objects) { MembershipBean membership = null; final String strClass = object.getClassName(); final String strType = object.getName(); final String key = strClass + "__" + strType; dataLogger.debug("Membership Key: " + key); try { Map<String, FieldMapBean> fieldMapping = this.fieldMapDAO.load(strClass, strType); membership = new MembershipBean(); membership.setType(strClass, strType, fieldMapping); } catch (Exception e) { dataLogger.error("Error initiating new MembershipBean: " + e.getMessage()); } if (membership != null) { // Add the membership bean to the internal hash this.membershipHash.put(key, membership); } } } } /** * Load membership. * * @param rs the rs * * @return the membership bean * * @throws SQLException the SQL exception */ private MembershipBean loadMembership(final ResultSet rs) throws SQLException { MembershipBean membership = this.getInstance(rs.getString("MembershipClass"), rs.getString("MembershipType")); membership.setId(rs.getInt("Id")); membership.setGUID(rs.getInt("GUID")); membership.setReferenceGUID(rs.getInt("ReferenceGUID")); membership.setMemo(rs.getString("Memo")); try { membership.setJoinedDate(rs.getDate("JoinedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading JoinedDate: " + sqe.getMessage()); } try { membership.setLeftDate(rs.getDate("LeftDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading LeftDate: " + sqe.getMessage()); } membership.setPrimary(rs.getBoolean("PrimaryMembership")); for (int i = 1; i <= MAX_INTEGERS; i++) { String field = "intField" + i; membership.setIntField(i, rs.getInt(field)); } for (int i = 1; i <= MAX_OBJECTTYPES; i++) { String field = "objecttypeField" + i; if (rs.getInt(field) > 0) { // Value exists, load objecttype bean from supplied dataset ObjectTypeBean objectType = loadObjectType(String.valueOf(i), rs); membership.setObjectTypeField(i, objectType); } } for (int i = 1; i <= MAX_CHARS; i++) { String field = "charField" + i; membership.setCharField(i, rs.getString(field)); } for (int i = 1; i <= MAX_DATES; i++) { String field = "dateField" + i; try { membership.setDateField(i, rs.getDate(field)); } catch (SQLException sqe) { dataLogger.debug("Error reading dateField (" + i + "): " + sqe.getMessage()); } } membership.setActive(rs.getBoolean("Active")); try { membership.setCreatedDate(rs.getTimestamp("CreatedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading CreatedDate: " + sqe.getMessage()); } membership.setCreatedBy(rs.getString("CreatedBy")); try { membership.setModifiedDate(rs.getTimestamp("ModifiedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading ModifiedDate: " + sqe.getMessage()); } membership.setModifiedBy(rs.getString("ModifiedBy")); try { membership.setExportedDate(rs.getTimestamp("ExportedDate")); } catch (SQLException sqe) { dataLogger.debug("Error reading ExportedDate: " + sqe.getMessage()); } membership.setExportedBy(rs.getString("ExportedBy")); return membership; } /** * Load object type. * * @param fieldValue the field value * @param rs the rs * * @return the object type bean */ private ObjectTypeBean loadObjectType(final String fieldValue, final ResultSet rs) { // Load objecttype bean from supplied dataset ObjectTypeBean objectType = new ObjectTypeBean(); try { objectType.setObjectTypeId(rs.getInt("objecttypeField" + fieldValue)); objectType.setName(rs.getString("objecttypeName" + fieldValue)); objectType.setClassName(rs.getString("objecttypeClass" + fieldValue)); objectType.setObject(rs.getString("objecttypeObject" + fieldValue)); objectType.setAbbreviation(rs.getString("objecttypeAbbreviation" + fieldValue)); objectType.setSecurity(rs.getString("objecttypeSecurity" + fieldValue)); objectType.setLdapMapping(1, rs.getString("objecttypeLdapMapping" + fieldValue)); } catch (Exception e) { dataLogger.error("Error loading objecttype data"); return null; } return objectType; } /** * If the MembershipBean is primary ensure no other primary entry exists for * the ReferenceGUID. * * @param membership the membership * @param action the action * * @throws WhichDoctorDaoException the which doctor dao exception */ private void updatePrimary(final MembershipBean membership, final String action) throws WhichDoctorDaoException { if (membership.getPrimary()) { if (!StringUtils.equals(action, "delete")) { /* Find old primary address and turn off flag */ this.getJdbcTemplateWriter().update(this.getSQL().getValue("membership/updatePrimary"), new Object[] { false, membership.getReferenceGUID(), membership.getGUID(), true }); } } } /** * Gets the candidate number. * * @param personGUID the person guid * * @return the candidate number * * @throws WhichDoctorDaoException the which doctor dao exception */ public final String getCandidateNumber(final int personGUID) throws WhichDoctorDaoException { String candidateNo = ""; String year = ""; /* By default use the current year */ Date examDate = new Date(Calendar.getInstance().getTimeInMillis()); if (personGUID > 0) { // Get year, use cutoff date of Nov 22 for year. // 23 Nov is next year BuilderBean builderBean = new BuilderBean(); builderBean.setParameter("EXAMS", true); PersonBean person = this.personDAO.loadGUID(personGUID, builderBean); boolean writtenExamSet = false; if (person != null && person.getExams() != null) { for (ExamBean exam : person.getExams()) { // Assume that exams are returned in chronological // order, first written exam is the assumed year. if (!writtenExamSet) { if (StringUtils.equals(exam.getType(), "Written Exam")) { examDate = new Date(exam.getDateSat().getTime()); writtenExamSet = true; } } } } } /* Format the year */ SimpleDateFormat df = new SimpleDateFormat("yyyy"); year = df.format(examDate); final int yearLength = 4; final int randomNumberLength = 4; final int randomSize = 10; candidateNo += year.substring(0, 1) + year.substring(2, yearLength); /* Generate four digit random number */ Random rand = new Random(); for (int x = 0; x < randomNumberLength; x++) { int random = rand.nextInt(randomSize); candidateNo += String.valueOf(random); } if (!uniqueCandidateNumber(personGUID, candidateNo)) { candidateNo = getCandidateNumber(personGUID); } return candidateNo; } /** * Validate candidate number. * * @param personGUID the person guid * @param testNumber the test number * * @return the string * * @throws WhichDoctorDaoException the which doctor dao exception */ public final String validateCandidateNumber(final int personGUID, final int testNumber) throws WhichDoctorDaoException { String candidateNumber = String.valueOf(testNumber); final int candidateNumberLength = 7; if (candidateNumber.length() != candidateNumberLength) { // Candidate number length is invalid // Generate a new unique number candidateNumber = getCandidateNumber(personGUID); } /* Test uniqueness */ if (!uniqueCandidateNumber(personGUID, candidateNumber)) { candidateNumber = getCandidateNumber(personGUID); } return candidateNumber; } /** * Check if the same number exists already. If it does then generate a new * number * * @param personGUID the person guid * @param candidateNumber the candidate number * * @return true, if unique candidate number * * @throws WhichDoctorDaoException the which doctor dao exception */ private boolean uniqueCandidateNumber(final int personGUID, final String candidateNumber) throws WhichDoctorDaoException { boolean unique = true; try { int uniquenessCount = this.getJdbcTemplateReader().queryForInt( this.getSQL().getValue("membership/checkCandidateNo"), new Object[] { personGUID, candidateNumber }); if (uniquenessCount > 0) { /* The number is not unique */ unique = false; } } catch (IncorrectResultSizeDataAccessException ie) { dataLogger.debug("No results found for search: " + ie.getMessage()); } return unique; } /** * Automatically updates the membership status of a person if their details * meet the criteria. * * The training status can be one of five possible types: * - "Not in training" * - "Training" - "Standard" * - "Training" - "Post-FRACP" * - "Training" - "No current rotation" * The person is a trainee but is not currently undertaking a rotation * - "Conditional training" - "Unmet requirements" * Undertaking AT rotations but hasn't met BT requirements * - "Conditional training" - "Requires upgrade" * Has met BT requirements but has yet to be updated to an AT * * @param personGUID the person guid * @param user the user * @param privileges the privileges */ public final void updateTrainingStatus(final int personGUID, final UserBean user, final PrivilegesBean privileges) { boolean membershipUpgrade = false; // Get the preferences and check whether the automatic update should be performed. try { final PreferencesBean preferences = this.preferencesDAO.load(); final String update = preferences.getOption("system", "membershipupgrade"); if (StringUtils.isNotBlank(update)) { membershipUpgrade = Boolean.parseBoolean(update); } } catch (WhichDoctorDaoException wde) { dataLogger.error("Error loading preferences: " + wde.getMessage()); } dataLogger.info("Performing automatic update check: " + personGUID); // Load the person based on the guid with // basic training summary and exams PersonBean person = new PersonBean(); try { BuilderBean loadDetails = new BuilderBean(); loadDetails.setParameter("EXAMS", true); loadDetails.setParameter("MEMBERSHIP", true); loadDetails.setParameter("TRAINING_BASIC", true); loadDetails.setParameter("TRAINING_ROTATIONS", true); person = this.personDAO.loadGUID(personGUID, loadDetails); } catch (Exception e) { dataLogger.error("Error loading person for automated training update: " + e.getMessage()); } dataLogger.info("Performing training status update for: " + person.getGUID()); int trainingStatusId = NOT_IN_TRAINING; if (StringUtils.equalsIgnoreCase(person.getMembershipField("Membership Type"), "Member: Basic Trainee")) { // The person is a basic trainee, check if undertaking a rotation trainingStatusId = STANDARD_NO_CURRENT; if (hasCurrentRotation(person)) { trainingStatusId = STANDARD_TRAINING; } if (testIfMembershipUpgradeApplicable(person)) { if (membershipUpgrade) { dataLogger.info("Performing automated membership upgrade"); changeToAdvancedTrainee(personGUID, user, privileges); } else { // By default set to conditional - no current trainingStatusId = CONDITIONAL_NO_CURRENT; if (hasCurrentRotation(person)) { // Is currently training with some sort of rotation trainingStatusId = CONDITIONAL_CONTINUING; } if (hasCurrentRotation(person, "Advanced Training")) { // Is currently training with an advanced rotation trainingStatusId = CONDITIONAL_REQUIRES_UPGRADE; } } } else { // Check if they are currently undertaking any AT rotations if (hasCurrentRotation(person, "Advanced Training")) { trainingStatusId = CONDITIONAL_UNMET; } } } if (StringUtils.equalsIgnoreCase(person.getMembershipField("Membership Type"), "Member: Advanced Trainee")) { // The person is an advanced trainee, check if undertaking a rotation trainingStatusId = STANDARD_NO_CURRENT; if (hasCurrentRotation(person)) { trainingStatusId = STANDARD_TRAINING; } } if (trainingStatusId == NOT_IN_TRAINING) { // Check if currently undertaking a rotation. if (hasCurrentRotation(person)) { trainingStatusId = STANDARD_POST_FRACP; } } try { updateTrainingStatus(person, trainingStatusId); } catch (WhichDoctorDaoException wde) { dataLogger.error("Error updating training status: " + wde.getMessage()); } } /** * Updates all training statuses. * * @param user the user * @param privileges the privileges * * @throws WhichDoctorDaoException the which doctor dao exception */ public final void updateAllTrainingStatuses(final UserBean user, final PrivilegesBean privileges) throws WhichDoctorDaoException { dataLogger.info("Rebuilding all training statuses"); Collection<PersonBean> people = personDAO.loadActivePeople(); for (PersonBean person : people) { updateTrainingStatus(person.getGUID(), user, privileges); dataLogger.info("Refreshed training status for guid: " + person.getGUID()); } } /** * Update training statuses between two dates. * * @param startDate the start date * @param endDate the end date * @throws WhichDoctorDaoException the which doctor dao exception */ public final void updateTrainingStatuses(final java.util.Date startDate, final java.util.Date endDate) throws WhichDoctorDaoException { dataLogger.info("Rebuilding training statuses between two dates"); if (startDate == null) { throw new WhichDoctorDaoException("The start date cannot be null"); } if (endDate == null) { throw new WhichDoctorDaoException("The end date cannot be null"); } // A map to store the potentially modified people GUIDs HashMap<Integer, Integer> peopleGUIDs = new HashMap<Integer, Integer>(); // Perform a search for rotations that started between the period of // time defined by the startDate and endDate parameters. try { SearchBean search = this.searchDAO.initiate("rotation", null); search.setLimit(0); RotationBean criteria = (RotationBean) search.getSearchCriteria(); RotationBean constraints = (RotationBean) search.getSearchConstraints(); criteria.setStartDate(startDate); constraints.setStartDate(endDate); search.setSearchCriteria(criteria); search.setSearchConstraints(constraints); SearchResultsBean results = this.searchDAO.search(search); if (results != null) { Collection<Object> rotations = results.getSearchResults(); for (Object objRotation : rotations) { RotationBean rotation = (RotationBean) objRotation; if (rotation.getPersonId() > 0) { peopleGUIDs.put(rotation.getPersonId(), 0); } } } } catch (WhichDoctorSearchDaoException wse) { dataLogger.error("Error performing search for recently started " + "rotations: " + wse.getMessage()); } // Perform a search for rotations that ended between the period of time // defined by the startDate and endDate parameters. try { SearchBean search = this.searchDAO.initiate("rotation", null); search.setLimit(0); RotationBean criteria = (RotationBean) search.getSearchCriteria(); RotationBean constraints = (RotationBean) search.getSearchConstraints(); criteria.setEndDate(startDate); constraints.setEndDate(endDate); search.setSearchCriteria(criteria); search.setSearchConstraints(constraints); SearchResultsBean results = this.searchDAO.search(search); if (results != null) { Collection<Object> rotations = results.getSearchResults(); for (Object objRotation : rotations) { RotationBean rotation = (RotationBean) objRotation; if (rotation.getPersonId() > 0) { peopleGUIDs.put(rotation.getPersonId(), 0); } } } } catch (WhichDoctorSearchDaoException wse) { dataLogger.error("Error performing search for recently started " + "rotations: " + wse.getMessage()); } UserBean systemUser = getSystemUser("Membership", "System"); PrivilegesBean privileges = new PrivilegesBean(); try { privileges = this.privilegesDAO.load(); } catch (SFSDaoException sfe) { dataLogger.error("Error loading privileges: " + sfe.getMessage()); } // With the searches complete iterate through the map and rebuild each // of the training statuses. for (Integer personGUID : peopleGUIDs.keySet()) { dataLogger.info("Rebuilding training status for person GUID: " + personGUID); this.updateTrainingStatus(personGUID, systemUser, privileges); } } /** * Update the training status. * * @param person the person * @param trainingStatusId the training status id * @throws WhichDoctorDaoException the which doctor dao exception */ private void updateTrainingStatus(final PersonBean person, final int trainingStatusId) throws WhichDoctorDaoException { String trainingStatus = ""; String trainingStatusDetail = ""; switch (trainingStatusId) { case NOT_IN_TRAINING: trainingStatus = "Not in training"; break; case STANDARD_TRAINING: trainingStatus = "Training"; trainingStatusDetail = "Standard"; break; case STANDARD_NO_CURRENT: trainingStatus = "Training"; trainingStatusDetail = "No current rotation"; break; case STANDARD_POST_FRACP: trainingStatus = "Training"; trainingStatusDetail = "Post-FRACP"; break; case CONDITIONAL_REQUIRES_UPGRADE: trainingStatus = "Conditional training"; trainingStatusDetail = "Requires upgrade"; break; case CONDITIONAL_UNMET: trainingStatus = "Conditional training"; trainingStatusDetail = "Unmet requirements"; break; case CONDITIONAL_NO_CURRENT: trainingStatus = "Conditional training"; trainingStatusDetail = "No current rotation"; break; case CONDITIONAL_CONTINUING: trainingStatus = "Conditional training"; trainingStatusDetail = "Continuing training"; break; } int objectTypeId = 0; try { ObjectTypeBean object = this.getObjectTypeDAO().load("Training Status", trainingStatusDetail, trainingStatus); objectTypeId = object.getObjectTypeId(); } catch (SFSDaoException sfe) { dataLogger.error("Error loading objecttype for the training status: " + sfe.getMessage()); } dataLogger.info("Training status update for GUID: " + person.getGUID()); dataLogger.info("Training status: " + trainingStatus); dataLogger.info("Training status detail: " + trainingStatusDetail); boolean updateTrainingStatus = false; if (!StringUtils.equalsIgnoreCase(person.getTrainingStatus(), trainingStatus)) { updateTrainingStatus = true; } if (!StringUtils.equalsIgnoreCase(person.getTrainingStatusDetail(), trainingStatusDetail)) { updateTrainingStatus = true; } if (updateTrainingStatus) { /* Begin the ISB transaction */ IsbTransactionBean isbTransaction = this.isbTransactionDAO.begin(person.getGUID()); int updateCount = 0; try { updateCount = this.getJdbcTemplateWriter().update( this.getSQL().getValue("person/updateTrainingStatus"), new Object[] { objectTypeId, person.getGUID() }); } catch (DataAccessException de) { dataLogger.error("Error updating the training status id for person guid: " + person.getGUID() + ", " + de.getMessage()); } if (updateCount > 0) { /* Commit the ISB transaction */ this.isbTransactionDAO.commit(isbTransaction); } } } /** * Test if the person is applicable for a membership upgrade. * * @param person the person * @return true, if successful */ private boolean testIfMembershipUpgradeApplicable(final PersonBean person) { boolean membershipUpgradeApplicable = false; dataLogger.info("Automated training check: person is a basic trainee"); boolean passedWritten = false; boolean passedClinical = false; int totalTraining = 0; if (person.getExams() != null) { for (ExamBean exam : person.getExams()) { if (StringUtils.equalsIgnoreCase(exam.getStatus(), "Passed")) { if (StringUtils.equalsIgnoreCase(exam.getType(), "Written Exam")) { passedWritten = true; } if (StringUtils.equalsIgnoreCase(exam.getType(), "Clinical Exam")) { passedClinical = true; } } } } final TreeMap<String, AccreditationBean[]> summary = person.getTrainingSummary("Basic Training"); if (summary != null) { for (String key : summary.keySet()) { AccreditationBean[] details = summary.get(key); AccreditationBean core = details[0]; AccreditationBean nonCore = details[1]; totalTraining += core.getWeeksCertified(); totalTraining += nonCore.getWeeksCertified(); } } final int basicTrainingLimit = this.getTrainingLimit("Basic Training"); if (totalTraining >= basicTrainingLimit && passedWritten && passedClinical) { membershipUpgradeApplicable = true; } return membershipUpgradeApplicable; } /** * Checks if the person has a current rotation. * * @param person the person * @return true, if successful */ private boolean hasCurrentRotation(final PersonBean person) { return hasCurrentRotation(person, ""); } /** * Checks if the person has a current rotation. * If the type parameter is supplied the function checks by type, otherwise ignored. * * @param person the person * @param type the rotation type * @return true, if successful */ private boolean hasCurrentRotation(final PersonBean person, final String type) { boolean currentRotation = false; java.util.Date today = Calendar.getInstance().getTime(); if (person.getRotations() != null) { for (RotationBean rtn : person.getRotations()) { if (rtn.getStartDate().compareTo(today) <= 0 && rtn.getEndDate().compareTo(today) >= 0) { if (StringUtils.isNotBlank(type)) { if (StringUtils.equalsIgnoreCase(rtn.getRotationType(), type)) { currentRotation = true; } } else { currentRotation = true; } } } } return currentRotation; } /** * Change to advanced trainee. * * @param guid the guid * @param user the user * @param privileges the privileges */ private void changeToAdvancedTrainee(final int guid, final UserBean user, final PrivilegesBean privileges) { /* The person needs upgrading to an advanced trainee */ try { Collection<MembershipBean> memberships = load(guid, false, "", ""); if (memberships != null) { for (MembershipBean membership : memberships) { if (membership.getPrimary()) { ObjectTypeBean object = new ObjectTypeBean(); object.setObject("Membership Type"); object.setClassName("Member"); object.setName("Advanced Trainee"); membership.setField("Membership Type", object); membership.setLogMessage("Advanced trainee automated upgrade"); modify(membership, user, privileges); } } } } catch (WhichDoctorDaoException wde) { dataLogger.error("Error performing automated membership update: " + wde.getMessage()); } /* Check their specialties to see if they need upgrading */ try { Collection<SpecialtyBean> specialties = this.specialtyDAO.load(guid, true); if (specialties != null) { for (SpecialtyBean specialty : specialties) { if (StringUtils.equalsIgnoreCase("Basic Training", specialty.getTrainingProgram()) && StringUtils.equalsIgnoreCase("In training", specialty.getStatus())) { specialty.setStatus("Completed training"); specialty.setLogMessage("Advanced trainee automated upgrade);"); specialtyDAO.modify(specialty, user, privileges); } } } } catch (WhichDoctorDaoException wde) { dataLogger.error("Error performing automated specialty update: " + wde.getMessage()); } } }