Package org.fenixedu.academic.domain.student.curriculum

Source Code of org.fenixedu.academic.domain.student.curriculum.Curriculum

/**
* 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.student.curriculum;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;

import org.fenixedu.academic.domain.ExecutionYear;
import org.fenixedu.academic.domain.IEnrolment;
import org.fenixedu.academic.domain.StudentCurricularPlan;
import org.fenixedu.academic.domain.degreeStructure.CycleType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.studentCurriculum.CurriculumModule;
import org.fenixedu.academic.domain.studentCurriculum.CycleCurriculumGroup;
import org.fenixedu.academic.domain.studentCurriculum.Dismissal;
import org.fenixedu.academic.domain.studentCurriculum.ExternalEnrolment;

public class Curriculum implements Serializable, ICurriculum {

    static private final long serialVersionUID = -8365985725904139675L;

    private CurriculumModule curriculumModule;

    private Boolean bolonhaDegree;

    private final ExecutionYear executionYear;

    private final Set<ICurriculumEntry> averageEnrolmentRelatedEntries = new HashSet<ICurriculumEntry>();

    private final Set<ICurriculumEntry> averageDismissalRelatedEntries = new HashSet<ICurriculumEntry>();

    private final Set<ICurriculumEntry> curricularYearEntries = new HashSet<ICurriculumEntry>();

    private BigDecimal sumPiCi;

    private BigDecimal sumPi;

    static final protected int SCALE = 2;

    static final protected RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP;

    private AverageType averageType = AverageType.WEIGHTED;

    private BigDecimal average;

    private BigDecimal sumEctsCredits;

    private Integer curricularYear;

    private boolean forceCalculus;

    static public Curriculum createEmpty(final ExecutionYear executionYear) {
        return Curriculum.createEmpty(null, executionYear);
    }

    static public Curriculum createEmpty(final CurriculumModule curriculumModule, final ExecutionYear executionYear) {
        return new Curriculum(curriculumModule, executionYear);
    }

    private Curriculum(final CurriculumModule curriculumModule, final ExecutionYear executionYear) {
        this.curriculumModule = curriculumModule;
        this.bolonhaDegree = curriculumModule == null ? null : curriculumModule.getStudentCurricularPlan().isBolonhaDegree();
        this.executionYear = executionYear;
    }

    public Curriculum(final CurriculumModule curriculumModule, final ExecutionYear executionYear,
            final Collection<ICurriculumEntry> averageEnrolmentRelatedEntries,
            final Collection<ICurriculumEntry> averageDismissalRelatedEntries,
            final Collection<ICurriculumEntry> curricularYearEntries) {
        this(curriculumModule, executionYear);

        addAverageEntries(this.averageEnrolmentRelatedEntries, averageEnrolmentRelatedEntries);
        addAverageEntries(this.averageDismissalRelatedEntries, averageDismissalRelatedEntries);
        addCurricularYearEntries(this.curricularYearEntries, curricularYearEntries);
    }

    public void add(final Curriculum curriculum) {
        if (!hasCurriculumModule()) {
            this.curriculumModule = curriculum.getCurriculumModule();
            this.bolonhaDegree = curriculum.isBolonha();
        }

        addAverageEntries(averageEnrolmentRelatedEntries, curriculum.getEnrolmentRelatedEntries());
        addAverageEntries(averageDismissalRelatedEntries, curriculum.getDismissalRelatedEntries());
        addCurricularYearEntries(curricularYearEntries, curriculum.getCurricularYearEntries());

        forceCalculus = true;
    }

    private void addAverageEntries(final Set<ICurriculumEntry> entries, final Collection<ICurriculumEntry> newEntries) {
        for (final ICurriculumEntry newEntry : newEntries) {
            if (!isAlreadyAverageEntry(newEntry)) {
                add(entries, newEntry);
            }
        }
    }

    private boolean isAlreadyAverageEntry(final ICurriculumEntry newEntry) {
        return averageEnrolmentRelatedEntries.contains(newEntry) || averageDismissalRelatedEntries.contains(newEntry);
    }

    private void addCurricularYearEntries(final Set<ICurriculumEntry> entries, final Collection<ICurriculumEntry> newEntries) {
        for (final ICurriculumEntry newEntry : newEntries) {
            add(entries, newEntry);
        }
    }

    private void add(final Set<ICurriculumEntry> entries, final ICurriculumEntry newEntry) {
        if (isBolonha() || !isAlreadyCurricularYearEntry(newEntry)) {
            entries.add(newEntry);
        }
    }

    private boolean isAlreadyCurricularYearEntry(final ICurriculumEntry newEntry) {
        if (newEntry instanceof IEnrolment) {
            return isCurricularYearEntryAsEnrolmentOrAsSourceEnrolment((IEnrolment) newEntry);
        } else if (newEntry instanceof Dismissal) {
            return isCurricularYearEntryAsSimilarDismissal((Dismissal) newEntry);
        }

        return false;
    }

    private boolean isCurricularYearEntryAsEnrolmentOrAsSourceEnrolment(final IEnrolment newIEnrolment) {
        for (final ICurriculumEntry entry : curricularYearEntries) {
            if (entry instanceof Dismissal && ((Dismissal) entry).hasSourceIEnrolments(newIEnrolment)) {
                return true;
            } else if (entry == newIEnrolment) {
                return true;
            }
        }

        return false;
    }

    private boolean isCurricularYearEntryAsSimilarDismissal(final Dismissal newDismissal) {
        for (final ICurriculumEntry entry : curricularYearEntries) {
            if (entry instanceof Dismissal && !newDismissal.isCreditsDismissal() && newDismissal.isSimilar((Dismissal) entry)) {
                return true;
            }
        }

        return false;
    }

    public CurriculumModule getCurriculumModule() {
        return curriculumModule;
    }

    public boolean hasCurriculumModule() {
        return getCurriculumModule() != null;
    }

    public Boolean isBolonha() {
        return bolonhaDegree;
    }

    public ExecutionYear getExecutionYear() {
        return executionYear;
    }

    @Override
    public StudentCurricularPlan getStudentCurricularPlan() {
        return hasCurriculumModule() ? getCurriculumModule().getStudentCurricularPlan() : null;
    }

    public boolean hasAverageEntry() {
        return hasCurriculumModule() && !getCurriculumEntries().isEmpty();
    }

    @Override
    public boolean isEmpty() {
        return !hasCurriculumModule() || (getCurriculumEntries().isEmpty() && curricularYearEntries.isEmpty());
    }

    @Override
    public Collection<ICurriculumEntry> getCurriculumEntries() {
        final Collection<ICurriculumEntry> result = new HashSet<ICurriculumEntry>();

        result.addAll(averageEnrolmentRelatedEntries);
        result.addAll(averageDismissalRelatedEntries);

        return result;
    }

    @Override
    public boolean hasAnyExternalApprovedEnrolment() {
        for (final ICurriculumEntry entry : averageDismissalRelatedEntries) {
            if (entry instanceof ExternalEnrolment) {
                return true;
            }
        }

        return false;
    }

    public Set<ICurriculumEntry> getEnrolmentRelatedEntries() {
        return averageEnrolmentRelatedEntries;
    }

    public Set<ICurriculumEntry> getDismissalRelatedEntries() {
        return averageDismissalRelatedEntries;
    }

    @Override
    public Set<ICurriculumEntry> getCurricularYearEntries() {
        return curricularYearEntries;
    }

    @Override
    public BigDecimal getSumPiCi() {
        if (sumPiCi == null || forceCalculus) {
            doCalculus();
            forceCalculus = false;
        }

        return sumPiCi;
    }

    @Override
    public BigDecimal getSumPi() {
        if (sumPi == null || forceCalculus) {
            doCalculus();
            forceCalculus = false;
        }

        return sumPi;
    }

    @Override
    public BigDecimal getAverage() {
        if (average == null || forceCalculus) {
            doCalculus();
            forceCalculus = false;
        }

        return average.setScale(SCALE, ROUNDING_MODE);
    }

    @Override
    public Integer getRoundedAverage() {
        return getRoundedAverage(getAverage());
    }

    static public Integer getRoundedAverage(final BigDecimal average) {
        return average.setScale(0, RoundingMode.HALF_UP).intValue();
    }

    @Override
    public BigDecimal getSumEctsCredits() {
        if (sumEctsCredits == null || forceCalculus) {
            doCalculus();
            forceCalculus = false;
        }

        return sumEctsCredits;
    }

    @Override
    public Integer getCurricularYear() {
        if (curricularYear == null || forceCalculus) {
            doCalculus();
            forceCalculus = false;
        }

        return curricularYear;
    }

    @Override
    public BigDecimal getRemainingCredits() {
        BigDecimal result = BigDecimal.ZERO;

        for (final ICurriculumEntry entry : curricularYearEntries) {
            if (entry instanceof Dismissal) {
                final Dismissal dismissal = (Dismissal) entry;
                if (dismissal.getCredits().isCredits() || dismissal.getCredits().isEquivalence()
                        || (dismissal.isCreditsDismissal() && !dismissal.getCredits().isSubstitution())) {
                    result = result.add(entry.getEctsCreditsForCurriculum());
                }
            }
        }

        return result;
    }

    private void doCalculus() {
        sumPiCi = BigDecimal.ZERO;
        sumPi = BigDecimal.ZERO;
        countAverage(averageEnrolmentRelatedEntries);
        countAverage(averageDismissalRelatedEntries);
        average = calculateAverage();

        sumEctsCredits = BigDecimal.ZERO;
        countCurricularYear(curricularYearEntries);
        accountForDirectIngressions();
        curricularYear = calculateCurricularYear();
    }

    @Override
    public void setAverageType(AverageType averageType) {
        this.averageType = averageType;
        forceCalculus = true;
    }

    private void countAverage(final Set<ICurriculumEntry> entries) {
        for (final ICurriculumEntry entry : entries) {
            if (entry.getGrade().isNumeric()) {
                final BigDecimal weigth = entry.getWeigthForCurriculum();

                if (averageType == AverageType.WEIGHTED) {
                    sumPi = sumPi.add(weigth);
                    sumPiCi = sumPiCi.add(entry.getWeigthTimesGrade());
                } else if (averageType == AverageType.SIMPLE) {
                    sumPi = sumPi.add(BigDecimal.ONE);
                    sumPiCi = sumPiCi.add(entry.getGrade().getNumericValue());
                } else {
                    throw new DomainException("Curriculum.average.type.not.supported");
                }
            }
        }
    }

    private BigDecimal calculateAverage() {
        return sumPi.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO : sumPiCi.divide(sumPi, SCALE * SCALE + 1, ROUNDING_MODE);
    }

    private void countCurricularYear(final Set<ICurriculumEntry> entries) {
        for (final ICurriculumEntry entry : entries) {
            sumEctsCredits = sumEctsCredits.add(entry.getEctsCreditsForCurriculum());
        }
    }

    private void accountForDirectIngressions() {
        if (getCycleType() != null) {
            return;
        }
        if (!getStudentCurricularPlan().getDegreeCurricularPlan().isBolonhaDegree()) {
            return;
        }
        //this is to prevent some oddly behavior spotted (e.g. student 57276)
        if (getStudentCurricularPlan().getCycleCurriculumGroups().isEmpty()) {
            return;
        }
        CycleCurriculumGroup sgroup =
                Collections.min(getStudentCurricularPlan().getCycleCurriculumGroups(),
                        CycleCurriculumGroup.COMPARATOR_BY_CYCLE_TYPE_AND_ID);
        CycleType cycleIter = sgroup.getCycleType().getPrevious();
        while (cycleIter != null) {
            if (getStudentCurricularPlan().getDegreeCurricularPlan().getCycleCourseGroup(cycleIter) != null) {
                sumEctsCredits = sumEctsCredits.add(new BigDecimal(cycleIter.getEctsCredits()));
            }
            cycleIter = cycleIter.getPrevious();
        }
    }

    private Integer calculateCurricularYear() {
        if (sumEctsCredits.compareTo(BigDecimal.ZERO) == 0) {
            return Integer.valueOf(1);
        }

        final BigDecimal ectsCreditsCurricularYear =
                sumEctsCredits.add(BigDecimal.valueOf(24))
                        .divide(BigDecimal.valueOf(60), SCALE * SCALE + 1, RoundingMode.HALF_EVEN).add(BigDecimal.valueOf(1));
        return Math.min(ectsCreditsCurricularYear.intValue(), getTotalCurricularYears().intValue());
    }

    @Override
    public Integer getTotalCurricularYears() {
        final StudentCurricularPlan plan = getStudentCurricularPlan();
        if (plan == null) {
            return Integer.valueOf(0);
        }

        return plan.getDegreeType().getYears(getCycleType());
    }

    private CycleType getCycleType() {
        if (!hasCurriculumModule() || !isBolonha()) {
            return null;
        }

        final CurriculumModule module = getCurriculumModule();
        final CycleType cycleType = module.isCycleCurriculumGroup() ? ((CycleCurriculumGroup) module).getCycleType() : null;
        return cycleType;
    }

    @Override
    public String toString() {
        final StringBuilder result = new StringBuilder();

        result.append("\n[CURRICULUM]");
        if (hasCurriculumModule()) {
            result.append("\n[CURRICULUM_MODULE][ID] " + getCurriculumModule().getExternalId() + "\t[NAME]"
                    + getCurriculumModule().getName().getContent());
            result.append("\n[BOLONHA] " + isBolonha().toString());
        } else {
            result.append("\n[NO CURRICULUM_MODULE]");
        }
        result.append("\n[SUM ENTRIES] " + (averageEnrolmentRelatedEntries.size() + averageDismissalRelatedEntries.size()));
        result.append("\n[SUM PiCi] " + getSumPiCi().toString());
        result.append("\n[SUM Pi] " + getSumPi().toString());
        result.append("\n[AVERAGE] " + getAverage());
        result.append("\n[ROUNDED_AVERAGE] " + getRoundedAverage());
        result.append("\n[SUM ECTS CREDITS] " + getSumEctsCredits().toString());
        result.append("\n[CURRICULAR YEAR] " + getCurricularYear());
        result.append("\n[REMAINING CREDITS] " + getRemainingCredits().toString());
        result.append("\n[TOTAL CURRICULAR YEARS] " + getTotalCurricularYears());

        result.append("\n[AVERAGE ENROLMENT ENTRIES]");
        for (final ICurriculumEntry entry : averageEnrolmentRelatedEntries) {
            result.append("\n[ENTRY] [NAME]" + entry.getName().getContent() + "\t[CREATION_DATE]"
                    + entry.getCreationDateDateTime() + "\t[GRADE] " + entry.getGrade().toString() + "\t[WEIGHT] "
                    + entry.getWeigthForCurriculum() + "\t[ECTS] " + entry.getEctsCreditsForCurriculum() + "\t[CLASS_NAME] "
                    + entry.getClass().getSimpleName());
        }

        result.append("\n[AVERAGE DISMISSAL RELATED ENTRIES]");
        for (final ICurriculumEntry entry : averageDismissalRelatedEntries) {
            result.append("\n[ENTRY] [NAME]" + entry.getName().getContent() + "\t[CREATION_DATE]"
                    + entry.getCreationDateDateTime() + "\t[GRADE] " + entry.getGrade().toString() + "\t[WEIGHT] "
                    + entry.getWeigthForCurriculum() + "\t[ECTS] " + entry.getEctsCreditsForCurriculum() + "\t[CLASS_NAME] "
                    + entry.getClass().getSimpleName());
        }

        result.append("\n[CURRICULAR YEAR ENTRIES]");
        for (final ICurriculumEntry entry : curricularYearEntries) {
            result.append("\n[ENTRY] [NAME]" + entry.getName().getContent() + "\t[CREATION_DATE]"
                    + entry.getCreationDateDateTime() + "\t[ECTS] " + entry.getEctsCreditsForCurriculum() + "\t[CLASS_NAME] "
                    + entry.getClass().getSimpleName());
        }

        return result.toString();
    }

    /**
     * This is used to remove an entry from average and curricular year entries
     * in order to do calculus without it
     *
     * @param entryToRemove
     */
    public void removeFromAllCurriculumEntries(final ICurriculumEntry entryToRemove) {
        averageEnrolmentRelatedEntries.remove(entryToRemove);
        averageDismissalRelatedEntries.remove(entryToRemove);
        curricularYearEntries.remove(entryToRemove);
    }

}
TOP

Related Classes of org.fenixedu.academic.domain.student.curriculum.Curriculum

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.