org.kuali.student.enrollment.class2.scheduleofclasses.service.impl.ScheduleOfClassesViewHelperServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.kuali.student.enrollment.class2.scheduleofclasses.service.impl.ScheduleOfClassesViewHelperServiceImpl.java

Source

/**
 * Copyright 2012 The Kuali Foundation 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://www.osedu.org/licenses/ECL-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.
 *
 * Created by vgadiyak on 9/10/12
 */
package org.kuali.student.enrollment.class2.scheduleofclasses.service.impl;

import org.apache.commons.lang.BooleanUtils;
import org.apache.commons.lang.StringUtils;
import org.kuali.rice.core.api.config.property.ConfigContext;
import org.kuali.rice.core.api.criteria.PredicateFactory;
import org.kuali.rice.core.api.criteria.QueryByCriteria;
import org.kuali.rice.core.api.util.RiceKeyConstants;
import org.kuali.rice.kim.api.identity.Person;
import org.kuali.rice.kim.impl.KIMPropertyConstants;
import org.kuali.rice.krad.uif.component.ReferenceCopy;
import org.kuali.rice.krad.util.GlobalVariables;
import org.kuali.rice.krad.util.KRADConstants;
import org.kuali.rice.krms.api.repository.NaturalLanguage;
import org.kuali.rice.krms.api.repository.agenda.AgendaDefinition;
import org.kuali.rice.krms.api.repository.agenda.AgendaItemDefinition;
import org.kuali.rice.krms.api.repository.reference.ReferenceObjectBinding;
import org.kuali.rice.krms.api.repository.rule.RuleDefinition;
import org.kuali.rice.krms.api.repository.typerelation.TypeTypeRelation;
import org.kuali.student.common.UUIDHelper;
import org.kuali.student.common.collection.KSCollectionUtils;
import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingClusterWrapper;
import org.kuali.student.enrollment.class2.courseoffering.dto.ActivityOfferingWrapper;
import org.kuali.student.enrollment.class2.courseoffering.dto.RegistrationGroupWrapper;
import org.kuali.student.enrollment.class2.courseoffering.service.decorators.PermissionServiceConstants;
import org.kuali.student.enrollment.class2.courseoffering.service.impl.CourseOfferingManagementViewHelperServiceImpl;
import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingConstants;
import org.kuali.student.enrollment.class2.courseoffering.util.CourseOfferingManagementUtil;
import org.kuali.student.enrollment.class2.scheduleofclasses.dto.CourseOfferingDisplayWrapper;
import org.kuali.student.enrollment.class2.scheduleofclasses.form.ScheduleOfClassesSearchForm;
import org.kuali.student.enrollment.class2.scheduleofclasses.service.ScheduleOfClassesViewHelperService;
import org.kuali.student.enrollment.class2.scheduleofclasses.sort.KSComparator;
import org.kuali.student.enrollment.class2.scheduleofclasses.sort.KSComparatorChain;
import org.kuali.student.enrollment.class2.scheduleofclasses.sort.impl.ActivityOfferingCodeComparator;
import org.kuali.student.enrollment.class2.scheduleofclasses.sort.impl.ActivityOfferingTypeComparator;
import org.kuali.student.enrollment.class2.scheduleofclasses.util.SOCRequisiteWrapper;
import org.kuali.student.enrollment.class2.scheduleofclasses.util.ScheduleOfClassesConstants;
import org.kuali.student.enrollment.class2.scheduleofclasses.util.ScheduleOfClassesUtil;
import org.kuali.student.r2.common.dto.ContextInfo;
import org.kuali.student.common.util.security.ContextUtils;
import org.kuali.student.r2.common.util.constants.CourseOfferingServiceConstants;
import org.kuali.student.r2.common.util.constants.LprServiceConstants;
import org.kuali.student.r2.common.util.constants.LuiServiceConstants;
import org.kuali.student.r2.common.util.date.DateFormatters;
import org.kuali.student.r2.core.acal.dto.TermInfo;
import org.kuali.student.r2.core.class1.search.CourseOfferingManagementSearchImpl;
import org.kuali.student.r2.core.constants.KSKRMSServiceConstants;
import org.kuali.student.r2.core.organization.service.OrganizationService;
import org.kuali.student.r2.core.search.dto.SearchRequestInfo;
import org.kuali.student.r2.lum.util.constants.LrcServiceConstants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Formatter;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;

/**
 * This class performs queries for scheduling of classes
 *
 * @author Kuali Student Team
 */
public class ScheduleOfClassesViewHelperServiceImpl extends CourseOfferingManagementViewHelperServiceImpl
        implements ScheduleOfClassesViewHelperService {

    private static final Logger LOG = LoggerFactory.getLogger(ScheduleOfClassesViewHelperServiceImpl.class);

    @ReferenceCopy
    private KSComparatorChain activityComparatorChain;
    @ReferenceCopy
    private KSComparatorChain regGroupComparatorChain;

    /**
     * These are templates to make creating "additional information" icons with BubblePopOver tooltips.
     * The $FOO items are placeholders which are filled in when the icon is created. The 'script' attribute has been changed
     * from 'first_run' to 'soc_run' and code has been added to onDocumentReady initialize the popovers.
     */
    private final static String TOOLTIP_CREATE_SCRIPT = "<input type='hidden' data-role='script' data-for='$ID' value=\"createTooltip('$ID', '$TEXT', "
            + "{position:'top',alwaysVisible:false,themeName:'all-black',themePath:'$APPLICATION_URL/plugins/tooltip/jquerybubblepopup-theme/',selectable:true,align:'left',distance:'0px',openingSpeed:'250', tail:{ align:'left', hidden: false },tableStyle:{ margin:'0px 0px 5px -8px'}},true, false);\" script='soc_run'>";

    private final static String TOOLTIP_ADD_ATTRIBUTE = "<input type='hidden' data-role='script' data-for='$ID' "
            + "value=\"addAttribute('$ID', 'class', 'uif-tooltip', true);\" script='soc_run'>";

    private final static String IMG = "<img id='$ID' src='$SRC'/>";

    /**
     * Loads all the Course offerings in a specific term by course code.
     *
     * @param termId
     * @param courseCode
     * @param form
     * @throws Exception
     */
    public void loadCourseOfferingsByTermAndCourseCode(String termId, String courseCode,
            ScheduleOfClassesSearchForm form) throws Exception {

        Map additionalParams = new HashMap();
        additionalParams.put(CourseOfferingManagementSearchImpl.SearchParameters.COURSE_CODE, courseCode);

        buildCOResultsDisplay(form, termId, additionalParams);

    }

    /**
     * Loads course offerings from a term which matches the title or description
     *
     * @param termId
     * @param titleOrDescription
     * @param form
     * @throws Exception
     */
    public void loadCourseOfferingsByTitleAndDescription(String termId, String titleOrDescription,
            ScheduleOfClassesSearchForm form) throws Exception {

        Map additionalParams = new HashMap();
        additionalParams.put(CourseOfferingManagementSearchImpl.SearchParameters.DESCRIPTION, titleOrDescription);

        buildCOResultsDisplay(form, termId, additionalParams);

    }

    /**
     *
     * Loads course offerings from a term which has a specific instructor
     *
     * @param termId
     * @param instructorId
     * @param instructorName
     * @param form
     * @throws Exception
     */
    public void loadCourseOfferingsByTermAndInstructor(String termId, String instructorId, String instructorName,
            ScheduleOfClassesSearchForm form) throws Exception {

        // Search ID based on organizationName
        if (StringUtils.isBlank(instructorId)) {

            Map<String, String> searchCriteria = new HashMap<String, String>();
            searchCriteria.put(KIMPropertyConstants.Person.PRINCIPAL_NAME, instructorName);

            List<Person> instructors = ScheduleOfClassesUtil.getPersonService().findPeople(searchCriteria);

            //JIRA FIX : KSENROLL-8730 - Added NULL check
            int firstInstructor = 0;

            if (instructors == null || instructors.isEmpty()) {
                LOG.error("Error: Can't find any instructor for selected instructor in term: {}", termId);
                GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES,
                        RiceKeyConstants.ERROR_CUSTOM, "Invalid Principal Id/Name.");
                return;
            } else if (instructors.size() > 1) {
                LOG.error("Error: There is more than one instructor with the same name in term: {}", termId);
                GlobalVariables.getMessageMap().putError("Term & Instructor",
                        ScheduleOfClassesConstants.SOC_MSG_ERROR_MULTIPLE_INSTRUCTOR_IS_FOUND, instructorName);
                return;
            } else {
                instructorId = instructors.get(firstInstructor).getPrincipalId();
            }
        }

        if (StringUtils.isNotBlank(instructorId)) {

            ContextInfo context = ContextUtils.createDefaultContextInfo();

            //this is a cross service search between LPR and LUI, so it is inefficient (no join)
            //First get all the luiIds that the instructor is teaching
            //Only get active courses
            List<String> luiIds = CourseOfferingManagementUtil.getLprService().getLuiIdsByPersonAndTypeAndState(
                    instructorId, LprServiceConstants.INSTRUCTOR_MAIN_TYPE_KEY,
                    LprServiceConstants.ACTIVE_STATE_KEY, context);

            List<String> courseOfferingIds = null;

            if (!luiIds.isEmpty()) {
                //Now find all the COs with Aos that are attached to that instructor.
                // Build a query
                QueryByCriteria.Builder qbcBuilder = QueryByCriteria.Builder.create();
                qbcBuilder.setPredicates(
                        PredicateFactory.and(PredicateFactory.in("aoid", luiIds.toArray()),
                                PredicateFactory.equalIgnoreCase("atpId", termId)),
                        PredicateFactory.equal("luiState", LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY));
                QueryByCriteria criteria = qbcBuilder.build();
                courseOfferingIds = CourseOfferingManagementUtil.getCourseOfferingService()
                        .searchForCourseOfferingIds(criteria, context);
            }

            //If nothing was found then error
            if (courseOfferingIds == null || courseOfferingIds.isEmpty()) {
                LOG.error("Error: Can't find any Course Offering for selected Instructor in term: {}", termId);
                GlobalVariables.getMessageMap().putError("Term & Instructor",
                        ScheduleOfClassesConstants.SOC_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "instructor",
                        instructorId, termId);
                form.getCoDisplayWrapperList().clear();
                return;
            }

            Map additionalParams = new HashMap();
            additionalParams.put(CourseOfferingManagementSearchImpl.SearchParameters.CO_IDS, courseOfferingIds);

            buildCOResultsDisplay(form, termId, additionalParams);
        }

    }

    /**
     * Loads course offerings by term and department.
     *
     * @param termId
     * @param organizationId
     * @param organizationName
     * @param form
     * @throws Exception
     */
    public void loadCourseOfferingsByTermAndDepartment(String termId, String organizationId,
            String organizationName, ScheduleOfClassesSearchForm form) throws Exception {

        // Search ID based on organizationName
        if (StringUtils.isBlank(organizationId)) {

            QueryByCriteria.Builder qBuilder = QueryByCriteria.Builder.create();
            qBuilder.setPredicates(PredicateFactory.equalIgnoreCase("longName", organizationName));
            QueryByCriteria query = qBuilder.build();
            OrganizationService organizationService = ScheduleOfClassesUtil.getOrganizationService();

            List<String> orgIDs = organizationService.searchForOrgIds(query,
                    ContextUtils.createDefaultContextInfo());

            if (orgIDs.isEmpty()) {
                LOG.error("Error: Can't find any Department for selected Department in term: {}", termId);
                GlobalVariables.getMessageMap().putError("Term & Department",
                        ScheduleOfClassesConstants.SOC_MSG_ERROR_NO_COURSE_OFFERING_IS_FOUND, "department",
                        organizationName, termId);
                return;
            } else if (orgIDs.size() > 1) {
                LOG.error("Error: There is more than one departments with the same long name in term: {}", termId);
                GlobalVariables.getMessageMap().putError("Term & Department",
                        ScheduleOfClassesConstants.SOC_MSG_ERROR_MULTIPLE_DEPARTMENT_IS_FOUND, organizationName);
                return;
            } else {
                organizationId = KSCollectionUtils.getRequiredZeroElement(orgIDs);
            }
        }

        Map additionalParams = new HashMap();
        additionalParams.put(CourseOfferingManagementSearchImpl.SearchParameters.DEPARTMENT_ID, organizationId);

        buildCOResultsDisplay(form, termId, additionalParams);

    }

    /**
     * Loads all the Course offerings from a term which matches the additional search parameters.
     *
     * @param form
     * @param termId
     * @param searchParameters
     * @throws Exception
     */
    protected void buildCOResultsDisplay(ScheduleOfClassesSearchForm form, String termId,
            Map<String, Object> searchParameters) throws Exception {

        form.getCoDisplayWrapperList().clear();

        SearchRequestInfo searchRequest = new SearchRequestInfo(
                CourseOfferingManagementSearchImpl.CO_MANAGEMENT_SEARCH.getKey());

        for (Map.Entry<String, Object> entry : searchParameters.entrySet()) {
            if (entry.getValue() instanceof String) {
                searchRequest.addParam(entry.getKey(), (String) entry.getValue());
            } else if (entry.getValue() instanceof List) {
                searchRequest.addParam(entry.getKey(), (List) entry.getValue());
            } else {
                throw new RuntimeException("Invalid Search Parameter type. Only String and List are allowed.");
            }
        }

        List<String> filterCOStates = new ArrayList<String>(1);
        filterCOStates.add(LuiServiceConstants.LUI_CO_STATE_OFFERED_KEY);

        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.FILTER_CO_STATES,
                filterCOStates);
        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.ATP_ID, termId);
        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.CROSS_LIST_SEARCH_ENABLED,
                BooleanUtils.toStringTrueFalse(true));
        searchRequest.addParam(CourseOfferingManagementSearchImpl.SearchParameters.IS_EXACT_MATCH_CO_CODE_SEARCH,
                BooleanUtils.toStringTrueFalse(false));
        searchRequest.addParam(
                CourseOfferingManagementSearchImpl.SearchParameters.INCLUDE_PASSFAIL_AUDIT_HONORS_RESULTS,
                BooleanUtils.toStringTrueFalse(true));

        loadCourseOfferings(searchRequest, form);

        if (form.getCoDisplayWrapperList() == null || form.getCoDisplayWrapperList().isEmpty()) {
            LOG.error("Error: Can't find any Course Offering for the selected search");
            GlobalVariables.getMessageMap().putError(KRADConstants.GLOBAL_MESSAGES, RiceKeyConstants.ERROR_CUSTOM,
                    "Can't find any Course Offering for the selected search");
            return;
        }

        for (CourseOfferingDisplayWrapper coDisplayWrapper : form.getCoDisplayWrapperList()) {

            // Adding Information (icons)
            StringBuilder information = new StringBuilder();
            if (coDisplayWrapper.isHonorsCourse()) {
                information.append(
                        makeInfoIconWithTooltip(ScheduleOfClassesConstants.SOC_RESULT_PAGE_HONORS_COURSE_IMG,
                                ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_HONORS_COURSE));
            }

            if (StringUtils.equals(coDisplayWrapper.getCourseOfferingGradingOptionKey(),
                    LrcServiceConstants.RESULT_GROUP_KEY_GRADE_SATISFACTORY)) {
                information.append(
                        makeInfoIconWithTooltip(ScheduleOfClassesConstants.SOC_RESULT_PAGE_GRADING_SATISFACTORY_IMG,
                                ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_GRADING_SATISFACTORY));
            } else if (StringUtils.equals(coDisplayWrapper.getCourseOfferingGradingOptionKey(),
                    LrcServiceConstants.RESULT_GROUP_KEY_GRADE_PERCENTAGE)) {
                information.append(
                        makeInfoIconWithTooltip(ScheduleOfClassesConstants.SOC_RESULT_PAGE_GRADING_PERCENT_IMG,
                                ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_GRADING_PERCENT));
            }

            if (coDisplayWrapper.isAuditCourse()) {
                information
                        .append(makeInfoIconWithTooltip(ScheduleOfClassesConstants.SOC_RESULT_PAGE_STUREG_AUDIT_IMG,
                                ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_STUREG_AUDIT));
            }

            if (coDisplayWrapper.isStudentSelectablePassFail()) {
                information.append(
                        makeInfoIconWithTooltip(ScheduleOfClassesConstants.SOC_RESULT_PAGE_STUREG_PASSFAIL_IMG,
                                ScheduleOfClassesConstants.SOC_RESULT_PAGE_HELP_STUREG_PASSFAIL));
            }

            coDisplayWrapper.setInformation(information.toString());

        }
    }

    /**
     * Creates an HTML img tag and the two hidden input fields to necessary to create a tooltip.
     * @param src The URL of the image to be used in the icon.
     * @param text The text to display in the tooltip.
     * @return An additional information icon with a tooltip attached.
     */
    private String makeInfoIconWithTooltip(String src, String text) {
        String id = UUIDHelper.genStringUUID();
        String tt = IMG + TOOLTIP_CREATE_SCRIPT + TOOLTIP_ADD_ATTRIBUTE;
        tt = tt.replace("$APPLICATION_URL", ConfigContext.getCurrentContextConfig()
                .getProperty(KRADConstants.ConfigParameters.APPLICATION_URL));
        tt = tt.replace("$ID", id);
        tt = tt.replace("$SRC", src);
        tt = tt.replace("$TEXT", text);
        return tt;
    }

    /**
     * Method to build the course offering requisites string for display
     *
     * @param courseOfferingId
     * @return Map of course offering requisites
     */
    public SOCRequisiteWrapper retrieveRequisites(String courseOfferingId,
            List<ActivityOfferingWrapper> activityOfferingWrapperList) {

        //Retrieve reference object bindings for course offering
        List<ReferenceObjectBinding> coRefObjectsBindingList = ScheduleOfClassesUtil.getRuleManagementService()
                .findReferenceObjectBindingsByReferenceObject(
                        CourseOfferingServiceConstants.REF_OBJECT_URI_COURSE_OFFERING, courseOfferingId);

        //Setup the requisites wrapper object.
        SOCRequisiteWrapper reqWrapper = new SOCRequisiteWrapper();
        Set<String> agendaTypes = new HashSet<String>();
        List<String> ruleIds = new ArrayList<String>();

        //Retrieve agenda's for course offering
        List<RuleDefinition> rules = new ArrayList<RuleDefinition>();
        for (ReferenceObjectBinding coReferenceObjectBinding : coRefObjectsBindingList) {
            AgendaDefinition agendaDefinition = ScheduleOfClassesUtil.getRuleManagementService()
                    .getAgenda(coReferenceObjectBinding.getKrmsObjectId());
            AgendaItemDefinition agendaItem = ScheduleOfClassesUtil.getRuleManagementService()
                    .getAgendaItem(agendaDefinition.getFirstItemId());
            agendaTypes.add(agendaDefinition.getTypeId());
            loadRules(rules, ruleIds, agendaItem);
        }
        reqWrapper.setCoRules(rules);

        //Setup a list of activity offerin ids
        List<String> aoIds = new ArrayList<String>();
        for (ActivityOfferingWrapper activityOfferingWrapper : activityOfferingWrapperList) {
            aoIds.add(activityOfferingWrapper.getAoInfo().getId());
        }

        //Retrieve reference object bindings for activity offering's
        Map<String, List<RuleDefinition>> aoToRulesMap = new HashMap<String, List<RuleDefinition>>();
        List<ReferenceObjectBinding> aoRefObjectBindingList = ScheduleOfClassesUtil.getRuleManagementService()
                .findReferenceObjectBindingsByReferenceObjectIds(
                        CourseOfferingServiceConstants.REF_OBJECT_URI_ACTIVITY_OFFERING, aoIds);
        for (ReferenceObjectBinding refObjectBinding : aoRefObjectBindingList) {
            AgendaDefinition agendaDefinition = ScheduleOfClassesUtil.getRuleManagementService()
                    .getAgenda(refObjectBinding.getKrmsObjectId());
            AgendaItemDefinition agendaItemDefinition = ScheduleOfClassesUtil.getRuleManagementService()
                    .getAgendaItem(agendaDefinition.getFirstItemId());
            agendaTypes.add(agendaDefinition.getTypeId());
            loadRulesIntoMap(refObjectBinding.getReferenceObjectId(), aoToRulesMap, ruleIds, agendaItemDefinition);
        }
        reqWrapper.setAoToRulesMap(aoToRulesMap);

        //Retrieve the natural language statements.
        String catalogUsageId = ScheduleOfClassesUtil.getRuleManagementService()
                .getNaturalLanguageUsageByNameAndNamespace(KSKRMSServiceConstants.KRMS_NL_TYPE_CATALOG,
                        PermissionServiceConstants.KS_SYS_NAMESPACE)
                .getId();
        List<NaturalLanguage> nlList = ScheduleOfClassesUtil.getRuleManagementService()
                .translateNaturalLanguageForObjects(catalogUsageId, "rule", ruleIds, "en");

        Map<String, String> nlMap = new HashMap<String, String>();
        for (NaturalLanguage entry : nlList) {
            nlMap.put(entry.getKrmsObjectId(), entry.getNaturalLanguage());
        }
        reqWrapper.setNlMap(nlMap);

        //Setup the rule type list.
        List<TypeTypeRelation> sortedRuleRelations = new ArrayList<TypeTypeRelation>();
        for (String type : agendaTypes) {
            sortedRuleRelations.addAll(
                    ScheduleOfClassesUtil.getKrmsTypeRepositoryService().findTypeTypeRelationsByFromType(type));
        }
        Collections.sort(sortedRuleRelations, new Comparator<TypeTypeRelation>() {
            @Override
            public int compare(TypeTypeRelation typeTypeRelation1, TypeTypeRelation typeTypeRelation2) {
                return typeTypeRelation1.getSequenceNumber().compareTo(typeTypeRelation2.getSequenceNumber());
            }
        });

        for (TypeTypeRelation ruleRelation : sortedRuleRelations) {
            reqWrapper.getRuleTypes().add(ruleRelation.getToTypeId());
        }

        ScheduleOfClassesUtil.loadRequisites(reqWrapper, aoIds);

        return reqWrapper;
    }

    protected void loadRules(List<RuleDefinition> rules, List<String> ruleIds, AgendaItemDefinition agendaItem) {

        if (agendaItem.getRuleId() != null) {

            ruleIds.add(agendaItem.getRuleId());
            rules.add(agendaItem.getRule());
        }

        if (agendaItem.getWhenTrue() != null) {
            this.loadRules(rules, ruleIds, agendaItem.getWhenTrue());
        }
    }

    protected void loadRulesIntoMap(String key, Map<String, List<RuleDefinition>> refObjectToRules,
            List<String> ruleIds, AgendaItemDefinition agendaItem) {

        if (agendaItem.getRuleId() != null) {

            ruleIds.add(agendaItem.getRuleId());

            if (refObjectToRules.containsKey(key)) {
                refObjectToRules.get(key).add(agendaItem.getRule());
            } else {
                List<RuleDefinition> rules = new ArrayList<RuleDefinition>();
                rules.add(agendaItem.getRule());
                refObjectToRules.put(key, rules);
            }
        }

        if (agendaItem.getWhenTrue() != null) {
            this.loadRulesIntoMap(key, refObjectToRules, ruleIds, agendaItem.getWhenTrue());
        }
    }

    public String getTermStartEndDate(TermInfo term) {
        // Return Term as String display like 'FALL 2020 (9/26/2020-12/26/2020)'
        StringBuilder stringBuilder = new StringBuilder();
        Formatter formatter = new Formatter(stringBuilder, Locale.US);
        String displayString = "";
        if (term != null) {
            String startDate = DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(term.getStartDate());
            String endDate = DateFormatters.MONTH_DAY_YEAR_DATE_FORMATTER.format(term.getEndDate());
            formatter.format("%s - %s", startDate, endDate);
            displayString = stringBuilder.toString();
        }
        return displayString;
    }

    /**
     * Comparators to be executed on the AOs
     *
     * @param activityComparatorChain
     */
    public void setActivityComparatorChain(KSComparatorChain activityComparatorChain) {
        this.activityComparatorChain = activityComparatorChain;
    }

    /**
     * Returns the Comparator chain associated with the Acivity sorting
     *
     * @return activityComparatorChain
     */
    public KSComparatorChain getActivityComparatorChain() {
        return activityComparatorChain;
    }

    /**
     * Sorts regGroups by the comparators in the chain.
     *
     * @param regGroupWrappers
     */
    public void sortRegGroups(List<RegistrationGroupWrapper> regGroupWrappers) {
        if (regGroupComparatorChain != null) {
            regGroupComparatorChain.sort(regGroupWrappers);
        }
    }

    /**
     * Comparators to be executed on the AOs
     *
     * @param regGroupComparatorChain
     */
    public void setRegGroupComparatorChain(KSComparatorChain regGroupComparatorChain) {
        this.regGroupComparatorChain = regGroupComparatorChain;
    }

    /**
     * Sorts AOs by the comparators in the chain.
     *
     * @param form
     * @param coWrapper
     */
    public void sortActivityOfferings(ScheduleOfClassesSearchForm form, CourseOfferingDisplayWrapper coWrapper) {
        if (form.getAoDisplayFormat() == ScheduleOfClassesSearchForm.AoDisplayFormat.FLAT) {
            KSComparatorChain defaultComparator = new KSComparatorChain();
            List<KSComparator> comparators = new ArrayList<KSComparator>(2);
            comparators.add(new ActivityOfferingTypeComparator());
            comparators.add(new ActivityOfferingCodeComparator());
            defaultComparator.setComparators(comparators);
            defaultComparator.sort(coWrapper.getActivityWrapperList());
        } else if (form.getAoDisplayFormat() == ScheduleOfClassesSearchForm.AoDisplayFormat.CLUSTER) {
            /**
             * Sort the AOs first by the type and then by institutionally configured list of comparators
             */
            for (ActivityOfferingClusterWrapper clusterWrapper : form.getClusterResultList()) {
                if (clusterWrapper.getAoWrapperList().size() > 1) {
                    //Add the type sorting as first.
                    getActivityComparatorChain().addComparator(0, new ActivityOfferingTypeComparator());
                    activityComparatorChain.sort(clusterWrapper.getAoWrapperList());
                }
            }
        }
    }

    /**
     * This method returns the institutionally configured AO states to filter at the ui. If it's not
     * configured, by default, it returns offerred state.
     *
     * @return
     */
    public List<String> getAOStateFilter() {

        String allowedAOStates = ConfigContext.getCurrentContextConfig()
                .getProperty(CourseOfferingConstants.CONFIG_PARAM_KEY_SCHOC_AO_STATES);
        List<String> aoStates;

        if ((allowedAOStates != null) && (!allowedAOStates.isEmpty())) {

            aoStates = Arrays.asList(allowedAOStates.split("\\s*,\\s*"));

            if (!Arrays.asList(LuiServiceConstants.ACTIVITY_OFFERING_LIFECYCLE_STATE_KEYS).containsAll(aoStates)) {
                String errorMessage = String.format(
                        "Error: invalid value for configuration parameter:  %s Value: %s",
                        CourseOfferingConstants.CONFIG_PARAM_KEY_SCHOC_AO_STATES, aoStates.toString());
                throw new RuntimeException(errorMessage);
            }

        } else {
            // If an institution does not customize valid AO states, then the default is AO Offered state
            aoStates = new ArrayList<String>();
            aoStates.add(LuiServiceConstants.LUI_AO_STATE_OFFERED_KEY);
        }

        return aoStates;
    }

    /**
     * This method returns the institutionally configured reg group states to filter at the ui. If it's not
     * configured, by default, it returns offerred, invalid and offered-invalid states.
     *
     * @return
     */
    public List<String> getRegGroupStateFilter() {

        String allowedRegGroupStates = ConfigContext.getCurrentContextConfig()
                .getProperty(CourseOfferingConstants.CONFIG_PARAM_KEY_SCHOC_REG_GROUP_STATES);
        List<String> regGroupStates;

        if ((allowedRegGroupStates != null) && (!allowedRegGroupStates.isEmpty())) {
            regGroupStates = Arrays.asList(allowedRegGroupStates.split("\\s*,\\s*"));
            if (!Arrays.asList(LuiServiceConstants.REGISTRATION_GROUP_LIFECYCLE_KEY_STATES)
                    .containsAll(regGroupStates)) {
                String errorMessage = String.format(
                        "Error: invalid value for configuration parameter:  %s Value: %s",
                        CourseOfferingConstants.CONFIG_PARAM_KEY_SCHOC_REG_GROUP_STATES, regGroupStates.toString());
                throw new RuntimeException(errorMessage);
            }
        } else {
            /*
            If an institution does not customize valid RegGroup states, then the default is RegGroup Offered+Invalid+Offered-invalid state.
            Offered-invalid not yet available. So, we ignored for now but eventually that will be added.
             */
            regGroupStates = new ArrayList<String>(2);
            regGroupStates.add(LuiServiceConstants.REGISTRATION_GROUP_OFFERED_STATE_KEY);
            regGroupStates.add(LuiServiceConstants.REGISTRATION_GROUP_INVALID_STATE_KEY);
        }

        return regGroupStates;
    }
}