org.jasig.ssp.service.impl.AbstractPlanServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.ssp.service.impl.AbstractPlanServiceImpl.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo licenses this file to you under the Apache 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 the following location:
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * 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.
 */
package org.jasig.ssp.service.impl;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import javax.validation.constraints.NotNull;

import org.apache.commons.lang.StringUtils;
import org.jasig.ssp.dao.AbstractPlanDao;
import org.jasig.ssp.model.AbstractPlan;
import org.jasig.ssp.model.AbstractPlanCourse;
import org.jasig.ssp.model.Person;
import org.jasig.ssp.model.SubjectAndBody;
import org.jasig.ssp.model.TermCourses;
import org.jasig.ssp.model.external.ExternalCourseRequisite;
import org.jasig.ssp.model.external.ExternalStudentTranscriptCourse;
import org.jasig.ssp.model.external.RequisiteCode;
import org.jasig.ssp.model.external.Term;
import org.jasig.ssp.service.AbstractAuditableCrudService;
import org.jasig.ssp.service.AbstractPlanService;
import org.jasig.ssp.service.ObjectNotFoundException;
import org.jasig.ssp.service.PersonService;
import org.jasig.ssp.service.SecurityService;
import org.jasig.ssp.service.external.ExternalCourseRequisiteService;
import org.jasig.ssp.service.external.ExternalCourseService;
import org.jasig.ssp.service.external.ExternalStudentTranscriptCourseService;
import org.jasig.ssp.service.external.TermService;
import org.jasig.ssp.service.reference.ConfigService;
import org.jasig.ssp.service.reference.MessageTemplateService;
import org.jasig.ssp.transferobject.AbstractPlanCourseTO;
import org.jasig.ssp.transferobject.AbstractPlanOutputTO;
import org.jasig.ssp.transferobject.AbstractPlanTO;
import org.jasig.ssp.transferobject.PlanTO;
import org.jasig.ssp.transferobject.TermNoteTO;
import org.jasig.ssp.transferobject.reference.AbstractMessageTemplateMapPrintParamsTO;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import com.google.common.collect.Lists;

/**
 * 
 * @author tony.arland
 */
@Transactional
public abstract class AbstractPlanServiceImpl<T extends AbstractPlan, TO extends AbstractPlanTO<T>, TOO extends AbstractPlanOutputTO<T, TO>, TOOMT extends AbstractMessageTemplateMapPrintParamsTO<TOO, T, TO>>
        extends AbstractAuditableCrudService<T> implements AbstractPlanService<T, TO, TOO, TOOMT> {

    @Autowired
    private SecurityService securityService;

    @Autowired
    private TermService termService;

    @Autowired
    private MessageTemplateService messageTemplateService;

    @Autowired
    private PersonService personService;

    @Autowired
    private ExternalCourseService courseService;

    @Autowired
    private ExternalCourseRequisiteService courseRequisiteService;

    @Autowired
    private ExternalStudentTranscriptCourseService studentTranscriptService;

    @Autowired
    private transient ConfigService configService;

    @Override
    public T save(T obj) {
        return getDao().save(obj);
    }

    @Override
    public T get(@NotNull final UUID id) throws ObjectNotFoundException {
        final T obj = getDao().get(id);
        if (obj == null)
            throw new ObjectNotFoundException(id, this.getClass().getName());
        return obj;

    }

    @Override
    protected abstract AbstractPlanDao<T> getDao();

    public SecurityService getSecurityService() {
        return securityService;
    }

    public void setSecurityService(SecurityService securityService) {
        this.securityService = securityService;
    }

    @Override
    public T copyAndSave(T model, Person newOwner) throws CloneNotSupportedException {
        return getDao().cloneAndSave(model, newOwner);
    }

    protected String getInstitutionName() {
        return configService.getByNameNullOrDefaultValue("inst_name");
    }

    @Override
    @Transactional(readOnly = true)
    public SubjectAndBody createFullOutput(TOO planOutput) throws ObjectNotFoundException {
        TO plan = planOutput.getNonOutputTO();
        //TODO eventually find a better way to set the student when in context
        Person student = plan instanceof PlanTO ? personService.get(UUID.fromString(((PlanTO) plan).getPersonId()))
                : null;
        Person owner = personService.get(UUID.fromString(plan.getOwnerId()));

        List<TermCourses<T, TO>> courses = collectTermCourses(plan);
        BigDecimal totalPlanCreditHours = calculateTotalPlanHours(courses);
        BigDecimal totalPlanDevHours = calculateTotalPlanDevHours(courses);

        SubjectAndBody subjectAndBody = messageTemplateService.createMapPlanFullOutput(student, owner, planOutput,
                totalPlanCreditHours, totalPlanDevHours, courses, getInstitutionName());
        return subjectAndBody;
    }

    @Override
    @Transactional(readOnly = true)
    public TO validate(TO model) throws ObjectNotFoundException {
        model.setIsValid(true); // SSP-2638. See validatePrerequisites()
        List<? extends AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>> courses = model.getCourses();
        Map<String, List<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>> coursesByTerm = new HashMap<String, List<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>>();
        for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : courses) {
            course.setIsValidInTerm(true);//Set all courses valid in term
            course.setInvalidReasons(null);
            if (coursesByTerm.containsKey(course.getTermCode())) {
                coursesByTerm.get(course.getTermCode()).add(course);
            } else {
                List<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>> termCourses = new ArrayList<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>();
                termCourses.add(course);
                coursesByTerm.put(course.getTermCode(), termCourses);
            }
        }
        final Boolean areAnyCourseTerms = courseService.hasCourseTerms();
        if (areAnyCourseTerms != null && areAnyCourseTerms) {
            for (Map.Entry<String, List<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>> entry : coursesByTerm
                    .entrySet()) {
                final String termCode = entry.getKey();
                final List<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>> coursesInTerm = entry
                        .getValue();
                final List<String> courseCodesInTerm = Lists.newArrayListWithCapacity(coursesInTerm.size());
                for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : coursesInTerm) {
                    courseCodesInTerm.add(course.getCourseCode());
                }
                final List<String> validCourseCodes = getCourseService().getValidCourseCodesForTerm(termCode,
                        courseCodesInTerm);
                for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : coursesInTerm) {
                    if (validCourseCodes.contains(course.getCourseCode())) {
                        continue;
                    } else {
                        course.setIsValidInTerm(false);
                        model.setIsValid(false);
                        course.setInvalidReasons("Course: " + course.getFormattedCourse()
                                + " is not currently offered in the selected term.");
                    }
                }
            }
        }
        model = validatePrerequisites(model);
        return model;
    }

    private TO validatePrerequisites(TO model) throws ObjectNotFoundException {
        List<? extends AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>> courses = model.getCourses();
        List<String> requiringCourseCodes = new ArrayList<String>();
        for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : courses) {
            requiringCourseCodes.add(course.getCourseCode());
            // Only clear validation fields this method controls exclusively. Fields that might be set by validate(),
            // e.g. plan.isValid and course.isValidInTerm, need to be cleared in that method.
            course.setHasCorequisites(true);
            course.setHasPrerequisites(true);
            course.setDuplicateOfTranscript(false);
        }

        List<String> transcriptedCourseCodeCourse = new ArrayList<String>();
        Map<String, ExternalStudentTranscriptCourse> transcriptedCoursesByFormattedCourseCode = new HashMap<String, ExternalStudentTranscriptCourse>();

        String studentSchoolId = getSchoolIdPlannedFor(model);

        if (StringUtils.isNotBlank(studentSchoolId)) {
            List<ExternalStudentTranscriptCourse> transcriptedCourses = studentTranscriptService
                    .getTranscriptsBySchoolId(studentSchoolId);
            for (ExternalStudentTranscriptCourse transcriptedCourse : transcriptedCourses) {
                transcriptedCourseCodeCourse.add(transcriptedCourse.getFormattedCourse());
                transcriptedCoursesByFormattedCourseCode.put(transcriptedCourse.getFormattedCourse(),
                        transcriptedCourse);
            }
        }

        Map<String, Term> termCodeTerm = new HashMap<String, Term>();
        Map<String, String> courseCodeTermCode = new HashMap<String, String>();
        Map<String, AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>> courseCodeCourse = new HashMap<String, AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>();
        List<ExternalCourseRequisite> requisiteCourses = getCourseRequisiteService()
                .getRequisitesForCourses(requiringCourseCodes);
        for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : courses) {

            if (!termCodeTerm.containsKey(course.getTermCode())) {
                try {
                    termCodeTerm.put(course.getTermCode(), getTermService().getByCode(course.getTermCode()));
                } catch (ObjectNotFoundException e) {
                    // nothing to be done
                    // (this sort of failed lookup has been in the field, e.g. where you're looking up an existing
                    // MAP while the back-end ETL job had truncated external_term and hadn't gotten around to
                    // filling it again
                }
            }
            courseCodeTermCode.put(course.getCourseCode(), course.getTermCode());
            courseCodeCourse.put(course.getCourseCode(), course);

            // This section sets isTranscript and duplicateOfTranscript since course may be from client.
            if (transcriptedCoursesByFormattedCourseCode.containsKey(course.getFormattedCourse())) {
                ExternalStudentTranscriptCourse transcriptCourse = transcriptedCoursesByFormattedCourseCode
                        .get(course.getFormattedCourse());
                course.setIsTranscript(true);
                if (!transcriptCourse.getTermCode().equals(course.getTermCode())) {
                    course.setDuplicateOfTranscript(true);
                }
            }
        }

        for (ExternalCourseRequisite requisiteCourse : requisiteCourses) {
            if (transcriptedCourseCodeCourse.contains(requisiteCourse.getRequiredFormattedCourse())) {
                continue;
            }
            AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> requiringCourse = courseCodeCourse
                    .get(requisiteCourse.getRequiringCourseCode());
            AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> requiredCourse = courseCodeCourse
                    .get(requisiteCourse.getRequiredCourseCode());
            if (courseCodeTermCode.containsKey(requisiteCourse.getRequiredCourseCode())) {
                String requiredTermCode = courseCodeTermCode.get(requisiteCourse.getRequiredCourseCode());
                String requireingTermCode = courseCodeTermCode.get(requisiteCourse.getRequiringCourseCode());
                Term requiredTerm = termCodeTerm.get(requiredTermCode);
                Term requireingTerm = termCodeTerm.get(requireingTermCode);
                if (requisiteCourse.getRequisiteCode().equals(RequisiteCode.CO)) {
                    if (!requiredTermCode.equals(requireingTermCode)) {
                        requiringCourse.setHasCorequisites(false);
                        model.setIsValid(false);
                        requiringCourse.setInvalidReasons(
                                " Corequisite " + requiredCourse.getFormattedCourse() + " is not in same term.");
                    }
                } else if (requisiteCourse.getRequisiteCode().equals(RequisiteCode.PRE_CO)) {
                    if (requiredTermCode.equals(requireingTermCode)) {
                        continue;
                    }
                    if (requiredTerm == null || requireingTerm == null) {
                        // no guarantee that we can resolve term codes
                        continue;
                    }
                    if (requireingTerm.getStartDate().before(requiredTerm.getStartDate())) {
                        requiringCourse.setHasCorequisites(false);
                        requiringCourse.setHasPrerequisites(false);
                        model.setIsValid(false);
                        requiringCourse.setInvalidReasons(" Pre/Corequisite " + requiredCourse.getFormattedCourse()
                                + " is not in previous term.");
                    }
                } else if (requisiteCourse.getRequisiteCode().equals(RequisiteCode.PRE)) {
                    if (requiredTermCode.equals(requireingTermCode)) {
                        requiringCourse.setHasPrerequisites(false);
                        model.setIsValid(false);
                        requiringCourse.setInvalidReasons(
                                " Prerequisite " + requiredCourse.getFormattedCourse() + " is in same term.");
                    } else if (requireingTerm.getStartDate().before(requiredTerm.getStartDate())) {
                        requiringCourse.setHasPrerequisites(false);
                        model.setIsValid(false);
                        requiringCourse.setInvalidReasons(" Prerequisite " + requiredCourse.getFormattedCourse()
                                + " is not in previous term.");
                    }
                }

            } else {
                requiringCourse.setHasCorequisites(false);
                requiringCourse.setHasPrerequisites(false);
                model.setIsValid(false);
                requiringCourse.setInvalidReasons(
                        " Pre/co requisite + " + requisiteCourse.getRequiredFormattedCourse() + " is missing.");
            }
        }

        return model;
    }

    protected String getSchoolIdPlannedFor(TO model) throws ObjectNotFoundException {
        UUID personId = getPersonIdPlannedFor(model);
        return personId == null ? null : personService.getSchoolIdForPersonId(personId);
    }

    protected abstract UUID getPersonIdPlannedFor(TO model);

    private BigDecimal calculateTotalPlanDevHours(List<TermCourses<T, TO>> courses) {
        BigDecimal totalDevCreditHours = new BigDecimal(0).setScale(2, BigDecimal.ROUND_HALF_UP);
        for (TermCourses<T, TO> termCourses : courses) {
            totalDevCreditHours = totalDevCreditHours.add(termCourses.getTotalDevCreditHours());
        }
        return totalDevCreditHours;
    }

    protected BigDecimal calculateTotalPlanHours(List<TermCourses<T, TO>> courses) {
        BigDecimal totalPlanCreditHours = new BigDecimal(0).setScale(2, BigDecimal.ROUND_HALF_UP);
        for (TermCourses<T, TO> termCourses : courses) {
            totalPlanCreditHours = totalPlanCreditHours.add(termCourses.getTotalCreditHours());
        }
        return totalPlanCreditHours;
    }

    protected List<TermCourses<T, TO>> collectTermCourses(TO plan) throws ObjectNotFoundException {
        Map<String, TermCourses<T, TO>> semesterCourses = new HashMap<String, TermCourses<T, TO>>();
        List<TermNoteTO> termNotes = plan.getTermNotes();
        List<Term> currentAndFutureTerms = getTermService().getCurrentAndFutureTerms();
        List<Term> futureTerms = currentAndFutureTerms.subList(0, Math.min(6, currentAndFutureTerms.size()));
        for (AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course : plan.getCourses()) {
            if (!semesterCourses.containsKey(course.getTermCode())) {
                Term term = getTermService().getByCode(course.getTermCode());

                TermCourses<T, TO> termCourses = new TermCourses<T, TO>(term);
                for (TermNoteTO termNote : termNotes) {
                    if (termNote.getTermCode().equals(term.getCode())) {
                        termCourses.setContactNotes(termNote.getContactNotes());
                        termCourses.setStudentNotes(termNote.getStudentNotes());
                        termCourses.setIsImportant(termNote.getIsImportant());
                    }
                }
                course.setPlanToOffer(getPlanToOfferTerms(course, futureTerms));
                termCourses.addCourse(course);
                semesterCourses.put(term.getCode(), termCourses);
            } else {
                semesterCourses.get(course.getTermCode()).addCourse(course);
            }
        }

        List<TermCourses<T, TO>> courses = Lists.newArrayList(semesterCourses.values());

        // Order terms in map for printing
        Collections.sort(courses, new Comparator<TermCourses<T, TO>>() {
            public int compare(TermCourses<T, TO> a, TermCourses<T, TO> b) {
                return a.getTerm().getStartDate().compareTo(b.getTerm().getStartDate());
            }
        });

        // Order courses in terms for printing
        for (TermCourses<T, TO> course : semesterCourses.values())
            Collections.sort(course.getCourses(),
                    new Comparator<AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>>>() {
                        public int compare(AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> a,
                                AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> b) {
                            return a.getOrderInTerm().compareTo(b.getOrderInTerm());
                        }
                    });

        return courses;
    }

    private String getPlanToOfferTerms(AbstractPlanCourseTO<T, ? extends AbstractPlanCourse<T>> course,
            List<Term> futureTerms) {
        String planToOffer = "";
        Integer offeredTerms = 0;
        for (Term offeredTerm : futureTerms) {
            if (getCourseService().validateCourseForTerm(course.getCourseCode(), offeredTerm.getCode())) {
                planToOffer = planToOffer + offeredTerm.getName() + " ";
                offeredTerms = offeredTerms + 1;
            }
            if (offeredTerms >= 4)
                break;
        }
        return planToOffer;
    }

    public PersonService getPersonService() {
        return personService;
    }

    public void setPersonService(PersonService personService) {
        this.personService = personService;
    }

    public MessageTemplateService getMessageTemplateService() {
        return messageTemplateService;
    }

    public void setMessageTemplateService(MessageTemplateService messageTemplateService) {
        this.messageTemplateService = messageTemplateService;
    }

    public TermService getTermService() {
        return termService;
    }

    public void setTermService(TermService termService) {
        this.termService = termService;
    }

    public ExternalCourseService getCourseService() {
        return courseService;
    }

    public void setCourseService(ExternalCourseService courseService) {
        this.courseService = courseService;
    }

    public ExternalCourseRequisiteService getCourseRequisiteService() {
        return courseRequisiteService;
    }

    public void setCourseService(ExternalCourseRequisiteService courseRequisiteService) {
        this.courseRequisiteService = courseRequisiteService;
    }
}