uk.ac.ox.oucs.vle.ModuleImpl.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.ox.oucs.vle.ModuleImpl.java

Source

/*
 * #%L
 * Course Signup Implementation
 * %%
 * Copyright (C) 2010 - 2013 University of Oxford
 * %%
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *             http://opensource.org/licenses/ecl2
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package uk.ac.ox.oucs.vle;

/**
 * Module Signup Overnight Jobs
 * 
 *    email course administrator if course component is about to close
 * 
 *    SESii 16.1 (WL-2273) Automated process for reminder emails to be sent to students
 *             
 *    SESii 16.2 Automated email to remind Supervisor and Administrator of any pending approvals
 */

import java.text.DateFormat;
import java.text.MessageFormat;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.util.ResourceLoader;

import uk.ac.ox.oucs.vle.CourseSignupService.Range;
import uk.ac.ox.oucs.vle.CourseSignupService.Status;

public class ModuleImpl implements Module {

    private static final Log log = LogFactory.getLog(ModuleImpl.class);

    private final static ResourceLoader rb = new ResourceLoader("messages");

    private Date lastMidnight;
    private Date todayMidnight;
    private Date aboutToStart;

    /**
     * The DAO to update our entries through.
     */
    private CourseDAO dao;

    /**
     * The proxy for getting users.
     */
    private SakaiProxy proxy;

    private UserPlacementDAO placementDAO;

    public void setCourseDao(CourseDAO dao) {
        this.dao = dao;
    }

    public void setProxy(SakaiProxy proxy) {
        this.proxy = proxy;
    }

    public void setPlacementDAO(UserPlacementDAO placementDAO) {
        this.placementDAO = placementDAO;
    }

    /**
     * 
     */
    public void initialise() {

        Calendar calendar = new GregorianCalendar();
        calendar.set(Calendar.HOUR, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        lastMidnight = (Date) calendar.getTime();

        int aboutToCloseDays = getAboutToCloseDays();
        Calendar today = (Calendar) calendar.clone();
        today.add(Calendar.DATE, aboutToCloseDays);
        todayMidnight = (Date) today.getTime();

        int aboutToStartDays = getAboutToStartDays();
        Calendar tomorrow = (Calendar) calendar.clone();
        tomorrow.add(Calendar.DATE, aboutToStartDays);
        aboutToStart = (Date) tomorrow.getTime();

        log.info("ModuleImpl.initialise");
        log.info("Last Midnight [" + DateFormat.getInstance().format(lastMidnight) + "]");
        log.info("Today Midnight [" + DateFormat.getInstance().format(todayMidnight) + "]");
        log.info("About to Start [" + DateFormat.getInstance().format(aboutToStart) + "]");

        log.info("Courses about to Close [close date between " + DateFormat.getInstance().format(lastMidnight)
                + " and " + DateFormat.getInstance().format(todayMidnight) + "]");

        log.info("Courses about to Start [start date between " + DateFormat.getInstance().format(todayMidnight)
                + " and " + DateFormat.getInstance().format(aboutToStart) + "]");
    }

    /**
     * 
     */
    public void update() {

        initialise();
        String[] words = new String[0];
        // This gets all the groups there are as all the values are ignored.
        final List<CourseGroupDAO> groups = dao.findCourseGroupByWords(words, Range.ALL, new Date(), false);

        modulesClosing(groups);
        modulesStarting(groups);

        int reminderDays = getReminderDays();
        attentionSignups(reminderDays);
    }

    /**
     * Email course administrator if course component is about to close
     * @param groups A list of all groups that may have closing components.
     */
    private void modulesClosing(final List<CourseGroupDAO> groups) {

        for (CourseGroupDAO group : groups) {

            final Set<CourseComponentDAO> components = group.getComponents();
            final Set<CourseComponentDAO> componentsClosing = new HashSet<CourseComponentDAO>();

            for (CourseComponentDAO component : components) {

                if (isToday(component.getCloses())) {
                    // Component is about to close
                    log.info("Component is about to close [" + component.getPresentationId() + ":"
                            + DateFormat.getInstance().format(component.getCloses()) + ":" + component.getTitle()
                            + "]");
                    componentsClosing.add(component);
                }
            }

            if (!componentsClosing.isEmpty()) {
                for (String administrator : group.getAdministrators()) {
                    sendModuleClosingEmail(administrator, group, componentsClosing);
                }
            }
        }
    }

    /**
     * SESii 16.1 Automated process for reminder emails to be sent to students
     * @param groups
     */
    private void modulesStarting(final List<CourseGroupDAO> groups) {

        for (CourseGroupDAO group : groups) {

            final Set<CourseComponentDAO> components = group.getComponents();
            final Set<CourseComponentDAO> componentsStarting = new HashSet<CourseComponentDAO>();

            for (CourseComponentDAO component : components) {

                if (isAboutToStart(component.getStarts())) {
                    // Component is about to start
                    log.info("Component is about to start [" + component.getPresentationId() + ":"
                            + DateFormat.getInstance().format(component.getStarts()) + ":" + component.getTitle()
                            + "]");
                    componentsStarting.add(component);
                }
            }

            for (CourseComponentDAO component : componentsStarting) {
                for (CourseSignupDAO signup : component.getSignups()) {
                    if (Status.CONFIRMED == signup.getStatus()) {
                        sendModuleStartingEmail(signup, component);
                    }
                }
            }
        }
    }

    /**
     * SESii 16.2 Automated email to remind Supervisor and Administrator of any pending approvals
     */
    private void attentionSignups(int reminderDays) {

        List<CourseSignupDAO> signups = dao.findSignupStillPendingOrAccepted(reminderDays);
        log.info("ModuleImpl.attentionSignups [" + signups.size() + "]");

        Map<String, Collection<CourseSignupDAO>> supervisors = new HashMap<String, Collection<CourseSignupDAO>>();
        Map<String, Collection<CourseSignupDAO>> administrators = new HashMap<String, Collection<CourseSignupDAO>>();

        for (CourseSignupDAO signup : signups) {

            if (Status.ACCEPTED == signup.getStatus()) {

                Collection<CourseSignupDAO> set = supervisors.get(signup.getSupervisorId());

                if (null == set) {
                    set = new HashSet<CourseSignupDAO>();
                    supervisors.put(signup.getSupervisorId(), set);
                }
                set.add(signup);
            }

            if (Status.PENDING == signup.getStatus()) {

                CourseGroupDAO group = signup.getGroup();
                Collection<String> admins = group.getAdministrators();

                for (String admin : admins) {

                    Collection<CourseSignupDAO> set = administrators.get(admin);

                    if (null == set) {
                        set = new HashSet<CourseSignupDAO>();
                        administrators.put(admin, set);
                    }
                    set.add(signup);
                }

            }
        }

        for (Map.Entry<String, Collection<CourseSignupDAO>> entry : supervisors.entrySet()) {
            sendBumpSupervisorEmail(entry.getKey(), entry.getValue());
        }

        for (Map.Entry<String, Collection<CourseSignupDAO>> entry : administrators.entrySet()) {
            sendBumpAdministratorEmail(entry.getKey(), entry.getValue());
        }

    }

    private boolean isToday(Date date) {
        if (null != date) {
            if (date.after(lastMidnight) && date.before(todayMidnight)) {
                return true;
            }
        }
        return false;
    }

    private boolean isAboutToStart(Date date) {
        if (null != date) {
            if (date.after(todayMidnight) && date.before(aboutToStart)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Sends a reminder email to an adminstrator that a component for one of their modules closes
     * @param administrator The administrator ID who should receive the email.
     * @param group The course group in question.
     * @param components The components that are closing.
     */
    private void sendModuleClosingEmail(String administrator, CourseGroupDAO group,
            Collection<CourseComponentDAO> components) {

        UserProxy recepient = proxy.findUserById(administrator);
        if (recepient == null) {
            log.warn("Failed to find user for sending email: " + administrator);
            return;
        }
        String to = recepient.getEmail();
        String subject = MessageFormat.format(rb.getString("signup.closing.subject"),
                new Object[] { group.getTitle() });

        StringBuffer componentDetails = new StringBuffer();
        for (CourseComponentDAO component : components) {
            componentDetails.append("\n");
            componentDetails.append(formatComponent(component));
        }
        Object[] baseBodyData = new Object[] { group.getTitle(), componentDetails.toString(),
                proxy.getAdminUrl(proxy.getCurrentPlacementId()) };
        Object[] bodyData = baseBodyData;
        String body = MessageFormat.format(rb.getString("signup.closing.body"), bodyData);
        proxy.sendEmail(to, subject, body);
    }

    private void sendModuleStartingEmail(CourseSignupDAO signup, CourseComponentDAO component) {

        UserProxy recepient = proxy.findUserById(signup.getUserId());
        if (recepient == null) {
            log.warn("Failed to find user for sending email: " + signup.getUserId());
            return;
        }
        CourseUserPlacementDAO placementDao = placementDAO.findUserPlacement(signup.getUserId());
        if (null == placementDao) {
            log.warn("Failed to find placement for sending email: " + signup.getUserId());
            return;
        }
        String placementId = placementDao.getPlacementId();

        Object[] data = new Object[] { signup.getGroup().getTitle(),
                new SimpleDateFormat("EEE d MMM yyyy").format(component.getStarts()), formatComponent(component),
                proxy.getMyUrl(placementId) };

        String to = recepient.getEmail();
        String subject = MessageFormat.format(rb.getString("signup.starting.subject"), data);
        String body = MessageFormat.format(rb.getString("signup.starting.body"), data);
        proxy.sendEmail(to, subject, body);
    }

    private void sendBumpAdministratorEmail(String administratorId, Collection<CourseSignupDAO> signups) {

        UserProxy recepient = proxy.findUserById(administratorId);
        if (recepient == null) {
            log.warn("Failed to find user for sending email: " + administratorId);
            return;
        }
        String to = recepient.getEmail();
        String subject = rb.getString("bump.admin.subject");
        StringBuffer signupsDetails = new StringBuffer();

        for (CourseSignupDAO signup : signups) {
            signupsDetails.append(formatSignup(signup));
            signupsDetails.append("\n");
        }

        CourseUserPlacementDAO placementDao = placementDAO.findUserPlacement(administratorId);
        if (null == placementDao) {
            log.warn("Failed to find placement for sending email: " + administratorId);
            return;
        }
        String placementId = placementDao.getPlacementId();
        String url = proxy.getConfirmUrl(null, placementId);
        String advanceUrl = null;//proxy.getAdvanceUrl(signup.getId(), "accept", null);

        Object[] bodyData = new Object[] { signupsDetails.toString(), url, advanceUrl };
        String body = MessageFormat.format(rb.getString("bump.admin.body"), bodyData);
        proxy.sendEmail(to, subject, body);
    }

    private void sendBumpSupervisorEmail(String supervisorId, Collection<CourseSignupDAO> signups) {

        UserProxy recepient = proxy.findUserById(supervisorId);
        if (recepient == null) {
            log.warn("Failed to find user for sending email: " + supervisorId);
            return;
        }
        String to = recepient.getEmail();
        String subject = rb.getString("bump.supervisor.subject");
        StringBuffer signupsDetails = new StringBuffer();

        for (CourseSignupDAO signup : signups) {
            signupsDetails.append(formatSignup(signup));
            signupsDetails.append("\n");
        }

        CourseUserPlacementDAO placementDao = placementDAO.findUserPlacement(supervisorId);
        if (null == placementDao) {
            log.warn("Failed to find placement for sending email: " + supervisorId);
            return;
        }
        String placementId = placementDao.getPlacementId();
        String url = proxy.getConfirmUrl(null, placementId);
        String advanceUrl = null;//proxy.getAdvanceUrl(signup.getId(), "accept", null);

        Object[] bodyData = new Object[] { signupsDetails.toString(), url, advanceUrl };
        String body = MessageFormat.format(rb.getString("bump.supervisor.body"), bodyData);
        proxy.sendEmail(to, subject, body);
    }

    /**
     * Produce a summary of a component for sending to users.
     * @param component The component to produce a summary of.
     * @return A string to summarise the component.
     */
    public String formatComponent(CourseComponentDAO component) {
        StringBuilder output = new StringBuilder();
        output.append(component.getTitle());
        // TODO - The sessions should really be an int and we should check > 1
        if (component.getSessions() != null && validString(component.getSessions())) {
            output.append(String.format(" runs for %s session(s) and", component.getSessions()));
        }
        output.append(" starts on ");
        if (component.getStarts() != null) {
            output.append(component.getStarts());
        } else {
            output.append(component.getStartsText());
        }

        TermCode termCode = new TermCode(component.getTermcode());
        if (termCode.isValid()) {
            output.append(" (");
            output.append(termCode.getName());
            output.append(")");
        }
        if (validString(component.getTeacherName())) {
            output.append("; the teacher is ");
            output.append(component.getTeacherName());
        }

        return output.toString();
    }

    /**
     * 
     * @param signup
     * @return
     */
    public String formatSignup(CourseSignupDAO signup) {

        StringBuilder output = new StringBuilder();
        CourseGroupDAO group = signup.getGroup();
        UserProxy student = proxy.findUserById(signup.getUserId());

        output.append(student.getDisplayName());
        output.append(" for the course ");
        output.append(group.getTitle());
        output.append(".");

        return output.toString();
    }

    protected int getTodayInt() {
        return proxy.getConfigParam("course-signup.site-id", 1);
    }

    protected int getReminderDays() {
        return proxy.getConfigParam("reminder.days", 7);
    }

    protected int getAboutToStartDays() {
        return proxy.getConfigParam("abouttostart.days", 2);
    }

    protected int getAboutToCloseDays() {
        return proxy.getConfigParam("abouttoclose.days", 1);
    }

    public boolean validString(String string) {
        if (null != string && string.trim().length() > 0) {
            return true;
        }
        return false;
    }
}