Package org.fenixedu.academic.domain.curricularRules.executors.ruleExecutors

Source Code of org.fenixedu.academic.domain.curricularRules.executors.ruleExecutors.PreviousYearsEnrolmentExecutor$CollectContext

/**
* Copyright © 2002 Instituto Superior Técnico
*
* This file is part of FenixEdu Academic.
*
* FenixEdu Academic is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* FenixEdu Academic is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with FenixEdu Academic.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.fenixedu.academic.domain.curricularRules.executors.ruleExecutors;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.fenixedu.academic.domain.CurricularCourse;
import org.fenixedu.academic.domain.ExecutionSemester;
import org.fenixedu.academic.domain.curricularPeriod.CurricularPeriod;
import org.fenixedu.academic.domain.curricularRules.CreditsLimit;
import org.fenixedu.academic.domain.curricularRules.DegreeModulesSelectionLimit;
import org.fenixedu.academic.domain.curricularRules.Exclusiveness;
import org.fenixedu.academic.domain.curricularRules.ICurricularRule;
import org.fenixedu.academic.domain.curricularRules.PreviousYearsEnrolmentCurricularRule;
import org.fenixedu.academic.domain.curricularRules.executors.RuleResult;
import org.fenixedu.academic.domain.curricularRules.executors.verifyExecutors.VerifyRuleLevel;
import org.fenixedu.academic.domain.degreeStructure.Context;
import org.fenixedu.academic.domain.degreeStructure.CourseGroup;
import org.fenixedu.academic.domain.degreeStructure.CycleType;
import org.fenixedu.academic.domain.degreeStructure.DegreeModule;
import org.fenixedu.academic.domain.enrolment.EnrolmentContext;
import org.fenixedu.academic.domain.enrolment.IDegreeModuleToEvaluate;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.studentCurriculum.CurriculumGroup;
import org.fenixedu.academic.domain.studentCurriculum.CycleCurriculumGroup;

public class PreviousYearsEnrolmentExecutor extends CurricularRuleExecutor {

    private static class CollectContext {

        public double ectsCredits;

        public CollectContext parentContext;

        public CollectContext() {
            this(null);
        }

        public CollectContext(final CollectContext parentContext) {
            this.parentContext = parentContext;

        }

        public boolean hasCreditsToSpent(final double ectsCredits) {
            if (this.ectsCredits >= ectsCredits) {
                return true;
            }

            if (this.parentContext == null) {
                return false;
            }

            return this.parentContext.hasCreditsToSpent(ectsCredits - this.ectsCredits);

        }

        public void useCredits(final double ectsCredits) {
            if (this.ectsCredits >= ectsCredits) {
                this.ectsCredits = this.ectsCredits - ectsCredits;
            } else {
                double creditsMissing = ectsCredits - this.ectsCredits;
                if (this.parentContext == null) {
                    throw new DomainException(
                            "error.net.sourceforge.fenixedu.domain.curricularRules.ruleExecutors.CollectContext.parentContent.is.expected");
                }

                this.parentContext.useCredits(creditsMissing);
            }

        }

    }

    @Override
    protected RuleResult executeEnrolmentVerificationWithRules(final ICurricularRule curricularRule,
            IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final EnrolmentContext enrolmentContext) {

        if (isEnrollingInCourseGroupsOnly(enrolmentContext, sourceDegreeModuleToEvaluate)) {
            return RuleResult.createNA(sourceDegreeModuleToEvaluate.getDegreeModule());
        }

        final PreviousYearsEnrolmentCurricularRule previousYearsEnrolmentCurricularRule =
                (PreviousYearsEnrolmentCurricularRule) curricularRule;
        final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear =
                getCurricularCoursesToEnrolByYear(previousYearsEnrolmentCurricularRule, enrolmentContext,
                        sourceDegreeModuleToEvaluate, false);

        // printCurricularCoursesToEnrol(curricularCoursesToEnrolByYear);

        return hasAnyCurricularCoursesToEnrolInPreviousYears(enrolmentContext, curricularCoursesToEnrolByYear,
                sourceDegreeModuleToEvaluate);

    }

    private void printCurricularCoursesToEnrol(Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear) {
        for (final Entry<Integer, Set<CurricularCourse>> entry : curricularCoursesToEnrolByYear.entrySet()) {
            System.out.println("Year " + entry.getKey());
            for (final CurricularCourse curricularCourse : entry.getValue()) {
                System.out.println(curricularCourse.getName());
            }

            System.out.println("-------------");
        }

    }

    private boolean hasCurricularCoursesToEnrolInPreviousYears(
            Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear, Integer curricularYear) {
        for (int i = curricularYear; i > 0; i--) {
            final int previousYear = i - 1;

            if (!curricularCoursesToEnrolByYear.containsKey(previousYear)) {
                continue;
            }

            if (!curricularCoursesToEnrolByYear.get(previousYear).isEmpty()) {
                return true;
            }
        }

        return false;

    }

    private RuleResult hasAnyCurricularCoursesToEnrolInPreviousYears(final EnrolmentContext enrolmentContext,
            final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate) {

        RuleResult result = RuleResult.createTrue(sourceDegreeModuleToEvaluate.getDegreeModule());
        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext
                .getAllChildDegreeModulesToEvaluateFor(sourceDegreeModuleToEvaluate.getDegreeModule())) {
            if (degreeModuleToEvaluate.isLeaf()) {

                if (degreeModuleToEvaluate.isAnnualCurricularCourse(enrolmentContext.getExecutionPeriod().getExecutionYear())
                        && degreeModuleToEvaluate.getContext() == null) {
                    continue;
                }

                if (degreeModuleToEvaluate.getContext() == null) {
                    throw new DomainException("error.degreeModuleToEvaluate.has.invalid.context",
                            degreeModuleToEvaluate.getName(), degreeModuleToEvaluate.getExecutionPeriod().getQualifiedName());
                }
                if (hasCurricularCoursesToEnrolInPreviousYears(curricularCoursesToEnrolByYear, degreeModuleToEvaluate
                        .getContext().getCurricularYear())) {

                    if (degreeModuleToEvaluate.isEnroled()) {
                        result = result.and(createImpossibleRuleResult(sourceDegreeModuleToEvaluate, degreeModuleToEvaluate));
                    } else {
                        result = result.and(createFalseRuleResult(sourceDegreeModuleToEvaluate, degreeModuleToEvaluate));
                    }
                }
            }
        }

        return result;
    }

    private RuleResult createFalseRuleResult(final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate,
            final IDegreeModuleToEvaluate degreeModuleToEvaluate) {
        return RuleResult.createFalse(degreeModuleToEvaluate.getDegreeModule(),
                "curricularRules.ruleExecutors.PreviousYearsEnrolmentExecutor", sourceDegreeModuleToEvaluate.getName(),
                degreeModuleToEvaluate.getContext().getCurricularYear().toString());
    }

    private RuleResult createImpossibleRuleResult(final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate,
            final IDegreeModuleToEvaluate degreeModuleToEvaluate) {
        return RuleResult.createImpossible(degreeModuleToEvaluate.getDegreeModule(),
                "curricularRules.ruleExecutors.PreviousYearsEnrolmentExecutor", sourceDegreeModuleToEvaluate.getName(),
                degreeModuleToEvaluate.getContext().getCurricularYear().toString());
    }

    private boolean isEnrollingInCourseGroupsOnly(final EnrolmentContext enrolmentContext,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate) {
        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext
                .getAllChildDegreeModulesToEvaluateFor(sourceDegreeModuleToEvaluate.getDegreeModule())) {
            if (degreeModuleToEvaluate.isLeaf()) {
                return false;
            }
        }

        return true;
    }

    private Map<Integer, Set<CurricularCourse>> getCurricularCoursesToEnrolByYear(
            final PreviousYearsEnrolmentCurricularRule previousYearsEnrolmentCurricularRule,
            final EnrolmentContext enrolmentContext, final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate,
            final boolean withTemporaryEnrolments) {
        final Map<Integer, Set<CurricularCourse>> result = new HashMap<Integer, Set<CurricularCourse>>();

        for (CourseGroup courseGroup : getCourseGroupsToEvaluate(
                previousYearsEnrolmentCurricularRule.getDegreeModuleToApplyRule(), enrolmentContext)) {
            collectCourseGroupCurricularCoursesToEnrol(result, courseGroup, new CollectContext(), enrolmentContext,
                    sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
        }

        return result;
    }

    private Collection<CourseGroup> getCourseGroupsToEvaluate(final CourseGroup courseGroup,
            final EnrolmentContext enrolmentContext) {
        if (courseGroup.isRoot()) {
            final Collection<CourseGroup> res = new HashSet<CourseGroup>();
            for (final CycleType cycleType : enrolmentContext.getStudentCurricularPlan().getDegreeType().getCycleTypes()) {
                CycleCurriculumGroup cycleCurriculumGroup =
                        enrolmentContext.getStudentCurricularPlan().getRoot().getCycleCurriculumGroup(cycleType);
                if (cycleCurriculumGroup != null) {
                    if (cycleCurriculumGroup.isExternal()) {
                        throw new DomainException("error.cycleCurriculumGroup.cannot.be.external");
                    }

                    res.add(cycleCurriculumGroup.getDegreeModule());
                }
            }
            return res;
        } else {
            return Collections.singleton(courseGroup);
        }
    }

    private void collectCourseGroupCurricularCoursesToEnrol(final Map<Integer, Set<CurricularCourse>> result,
            final CourseGroup courseGroup, final CollectContext collectContext, final EnrolmentContext enrolmentContext,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, boolean withTemporaryEnrolments) {

        if (!isToCollectCurricularCourses(courseGroup, enrolmentContext, sourceDegreeModuleToEvaluate, withTemporaryEnrolments)) {
            return;
        }

        final int childDegreeModulesCount =
                courseGroup.getActiveChildContextsWithMax(enrolmentContext.getExecutionPeriod()).size();

        collectCurricularCoursesToEnrol(result, courseGroup, collectContext, enrolmentContext, sourceDegreeModuleToEvaluate,
                withTemporaryEnrolments);

        final int minModules = getMinModules(courseGroup, enrolmentContext.getExecutionPeriod());
        final int maxModules = getMaxModules(courseGroup, enrolmentContext.getExecutionPeriod());
        if (minModules == maxModules) {
            if (maxModules == childDegreeModulesCount) {
                // N-N == Nchilds
                collectChildCourseGroupsCurricularCoursesToEnrol(result, courseGroup, collectContext, enrolmentContext,
                        sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
            } else {
                // N-N <> Nchilds
                if (getSelectedChildDegreeModules(courseGroup, enrolmentContext).size() < minModules) {
                    collectChildCourseGroupsCurricularCoursesToEnrol(result, courseGroup, collectContext, enrolmentContext,
                            sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
                } else {
                    collectSelectedChildCourseGroupsCurricularCoursesToEnrol(result, courseGroup, collectContext,
                            enrolmentContext, sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
                }
            }
        } else {
            // N-M
            if (getSelectedChildDegreeModules(courseGroup, enrolmentContext).size() < minModules) {
                collectChildCourseGroupsCurricularCoursesToEnrol(result, courseGroup, collectContext, enrolmentContext,
                        sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
            } else {
                collectSelectedChildCourseGroupsCurricularCoursesToEnrol(result, courseGroup, collectContext, enrolmentContext,
                        sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
            }
        }

    }

    private boolean isToCollectCurricularCourses(CourseGroup courseGroup, EnrolmentContext enrolmentContext,
            IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, boolean withTemporaryEnrolments) {
        return !isConcluded(courseGroup, enrolmentContext, sourceDegreeModuleToEvaluate, withTemporaryEnrolments)
                && !isExclusiveWithExisting(courseGroup, enrolmentContext);
    }

    private boolean isExclusiveWithExisting(CourseGroup courseGroup, EnrolmentContext enrolmentContext) {
        for (final Exclusiveness exclusiveness : courseGroup.getExclusivenessRules(enrolmentContext.getExecutionPeriod())) {
            if (isEnroled(enrolmentContext, exclusiveness.getExclusiveDegreeModule())) {
                return true;
            }
        }

        return false;
    }

    private boolean isConcluded(final CourseGroup courseGroup, final EnrolmentContext enrolmentContext,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final boolean withTemporaryEnrolments) {
        final CurriculumGroup curriculumGroup = enrolmentContext.getStudentCurricularPlan().findCurriculumGroupFor(courseGroup);

        if (curriculumGroup == null) {
            return false;
        }

        final double minEctsToApprove = curriculumGroup.getDegreeModule().getMinEctsCredits();
        final double totalEcts = calculateTotalEctsInGroup(enrolmentContext, curriculumGroup, withTemporaryEnrolments);

        return totalEcts >= minEctsToApprove;
    }

    private void collectSelectedChildCourseGroupsCurricularCoursesToEnrol(Map<Integer, Set<CurricularCourse>> result,
            CourseGroup courseGroup, CollectContext collectContext, EnrolmentContext enrolmentContext,
            IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, boolean withTemporaryEnrolments) {

        for (final DegreeModule degreeModule : getSelectedChildDegreeModules(courseGroup, enrolmentContext)) {
            if (degreeModule.isCourseGroup()) {
                collectCourseGroupCurricularCoursesToEnrol(result, (CourseGroup) degreeModule,
                        new CollectContext(collectContext), enrolmentContext, sourceDegreeModuleToEvaluate,
                        withTemporaryEnrolments);
            }
        }

    }

    private Set<DegreeModule> getSelectedChildDegreeModules(final CourseGroup courseGroup, final EnrolmentContext enrolmentContext) {
        final Set<DegreeModule> result = new HashSet<DegreeModule>();

        for (final DegreeModule degreeModule : courseGroup.getChildDegreeModulesValidOn(enrolmentContext.getExecutionPeriod())) {
            if (enrolmentContext.getStudentCurricularPlan().hasDegreeModule(degreeModule)) {
                result.add(degreeModule);
            }
        }

        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext.getDegreeModulesToEvaluate()) {
            if (degreeModuleToEvaluate.getCurriculumGroup() != null
                    && degreeModuleToEvaluate.getCurriculumGroup().getDegreeModule() == courseGroup) {
                result.add(degreeModuleToEvaluate.getDegreeModule());
            }
        }

        return result;

    }

    private int getMinModules(final CourseGroup courseGroup, final ExecutionSemester executionSemester) {
        final DegreeModulesSelectionLimit degreeModulesSelectionLimit =
                courseGroup.getDegreeModulesSelectionLimitRule(executionSemester);
        if (degreeModulesSelectionLimit != null) {
            return degreeModulesSelectionLimit.getMinimumLimit();
        }

        final CreditsLimit creditsLimit = courseGroup.getCreditsLimitRule(executionSemester);
        if (creditsLimit != null) {
            final SortedSet<DegreeModule> sortedChilds =
                    new TreeSet<DegreeModule>(new DegreeModule.ComparatorByMinEcts(executionSemester));
            sortedChilds.addAll(courseGroup.getChildDegreeModulesValidOn(executionSemester));
            int counter = 0;
            double total = 0d;
            for (final DegreeModule degreeModule : sortedChilds) {
                total += degreeModule.getMinEctsCredits();
                if (total > creditsLimit.getMinimumCredits()) {
                    break;
                } else {
                    counter++;
                }
            }

            return counter;
        }

        return courseGroup.getChildDegreeModulesValidOn(executionSemester).size();
    }

    private int getMaxModules(final CourseGroup courseGroup, final ExecutionSemester executionSemester) {
        final DegreeModulesSelectionLimit degreeModulesSelectionLimit =
                courseGroup.getDegreeModulesSelectionLimitRule(executionSemester);
        if (degreeModulesSelectionLimit != null) {
            return degreeModulesSelectionLimit.getMaximumLimit();
        }

        final CreditsLimit creditsLimit = courseGroup.getCreditsLimitRule(executionSemester);
        if (creditsLimit != null) {
            final SortedSet<DegreeModule> sortedChilds =
                    new TreeSet<DegreeModule>(new DegreeModule.ComparatorByMinEcts(executionSemester));
            sortedChilds.addAll(courseGroup.getChildDegreeModulesValidOn(executionSemester));
            int counter = 0;
            double total = 0d;
            for (final DegreeModule degreeModule : sortedChilds) {
                total += degreeModule.getMaxEctsCredits();
                if (total > creditsLimit.getMaximumCredits()) {
                    break;
                } else {
                    counter++;
                }
            }

            return counter;

        }

        return courseGroup.getChildDegreeModulesValidOn(executionSemester).size();
    }

    private void collectCurricularCoursesToEnrol(final Map<Integer, Set<CurricularCourse>> result, final CourseGroup courseGroup,
            final CollectContext collectContext, final EnrolmentContext enrolmentContext,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final boolean withTemporaryEnrolments) {

        final CurriculumGroup curriculumGroup = enrolmentContext.getStudentCurricularPlan().findCurriculumGroupFor(courseGroup);
        collectContext.ectsCredits = curriculumGroup != null ? curriculumGroup.getChildCreditsDismissalEcts() : 0;

        final double missingEctsToConcludeGroup;
        if (curriculumGroup != null) {
            missingEctsToConcludeGroup =
                    curriculumGroup.getDegreeModule().getMinEctsCredits()
                            - calculateTotalEctsInGroup(enrolmentContext, curriculumGroup, withTemporaryEnrolments)
                            - calculateEnrollingEctsCreditsInCurricularCoursesFor(enrolmentContext, courseGroup);
        } else {
            missingEctsToConcludeGroup = courseGroup.getMinEctsCredits();
        }

        // we need curricular courses in all periods, otherwise enrolment with
        // exclusiviness in next semester will
        // not be enforced
        // e.g. B required A. If boot are in alternative semesters, that means
        // we should force enrolment in A in this
        // semester, otherwise the group will not close in next semester
        final Map<CurricularPeriod, Set<Context>> childContextsWithMaxByCurricularPeriod =
                courseGroup.getActiveChildCurricularContextsWithMaxByCurricularPeriod(enrolmentContext.getExecutionPeriod());

        final SortedSet<Context> sortedCurricularCoursesContexts =
                getChildCurricularCoursesContextsToEvaluate(courseGroup, enrolmentContext);

        removeApprovedOrEnrolledOrEnrollingOrNotSatifyingCurricularRules(sortedCurricularCoursesContexts,
                childContextsWithMaxByCurricularPeriod, enrolmentContext, sourceDegreeModuleToEvaluate, withTemporaryEnrolments);

        removeCurricularCoursesThatCanBeApprovedInOtherCurricularPeriod(missingEctsToConcludeGroup,
                childContextsWithMaxByCurricularPeriod, sortedCurricularCoursesContexts, enrolmentContext);

        addValidCurricularCourses(result, sortedCurricularCoursesContexts, courseGroup, enrolmentContext.getExecutionPeriod());
    }

    private void removeCurricularCoursesThatCanBeApprovedInOtherCurricularPeriodOLD(final double missingEctsToConcludeGroup,
            final Map<CurricularPeriod, Set<Context>> childContextsWithMaxByCurricularPeriod,
            final SortedSet<Context> sortedCurricularCoursesContexts, final EnrolmentContext enrolmentContext) {
        final Iterator<Context> iterator = sortedCurricularCoursesContexts.iterator();
        while (iterator.hasNext()) {
            final Context context = iterator.next();

            if (canObtainApprovalInOtherCurricularPeriod(missingEctsToConcludeGroup, context,
                    childContextsWithMaxByCurricularPeriod)) {
                iterator.remove();
            }

        }
    }

    private void removeCurricularCoursesThatCanBeApprovedInOtherCurricularPeriod(final double missingEctsToConcludeGroup,
            final Map<CurricularPeriod, Set<Context>> childContextsByCurricularPeriod,
            final SortedSet<Context> sortedCurricularCoursesContexts, final EnrolmentContext enrolmentContext) {

        for (final Entry<CurricularPeriod, Set<Context>> each : childContextsByCurricularPeriod.entrySet()) {
            for (final Context context : each.getValue()) {
                if (canObtainApprovalInOtherCurricularPeriod(missingEctsToConcludeGroup, context, childContextsByCurricularPeriod)) {
                    sortedCurricularCoursesContexts.remove(context);
                }
            }
        }

    }

    private void removeApprovedOrEnrolledOrEnrollingOrNotSatifyingCurricularRules(
            final SortedSet<Context> sortedCurricularCoursesContexts,
            final Map<CurricularPeriod, Set<Context>> childContextsWithMaxByCurricularPeriod,
            final EnrolmentContext enrolmentContext, final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate,
            final boolean withTemporaryEnrolments) {
        final Iterator<Context> iterator = sortedCurricularCoursesContexts.iterator();
        while (iterator.hasNext()) {
            final Context context = iterator.next();
            final CurricularCourse curricularCourse = (CurricularCourse) context.getChildDegreeModule();
            if (isApproved(enrolmentContext, curricularCourse)) {
                iterator.remove();
                removeFromChildContextsByCurricularPeriod(childContextsWithMaxByCurricularPeriod, context);

            } else if (isEnroled(enrolmentContext, curricularCourse, withTemporaryEnrolments)
                    || isEnrolling(enrolmentContext, curricularCourse)) {
                iterator.remove();
                removeFromChildContextsByCurricularPeriod(childContextsWithMaxByCurricularPeriod, context);
            } else if (!isCurricularRulesSatisfied(enrolmentContext, curricularCourse, sourceDegreeModuleToEvaluate)) {
                iterator.remove();
                removeFromChildContextsByCurricularPeriod(childContextsWithMaxByCurricularPeriod, context);

            }
            // } else {
            // final double creditsRequiredToApprove =
            // curricularCourse.isOptionalCurricularCourse() ?
            // ((OptionalCurricularCourse) curricularCourse)
            // .getMinEctsCredits(enrolmentContext.getExecutionPeriod())
            // :
            // curricularCourse.getEctsCredits(enrolmentContext.getExecutionPeriod
            // ());
            // if
            // (collectContext.hasCreditsToSpent(creditsRequiredToApprove))
            // {
            // collectContext.useCredits(creditsRequiredToApprove);
            // iterator.remove();
            //
            // }
            // }

        }

    }

    private void removeFromChildContextsByCurricularPeriod(final Map<CurricularPeriod, Set<Context>> result,
            final Context contextToRemove) {
        final DegreeModule degreeModule = contextToRemove.getChildDegreeModule();

        for (final Entry<CurricularPeriod, Set<Context>> each : result.entrySet()) {
            final Iterator<Context> iterator = each.getValue().iterator();
            while (iterator.hasNext()) {
                Context candidate = iterator.next();
                if (candidate.getChildDegreeModule() == degreeModule) {
                    iterator.remove();
                }
            }
        }

    }

    private double calculateEnrollingEctsCreditsInCurricularCoursesFor(final EnrolmentContext enrolmentContext,
            final CourseGroup courseGroup) {
        double result = 0;

        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext.getDegreeModulesToEvaluate()) {
            if (degreeModuleToEvaluate.isLeaf() && degreeModuleToEvaluate.isEnroling()
                    && degreeModuleToEvaluate.getCurriculumGroup().getDegreeModule() == courseGroup) {
                result += degreeModuleToEvaluate.getDegreeModule().getMinEctsCredits();
            }
        }

        return result;

    }

    private boolean canObtainApprovalInOtherCurricularPeriod(final double missingEctsToConcludeGroup,
            final Context curricularCourseContext, final Map<CurricularPeriod, Set<Context>> contextsByCurricularPeriod) {

        final int curricularCourseSemester = curricularCourseContext.getCurricularPeriod().getChildOrder();
        double ectsToPerformInOtherCurricularPeriods = 0;

        for (final Entry<CurricularPeriod, Set<Context>> each : contextsByCurricularPeriod.entrySet()) {
            if (each.getKey().getChildOrder() == curricularCourseSemester) {
                continue;
            }

            for (final Context context : each.getValue()) {
                final CurricularCourse curricularCourse = (CurricularCourse) context.getChildDegreeModule();
                // final String name = curricularCourse.getName();
                ectsToPerformInOtherCurricularPeriods += curricularCourse.getMinEctsCredits();
            }
        }

        return ectsToPerformInOtherCurricularPeriods >= missingEctsToConcludeGroup;

    }

    private double calculateTotalEctsInGroup(final EnrolmentContext enrolmentContext, final CurriculumGroup curriculumGroup,
            final boolean withTemporaryEnrolments) {
        double result = curriculumGroup.getCreditsConcluded(enrolmentContext.getExecutionPeriod().getExecutionYear());
        result += curriculumGroup.getEnroledEctsCredits(enrolmentContext.getExecutionPeriod());

        if (withTemporaryEnrolments) {
            result += curriculumGroup.getEnroledEctsCredits(enrolmentContext.getExecutionPeriod().getPreviousExecutionPeriod());
        }

        return result;
    }

    private boolean isEnroled(final EnrolmentContext enrolmentContext, final CurricularCourse curricularCourse,
            final boolean withTemporaryEnrolments) {
        if (withTemporaryEnrolments) {
            return isEnroled(enrolmentContext, curricularCourse, enrolmentContext.getExecutionPeriod())
                    || isEnroled(enrolmentContext, curricularCourse, enrolmentContext.getExecutionPeriod()
                            .getPreviousExecutionPeriod());
        }

        return isEnroled(enrolmentContext, curricularCourse, enrolmentContext.getExecutionPeriod());

    }

    private boolean isCurricularRulesSatisfied(EnrolmentContext enrolmentContext, CurricularCourse curricularCourse,
            IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate) {
        RuleResult result = RuleResult.createTrue(sourceDegreeModuleToEvaluate.getDegreeModule());
        for (final ICurricularRule curricularRule : curricularCourse.getCurricularRules(enrolmentContext.getExecutionPeriod())) {
            result =
                    result.and(curricularRule.verify(getVerifyRuleLevel(enrolmentContext), enrolmentContext, curricularCourse,
                            (CourseGroup) sourceDegreeModuleToEvaluate.getDegreeModule()));
        }

        return result.isTrue();

    }

    private VerifyRuleLevel getVerifyRuleLevel(final EnrolmentContext enrolmentContext) {
        return enrolmentContext.getCurricularRuleLevel() == CurricularRuleLevel.ENROLMENT_WITH_RULES_AND_TEMPORARY_ENROLMENT ? VerifyRuleLevel.ENROLMENT_WITH_RULES_AND_TEMPORARY : VerifyRuleLevel.ENROLMENT_WITH_RULES;
    }

    private SortedSet<Context> getChildCurricularCoursesContextsToEvaluate(final CourseGroup courseGroup,
            final EnrolmentContext enrolmentContext) {
        final ExecutionSemester executionSemester = enrolmentContext.getExecutionPeriod();

        final SortedSet<Context> result = new TreeSet<Context>(Context.COMPARATOR_BY_CURRICULAR_YEAR);

        final int minModules = getMinModules(courseGroup, executionSemester);
        final int maxModules = getMaxModules(courseGroup, executionSemester);
        final int childDegreeModulesCount =
                courseGroup.getActiveChildContextsWithMax(enrolmentContext.getExecutionPeriod()).size();

        if (minModules == maxModules) {
            if (maxModules == childDegreeModulesCount) {
                // N-N == Nchilds
                result.addAll(courseGroup.getActiveChildContextsWithMaxCurricularPeriodForCurricularCourses(enrolmentContext
                        .getExecutionPeriod()));
            } else {
                // N-N <> Nchilds
                if (getSelectedChildDegreeModules(courseGroup, enrolmentContext).size() < minModules) {
                    result.addAll(courseGroup.getActiveChildContextsWithMaxCurricularPeriodForCurricularCourses(enrolmentContext
                            .getExecutionPeriod()));
                } else {
                    result.addAll(getSelectedChildCurricularCoursesContexts(courseGroup, enrolmentContext));
                }
            }
        } else {
            // N-M
            if (getSelectedChildDegreeModules(courseGroup, enrolmentContext).size() < minModules) {
                result.addAll(courseGroup.getActiveChildContextsWithMaxCurricularPeriodForCurricularCourses(enrolmentContext
                        .getExecutionPeriod()));
            } else {
                result.addAll(getSelectedChildCurricularCoursesContexts(courseGroup, enrolmentContext));
            }
        }

        return result;

    }

    private Set<Context> getSelectedChildCurricularCoursesContexts(final CourseGroup courseGroup,
            final EnrolmentContext enrolmentContext) {
        final Set<Context> result = new HashSet<Context>();

        for (final Context context : courseGroup.getActiveChildContexts()) {
            if (context.getChildDegreeModule().isCurricularCourse()
                    && enrolmentContext.getStudentCurricularPlan().hasDegreeModule(context.getChildDegreeModule())) {
                result.add(context);
            }
        }

        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext.getDegreeModulesToEvaluate()) {
            if (degreeModuleToEvaluate.isLeaf() && degreeModuleToEvaluate.getCurriculumGroup() != null
                    && degreeModuleToEvaluate.getCurriculumGroup().getDegreeModule() == courseGroup) {

                if (degreeModuleToEvaluate.isAnnualCurricularCourse(enrolmentContext.getExecutionPeriod().getExecutionYear())
                        && degreeModuleToEvaluate.getContext() == null) {
                    continue;
                }

                final Context context = degreeModuleToEvaluate.getContext();
                if (context == null) {
                    throw new DomainException("error.degreeModuleToEvaluate.has.invalid.context",
                            degreeModuleToEvaluate.getName(), degreeModuleToEvaluate.getExecutionPeriod().getQualifiedName());
                }

                result.add(context);
            }
        }

        return result;
    }

    private void addValidCurricularCourses(final Map<Integer, Set<CurricularCourse>> result,
            final Set<Context> curricularCoursesContexts, final CourseGroup courseGroup, final ExecutionSemester executionSemester) {
        for (final Context context : curricularCoursesContexts) {
            if (context.isValid(executionSemester)) {
                addCurricularCourse(result, context.getCurricularYear(), (CurricularCourse) context.getChildDegreeModule());
            }
        }
    }

    private void addCurricularCourse(Map<Integer, Set<CurricularCourse>> result, Integer curricularYear,
            CurricularCourse curricularCourse) {
        Set<CurricularCourse> curricularCourses = result.get(curricularYear);

        if (curricularCourses == null) {
            curricularCourses = new HashSet<CurricularCourse>();
            result.put(curricularYear, curricularCourses);
        }

        curricularCourses.add(curricularCourse);

    }

    private void collectChildCourseGroupsCurricularCoursesToEnrol(final Map<Integer, Set<CurricularCourse>> result,
            final CourseGroup courseGroup, final CollectContext parentCollectContext, final EnrolmentContext enrolmentContext,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final boolean withTemporaryEnrolments) {
        for (final DegreeModule childDegreeModule : courseGroup.getChildDegreeModulesValidOn(enrolmentContext
                .getExecutionPeriod())) {
            if (childDegreeModule.isCourseGroup()) {
                collectCourseGroupCurricularCoursesToEnrol(result, (CourseGroup) childDegreeModule, new CollectContext(
                        parentCollectContext), enrolmentContext, sourceDegreeModuleToEvaluate, withTemporaryEnrolments);
            }
        }
    }

    @Override
    protected RuleResult executeEnrolmentWithRulesAndTemporaryEnrolment(final ICurricularRule curricularRule,
            IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final EnrolmentContext enrolmentContext) {

        if (isEnrollingInCourseGroupsOnly(enrolmentContext, sourceDegreeModuleToEvaluate)) {
            return RuleResult.createNA(sourceDegreeModuleToEvaluate.getDegreeModule());
        }

        final PreviousYearsEnrolmentCurricularRule previousYearsEnrolmentCurricularRule =
                (PreviousYearsEnrolmentCurricularRule) curricularRule;
        final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear =
                getCurricularCoursesToEnrolByYear(previousYearsEnrolmentCurricularRule, enrolmentContext,
                        sourceDegreeModuleToEvaluate, false);
        final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYearWithTemporaries =
                getCurricularCoursesToEnrolByYear(previousYearsEnrolmentCurricularRule, enrolmentContext,
                        sourceDegreeModuleToEvaluate, true);

        return hasAnyCurricularCoursesToEnrolInPreviousYears(enrolmentContext, curricularCoursesToEnrolByYear,
                curricularCoursesToEnrolByYearWithTemporaries, sourceDegreeModuleToEvaluate);

    }

    private RuleResult hasAnyCurricularCoursesToEnrolInPreviousYears(final EnrolmentContext enrolmentContext,
            final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear,
            final Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYearWithTemporaries,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate) {

        RuleResult result = RuleResult.createTrue(sourceDegreeModuleToEvaluate.getDegreeModule());
        for (final IDegreeModuleToEvaluate degreeModuleToEvaluate : enrolmentContext
                .getAllChildDegreeModulesToEvaluateFor(sourceDegreeModuleToEvaluate.getDegreeModule())) {
            if (degreeModuleToEvaluate.isLeaf()) {

                if (degreeModuleToEvaluate.isAnnualCurricularCourse(enrolmentContext.getExecutionPeriod().getExecutionYear())
                        && degreeModuleToEvaluate.getContext() == null) {
                    continue;
                }

                if (degreeModuleToEvaluate.getContext() == null) {
                    throw new DomainException("error.degreeModuleToEvaluate.has.invalid.context",
                            degreeModuleToEvaluate.getName(), degreeModuleToEvaluate.getExecutionPeriod().getQualifiedName());
                }

                if (hasCurricularCoursesToEnrolInPreviousYears(curricularCoursesToEnrolByYearWithTemporaries,
                        degreeModuleToEvaluate.getContext().getCurricularYear())) {

                    if (degreeModuleToEvaluate.isEnroled()) {
                        result = result.and(createImpossibleRuleResult(sourceDegreeModuleToEvaluate, degreeModuleToEvaluate));

                    } else {
                        result = result.and(createFalseRuleResult(sourceDegreeModuleToEvaluate, degreeModuleToEvaluate));
                    }

                } else {
                    if (isAnyPreviousYearCurricularCoursesTemporary(curricularCoursesToEnrolByYear,
                            curricularCoursesToEnrolByYearWithTemporaries, degreeModuleToEvaluate.getContext()
                                    .getCurricularYear())) {
                        result =
                                result.and(RuleResult.createTrue(EnrolmentResultType.TEMPORARY,
                                        degreeModuleToEvaluate.getDegreeModule()));

                    }
                }
            }
        }

        return result;
    }

    private boolean isAnyPreviousYearCurricularCoursesTemporary(
            Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYear,
            Map<Integer, Set<CurricularCourse>> curricularCoursesToEnrolByYearWithTemporaries, Integer curricularYear) {

        for (int i = curricularYear; i > 0; i--) {
            final int previousYear = i - 1;

            if (!curricularCoursesToEnrolByYear.containsKey(previousYear)
                    && !curricularCoursesToEnrolByYearWithTemporaries.containsKey(previousYear)) {
                continue;
            }

            if ((curricularCoursesToEnrolByYearWithTemporaries.containsKey(previousYear) && !curricularCoursesToEnrolByYear
                    .containsKey(previousYear))
                    || (!curricularCoursesToEnrolByYearWithTemporaries.containsKey(previousYear) && curricularCoursesToEnrolByYear
                            .containsKey(previousYear))) {
                return true;
            }

            if (curricularCoursesToEnrolByYear.get(previousYear).size() != curricularCoursesToEnrolByYearWithTemporaries.get(
                    previousYear).size()) {
                return true;
            }
        }

        return false;

    }

    @Override
    protected RuleResult executeEnrolmentInEnrolmentEvaluation(final ICurricularRule curricularRule,
            final IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate, final EnrolmentContext enrolmentContext) {
        return RuleResult.createNA(sourceDegreeModuleToEvaluate.getDegreeModule());
    }

    @Override
    protected boolean canBeEvaluated(ICurricularRule curricularRule, IDegreeModuleToEvaluate sourceDegreeModuleToEvaluate,
            EnrolmentContext enrolmentContext) {
        return true;
    }
}
TOP

Related Classes of org.fenixedu.academic.domain.curricularRules.executors.ruleExecutors.PreviousYearsEnrolmentExecutor$CollectContext

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.