package net.sourceforge.fenixedu.domain.student.curriculum; import java.io.Serializable; import java.math.BigDecimal; import java.math.RoundingMode; import java.util.Collection; import java.util.HashSet; import java.util.Set; import net.sourceforge.fenixedu.domain.DegreeCurricularPlan; import net.sourceforge.fenixedu.domain.ExecutionYear; import net.sourceforge.fenixedu.domain.IEnrolment; import net.sourceforge.fenixedu.domain.StudentCurricularPlan; import net.sourceforge.fenixedu.domain.exceptions.DomainException; import net.sourceforge.fenixedu.domain.student.Registration; import net.sourceforge.fenixedu.domain.student.curriculum.AverageRule.AverageRuleResult; import net.sourceforge.fenixedu.domain.student.curriculum.curricularYear.CurricularYearCalculatorFactory; import net.sourceforge.fenixedu.domain.studentCurriculum.CurriculumGroup; import net.sourceforge.fenixedu.domain.studentCurriculum.CurriculumModule; import net.sourceforge.fenixedu.domain.studentCurriculum.CycleCurriculumGroup; import net.sourceforge.fenixedu.domain.studentCurriculum.Dismissal; import net.sourceforge.fenixedu.domain.studentCurriculum.ExternalEnrolment; import net.sourceforge.fenixedu.domain.studentCurriculum.RootCurriculumGroup; import pt.iscte.ci.metadata.ISCTE; public class Curriculum implements Serializable, ICurriculum { static private final long serialVersionUID = 1L; private CurriculumModule curriculumModule; private ExecutionYear executionYear; private Set enrolmentRelatedEntries = new HashSet(); private Set dismissalRelatedEntries = new HashSet(); private Set curricularYearEntries = new HashSet(); private BigDecimal sumEctsCredits; private AverageRule averageRule; private AverageRuleResult averageRuleResult; static final protected int SCALE = 2; static final protected RoundingMode ROUNDING_MODE = RoundingMode.HALF_UP; private BigDecimal average; 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.executionYear = executionYear; } public Curriculum(final CurriculumModule curriculumModule, final ExecutionYear executionYear, final Collection entries, final Collection dismissalRelatedEntries, final Collection curricularYearEntries) { this(curriculumModule, executionYear); addEntries(this.enrolmentRelatedEntries, entries); addEntries(this.dismissalRelatedEntries, dismissalRelatedEntries); addEntries(this.curricularYearEntries, curricularYearEntries); } public void add(final Curriculum curriculum) { addEntries(this.enrolmentRelatedEntries, curriculum.getEnrolmentRelatedEntries()); addEntries(this.dismissalRelatedEntries, curriculum.getDismissalRelatedEntries()); addEntries(this.curricularYearEntries, curriculum.getCurricularYearEntries()); forceCalculus = true; } private void addEntries(final Set entries, final Collection newEntries) { final boolean bolonhaDegree = curriculumModule.getStudentCurricularPlan().isBolonhaDegree(); for (final ICurriculumEntry newEntry : newEntries) { if (bolonhaDegree || shouldAdd(newEntry)) { entries.add(newEntry); } } } /** * Just for pre-Bbolonha verification */ final private boolean shouldAdd(final ICurriculumEntry newEntry) { if (newEntry instanceof IEnrolment) { final IEnrolment newIEnrolment = (IEnrolment) newEntry; for (final ICurriculumEntry entry : curricularYearEntries) { if (entry instanceof Dismissal && ((Dismissal) entry).hasSourceIEnrolments(newIEnrolment)) { return false; } else if (entry == newIEnrolment) { return false; } } } else if (newEntry instanceof Dismissal) { final Dismissal newDismissal = (Dismissal) newEntry; for (final ICurriculumEntry entry : curricularYearEntries) { if (entry instanceof Dismissal && newDismissal.isSimilar((Dismissal) entry)) { return false; } } } return true; } public CurriculumModule getCurriculumModule() { return curriculumModule; } public CurriculumGroup getStartCurriculumGroup() { return (CurriculumGroup) (this.curriculumModule.isLeaf() ? null : this.curriculumModule); } public ExecutionYear getExecutionYear() { return executionYear; } public boolean hasExecutionYear() { return getExecutionYear() != null; } public StudentCurricularPlan getStudentCurricularPlan() { return curriculumModule == null ? null : curriculumModule.getStudentCurricularPlan(); } public boolean hasAverageEntry() { return curriculumModule != null && !getCurriculumEntries().isEmpty(); } public boolean isEmpty() { return curriculumModule == null || (getCurriculumEntries().isEmpty() && this.curricularYearEntries.isEmpty()); } public Collection getCurriculumEntries() { final Collection result = new HashSet(); result.addAll(enrolmentRelatedEntries); result.addAll(dismissalRelatedEntries); return result; } public boolean hasAnyExternalApprovedEnrolment() { for (final ICurriculumEntry entry : dismissalRelatedEntries) { if (entry instanceof ExternalEnrolment) { return true; } } return false; } public Set getEnrolmentRelatedEntries() { return enrolmentRelatedEntries; } public Set getDismissalRelatedEntries() { return dismissalRelatedEntries; } public Set getCurricularYearEntries() { return curricularYearEntries; } public BigDecimal getSumPi() { if (averageRuleResult == null || forceCalculus) { doCalculus(); forceCalculus = false; } return averageRuleResult.getSumOfWeight(); } /** * @author rjmvo */ public BigDecimal getSumEi() { if (averageRuleResult == null || forceCalculus) { doCalculus(); forceCalculus = false; } return averageRuleResult.getSumOfEcts(); } public BigDecimal getSumEctsCredits() { if (sumEctsCredits == null || forceCalculus) { doCalculus(); forceCalculus = false; } return sumEctsCredits; } public BigDecimal getSumPiCi() { if (averageRuleResult == null || forceCalculus) { doCalculus(); forceCalculus = false; } return averageRuleResult.getGradeTimesFactor(); } public BigDecimal getAverage() { if (average == null || forceCalculus) { doCalculus(); forceCalculus = false; } return average.setScale(SCALE, ROUNDING_MODE); } public Integer getRoundedAverage() { return getAverage().setScale(0, RoundingMode.HALF_UP).intValue(); } public Integer getCurricularYear() { if (curricularYear == null || forceCalculus) { doCalculus(); forceCalculus = false; } return curricularYear; } public BigDecimal getRemainingCredits() { BigDecimal result = BigDecimal.ZERO; for (final ICurriculumEntry entry : this.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() { initAverageRule(); average = averageRuleResult.getAverage(); sumEctsCredits = calculateSumEctsCredits(); if (getStudentCurricularPlan().isBolonhaDegree()) { curricularYear = calculateCurricularYear(); } else { curricularYear = getStudentCurricularPlan().getRegistration().getCurricularYear(); } } private void initAverageRule() { if (!hasAverageRule()) { changeAverageRule(getDegreeCurricularPlan().getAverageRule(getExecutionYearForAverageRule())); } if (!hasAverageRuleResult()) { averageRuleResult = calculateAverageRuleResult(); } } private ExecutionYear getExecutionYearForAverageRule() { if (curriculumModule != null) { if (curriculumModule.isCycleCurriculumGroup()) { final CycleCurriculumGroup cycle = (CycleCurriculumGroup) curriculumModule; return cycle.isConclusionProcessed() ? cycle.getConclusionYear() : (cycle.isConcluded() ? cycle .calculateConclusionYear() : ExecutionYear.readCurrentExecutionYear()); } else if (curriculumModule.isRoot()) { final RootCurriculumGroup root = (RootCurriculumGroup) curriculumModule; final Registration registration = root.getStudentCurricularPlan().getRegistration(); return registration.isRegistrationConclusionProcessed() ? registration.getConclusionYear() : (root.isConcluded() ? registration.calculateConclusionYear() : ExecutionYear.readCurrentExecutionYear()); } } throw new DomainException("error.Curriculum.invalid.curriculum.module"); } private AverageRuleResult calculateAverageRuleResult() { return averageRule.calculate(getTopCurriculumGroup(), getCurriculumEntries(), SCALE * SCALE + 1, ROUNDING_MODE); } private CurriculumGroup getTopCurriculumGroup() { return (CurriculumGroup) (!this.curriculumModule.isLeaf() ? this.curriculumModule : null); } @ISCTE(author = "Nadir Amin") private BigDecimal calculateSumEctsCredits() { BigDecimal result = BigDecimal.ZERO; for (final ICurriculumEntry entry : getCurricularYearEntries()) { result = result.add(entry.getEctsCreditsForCurriculum()); } return result; } private Integer calculateCurricularYear() { return CurricularYearCalculatorFactory.create(this).calculateYear(); } public Integer getTotalCurricularYears() { return Float.valueOf(getDegreeCurricularPlan().getDegreeStructure().getPeriodType().getWeight()).intValue(); } @Override public String toString() { final StringBuilder result = new StringBuilder(); result.append("\n[CURRICULUM]"); result.append("\n[CURRICULUM_MODULE][ID] ").append(curriculumModule.getIdInternal()); result.append("\t[NAME]").append(curriculumModule.getName().getContent()); result.append("\n[SUM ENTRIES] ").append(enrolmentRelatedEntries.size() + dismissalRelatedEntries.size()); result.append("\n[AVERAGE] ").append(average); result.append("\n[SUM WEIGHT] ").append(getSumPi()); result.append("\n[SUM ECTS CREDITS] ").append(getSumEi()); result.append("\n[SUM APPROVED ECTS CREDITS] ").append(getSumEctsCredits()); result.append("\n[SUM GRADE TIMES FACTOR] ").append(getSumPiCi()); result.append("\n[CURRICULAR YEAR] ").append(curricularYear); result.append("\n[ENTRIES]"); for (final ICurriculumEntry entry : enrolmentRelatedEntries) { result.append("\n[ENTRY] [NAME]").append(entry.getName().getContent()); result.append("\t[GRADE] ").append(entry.getGrade().getNumericValue()); result.append("\t[WEIGHT] ").append(entry.getWeigthForCurriculum()); result.append("\t[ECTS] ").append(entry.getEctsCreditsForCurriculum()); result.append("\t[CLASS_NAME] ").append(entry.getClass().getSimpleName()); } result.append("\n[DISMISSAL RELATED ENTRIES]"); for (final ICurriculumEntry entry : dismissalRelatedEntries) { result.append("\n[ENTRY] [NAME]").append(entry.getName().getContent()); result.append("\t[GRADE] ").append(entry.getGrade().getNumericValue()); result.append("\t[WEIGHT] ").append(entry.getWeigthForCurriculum()); result.append("\t[ECTS] ").append(entry.getEctsCreditsForCurriculum()); result.append("\t[CLASS_NAME] ").append(entry.getClass().getSimpleName()); } result.append("\n[CURRICULAR YEAR ENTRIES]"); for (final ICurriculumEntry entry : curricularYearEntries) { result.append("\n[ENTRY] [NAME]").append(entry.getName().getContent()); result.append("\t[ECTS] ").append(entry.getEctsCreditsForCurriculum()); result.append("\t[CLASS_NAME] ").append(entry.getClass().getSimpleName()); } return result.toString(); } public AverageRule getAverageRule() { if (averageRule == null) { initAverageRule(); } return averageRule; } public void changeAverageRule(AverageRule averageRule) { this.averageRule = averageRule; } public boolean hasAverageRule() { return averageRule != null; } public String getAverageResult() { return averageRuleResult.getAverageResult(); } private boolean hasAverageRuleResult() { return averageRuleResult != null; } private DegreeCurricularPlan getDegreeCurricularPlan() { return getStudentCurricularPlan().getDegreeCurricularPlan(); } @ISCTE(author = "Nadir Amin") public BigDecimal getDismissedEctsCredits() { BigDecimal result = BigDecimal.ZERO; for (final ICurriculumEntry entry : this.curricularYearEntries) { if (entry instanceof Dismissal && ((Dismissal) entry).getCredits().isCredits()) { result = result.add(entry.getEctsCreditsForCurriculum()); } } return result; } }