/** * 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; import static org.fenixedu.academic.predicate.AccessControl.check; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.ListIterator; import java.util.Locale; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import java.util.function.Function; import java.util.stream.Collectors; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.apache.commons.lang.StringUtils; import org.fenixedu.academic.domain.accounting.serviceAgreementTemplates.DegreeCurricularPlanServiceAgreementTemplate; import org.fenixedu.academic.domain.curricularPeriod.CurricularPeriod; import org.fenixedu.academic.domain.curricularRules.CurricularRule; import org.fenixedu.academic.domain.curricularRules.MaximumNumberOfCreditsForEnrolmentPeriod; import org.fenixedu.academic.domain.degree.DegreeType; import org.fenixedu.academic.domain.degree.degreeCurricularPlan.DegreeCurricularPlanState; import org.fenixedu.academic.domain.degreeStructure.BranchCourseGroup; import org.fenixedu.academic.domain.degreeStructure.BranchType; import org.fenixedu.academic.domain.degreeStructure.Context; import org.fenixedu.academic.domain.degreeStructure.CourseGroup; import org.fenixedu.academic.domain.degreeStructure.CurricularCourseFunctor; import org.fenixedu.academic.domain.degreeStructure.CurricularStage; import org.fenixedu.academic.domain.degreeStructure.CycleCourseGroup; import org.fenixedu.academic.domain.degreeStructure.CycleType; import org.fenixedu.academic.domain.degreeStructure.DegreeModule; import org.fenixedu.academic.domain.degreeStructure.OptionalCurricularCourse; import org.fenixedu.academic.domain.degreeStructure.ProgramConclusion; import org.fenixedu.academic.domain.degreeStructure.RootCourseGroup; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.domain.space.SpaceUtils; import org.fenixedu.academic.domain.student.Registration; import org.fenixedu.academic.domain.thesis.Thesis; import org.fenixedu.academic.domain.time.calendarStructure.AcademicCalendarEntry; import org.fenixedu.academic.domain.time.calendarStructure.AcademicCalendarRootEntry; import org.fenixedu.academic.domain.time.calendarStructure.AcademicInterval; import org.fenixedu.academic.domain.time.calendarStructure.AcademicPeriod; import org.fenixedu.academic.domain.time.calendarStructure.AcademicYearCE; import org.fenixedu.academic.domain.time.calendarStructure.AcademicYears; import org.fenixedu.academic.dto.CurricularPeriodInfoDTO; import org.fenixedu.academic.predicate.AcademicPredicates; import org.fenixedu.academic.predicate.AccessControl; import org.fenixedu.academic.predicate.DegreeCurricularPlanPredicates; import org.fenixedu.academic.util.MarkType; import org.fenixedu.academic.util.MultiLanguageString; import org.fenixedu.academic.util.PeriodState; import org.fenixedu.bennu.core.domain.Bennu; import org.fenixedu.bennu.core.groups.Group; import org.fenixedu.commons.i18n.I18N; import org.fenixedu.spaces.domain.Space; import org.joda.time.DateTime; import org.joda.time.YearMonthDay; import pt.ist.fenixframework.Atomic; public class DegreeCurricularPlan extends DegreeCurricularPlan_Base { public static final Comparator<DegreeCurricularPlan> COMPARATOR_BY_NAME = new Comparator<DegreeCurricularPlan>() { @Override public int compare(DegreeCurricularPlan o1, DegreeCurricularPlan o2) { return o1.getName().compareTo(o2.getName()); } }; public static final Comparator<DegreeCurricularPlan> COMPARATOR_BY_PRESENTATION_NAME = new Comparator<DegreeCurricularPlan>() { @Override public int compare(DegreeCurricularPlan o1, DegreeCurricularPlan o2) { final int c = o1.getPresentationName().compareTo(o2.getPresentationName()); return c == 0 ? DomainObjectUtil.COMPARATOR_BY_ID.compare(o1, o2) : c; } }; /** * This might look a strange comparator, but the idea is to show a list of * degree curricular plans according to, in the following order: 1. It's * degree type 2. Reverse order of ExecutionDegrees 3. It's degree code (in * order to roughly order them by prebolonha/bolonha) OR reverse order of * their own name * * For an example, see the coordinator's portal. */ public static final Comparator<DegreeCurricularPlan> DEGREE_CURRICULAR_PLAN_COMPARATOR_BY_DEGREE_TYPE_AND_EXECUTION_DEGREE_AND_DEGREE_CODE = new Comparator<DegreeCurricularPlan>() { @Override public int compare(DegreeCurricularPlan o1, DegreeCurricularPlan o2) { final int degreeTypeCompare = o1.getDegreeType().getName().compareTo(o2.getDegreeType().getName()); if (degreeTypeCompare != 0) { return degreeTypeCompare; } int finalCompare = o1.getDegree().getSigla().compareTo(o2.getDegree().getSigla()); if (finalCompare == 0) { finalCompare = o2.getName().compareTo(o1.getName()); } if (finalCompare == 0) { finalCompare = o1.getExternalId().compareTo(o2.getExternalId()); } return finalCompare; } }; protected DegreeCurricularPlan() { super(); super.setRootDomainObject(Bennu.getInstance()); super.setApplyPreviousYearsEnrolmentRule(Boolean.TRUE); } private DegreeCurricularPlan(Degree degree, String name, GradeScale gradeScale) { this(); if (degree == null) { throw new DomainException("degreeCurricularPlan.degree.not.null"); } if (name == null) { throw new DomainException("degreeCurricularPlan.name.not.null"); } super.setDegree(degree); super.setName(name); super.setGradeScale(gradeScale); } protected DegreeCurricularPlan(Degree degree, String name, DegreeCurricularPlanState state, Date inicialDate, Date endDate, Integer degreeDuration, Integer minimalYearForOptionalCourses, Double neededCredits, MarkType markType, Integer numerusClausus, String annotation, GradeScale gradeScale) { this(degree, name, gradeScale); super.setCurricularStage(CurricularStage.OLD); super.setState(state); oldStructureFieldsChange(inicialDate, endDate, degreeDuration, minimalYearForOptionalCourses, neededCredits, markType, numerusClausus, annotation); } private void oldStructureFieldsChange(Date inicialDate, Date endDate, Integer degreeDuration, Integer minimalYearForOptionalCourses, Double neededCredits, MarkType markType, Integer numerusClausus, String annotation) { if (inicialDate == null) { throw new DomainException("degreeCurricularPlan.inicialDate.not.null"); } else if (degreeDuration == null) { throw new DomainException("degreeCurricularPlan.degreeDuration.not.null"); } else if (minimalYearForOptionalCourses == null) { throw new DomainException("degreeCurricularPlan.minimalYearForOptionalCourses.not.null"); } this.setInitialDateYearMonthDay(new YearMonthDay(inicialDate)); this.setEndDateYearMonthDay(endDate != null ? new YearMonthDay(endDate) : null); this.setDegreeDuration(degreeDuration); this.setMinimalYearForOptionalCourses(minimalYearForOptionalCourses); this.setNeededCredits(neededCredits); this.setMarkType(markType); this.setNumerusClausus(numerusClausus); this.setAnotation(annotation); } DegreeCurricularPlan(Degree degree, String name, GradeScale gradeScale, Person creator, CurricularPeriod curricularPeriod) { this(degree, name, gradeScale); if (creator == null) { throw new DomainException("degreeCurricularPlan.creator.not.null"); } if (curricularPeriod == null) { throw new DomainException("degreeCurricularPlan.curricularPeriod.not.null"); } createDefaultCourseGroups(); setCurricularPlanMembersGroup(creator.getUser().groupOf()); setDegreeStructure(curricularPeriod); setState(DegreeCurricularPlanState.ACTIVE); newStructureFieldsChange(CurricularStage.DRAFT, null); createDefaultCurricularRules(); new DegreeCurricularPlanServiceAgreementTemplate(this); } private void createDefaultCourseGroups() { RootCourseGroup.createRoot(this, getName(), getName()); } private void createDefaultCurricularRules() { new MaximumNumberOfCreditsForEnrolmentPeriod(getRoot(), ExecutionSemester.readActualExecutionSemester()); } private void newStructureFieldsChange(CurricularStage curricularStage, ExecutionYear beginExecutionYear) { if (curricularStage == null) { throw new DomainException("degreeCurricularPlan.curricularStage.not.null"); } else if (!getExecutionDegreesSet().isEmpty() && curricularStage == CurricularStage.DRAFT) { throw new DomainException("degreeCurricularPlan.has.already.been.executed"); } else if (curricularStage == CurricularStage.APPROVED) { approve(beginExecutionYear); } else { setCurricularStage(curricularStage); } } private void commonFieldsChange(String name, GradeScale gradeScale) { if (name == null) { throw new DomainException("degreeCurricularPlan.name.not.null"); } // assert unique pair name/degree for (final DegreeCurricularPlan dcp : this.getDegree().getDegreeCurricularPlansSet()) { if (dcp != this && dcp.getName().equalsIgnoreCase(name)) { throw new DomainException("error.degreeCurricularPlan.existing.name.and.degree"); } } this.setName(name); this.setGradeScale(gradeScale); } public void edit(String name, DegreeCurricularPlanState state, Date inicialDate, Date endDate, Integer degreeDuration, Integer minimalYearForOptionalCourses, Double neededCredits, MarkType markType, Integer numerusClausus, String annotation, GradeScale gradeScale) { commonFieldsChange(name, gradeScale); oldStructureFieldsChange(inicialDate, endDate, degreeDuration, minimalYearForOptionalCourses, neededCredits, markType, numerusClausus, annotation); this.setState(state); } public void edit(String name, CurricularStage curricularStage, DegreeCurricularPlanState state, GradeScale gradeScale, ExecutionYear beginExecutionYear) { if (curricularStage.equals(CurricularStage.APPROVED) && !getAllCoursesGroups().stream() .map(CourseGroup::getProgramConclusion).anyMatch(Objects::nonNull)) { throw new DomainException("error.degreeCurricularPlan.missing.program.conclusion"); } if (isApproved() && (name != null && !getName().equals(name) || gradeScale != null && !getGradeScale().equals(gradeScale))) { throw new DomainException("error.degreeCurricularPlan.already.approved"); } else { commonFieldsChange(name, gradeScale); } newStructureFieldsChange(curricularStage, beginExecutionYear); this.setState(state); this.getRoot().setName(name); this.getRoot().setNameEn(name); } private void approve(ExecutionYear beginExecutionYear) { if (isApproved()) { return; } if (!getCanModify().booleanValue()) { throw new DomainException("error.degreeCurricularPlan.already.approved"); } final ExecutionSemester beginExecutionPeriod; if (beginExecutionYear == null) { throw new DomainException("error.invalid.execution.year"); } else { beginExecutionPeriod = beginExecutionYear.getFirstExecutionPeriod(); if (beginExecutionPeriod == null) { throw new DomainException("error.invalid.execution.period"); } } checkIfCurricularCoursesBelongToApprovedCompetenceCourses(); initBeginExecutionPeriodForDegreeCurricularPlan(getRoot(), beginExecutionPeriod); setCurricularStage(CurricularStage.APPROVED); } private void checkIfCurricularCoursesBelongToApprovedCompetenceCourses() { final List<String> notApprovedCompetenceCourses = new ArrayList<String>(); for (final DegreeModule degreeModule : getDcpDegreeModules(CurricularCourse.class)) { final CurricularCourse curricularCourse = (CurricularCourse) degreeModule; if (!curricularCourse.isOptional() && !curricularCourse.getCompetenceCourse().isApproved()) { notApprovedCompetenceCourses.add(curricularCourse.getCompetenceCourse().getDepartmentUnit().getName() + " > " + curricularCourse.getCompetenceCourse().getName()); } } if (!notApprovedCompetenceCourses.isEmpty()) { final String[] result = new String[notApprovedCompetenceCourses.size()]; throw new DomainException("error.not.all.competence.courses.are.approved", notApprovedCompetenceCourses.toArray(result)); } } private void initBeginExecutionPeriodForDegreeCurricularPlan(final CourseGroup courseGroup, final ExecutionSemester beginExecutionPeriod) { if (beginExecutionPeriod == null) { throw new DomainException(""); } for (final CurricularRule curricularRule : courseGroup.getCurricularRulesSet()) { curricularRule.setBegin(beginExecutionPeriod); } for (final Context context : courseGroup.getChildContextsSet()) { context.setBeginExecutionPeriod(beginExecutionPeriod); if (!context.getChildDegreeModule().isLeaf()) { initBeginExecutionPeriodForDegreeCurricularPlan((CourseGroup) context.getChildDegreeModule(), beginExecutionPeriod); } } } public boolean isBolonhaDegree() { return getDegree().isBolonhaDegree(); } public boolean isEmpty() { return false; } public boolean isApproved() { return getCurricularStage() == CurricularStage.APPROVED; } public boolean isDraft() { return getCurricularStage() == CurricularStage.DRAFT; } public boolean isActive() { return getState() == DegreeCurricularPlanState.ACTIVE; } private Boolean getCanBeDeleted() { return canDeleteRoot() && getStudentCurricularPlansSet().isEmpty() && getCurricularCourseEquivalencesSet().isEmpty() && getEnrolmentPeriodsSet().isEmpty() && getCurricularCoursesSet().isEmpty() && getExecutionDegreesSet().isEmpty() && getAreasSet().isEmpty() && canDeleteServiceAgreement() && getTeachersWithIncompleteEvaluationWorkGroupSet().isEmpty() && getEquivalencePlan() == null && getTargetEquivalencePlansSet().isEmpty() && getDegreeContextsSet().isEmpty(); } private boolean canDeleteRoot() { return getRoot().getCanBeDeleted(); } private boolean canDeleteServiceAgreement() { return getServiceAgreementTemplate() == null || getServiceAgreementTemplate().getPostingRulesSet().isEmpty() && getServiceAgreementTemplate().getServiceAgreementsSet().isEmpty(); } public void delete() { if (getCanBeDeleted()) { setDegree(null); getRoot().delete(); if (getDegreeStructure() != null) { getDegreeStructure().delete(); } if (getServiceAgreementTemplate() != null) { DegreeCurricularPlanServiceAgreementTemplate template = getServiceAgreementTemplate(); setServiceAgreementTemplate(null); template.delete(); } setShift(null); setMembersGroup(null); setRootDomainObject(null); deleteDomainObject(); } else { throw new DomainException("error.degree.curricular.plan.cant.delete"); } } public String print() { StringBuilder dcp = new StringBuilder(); dcp.append("[DCP ").append(this.getExternalId()).append("] ").append(this.getName()).append("\n"); this.getRoot().print(dcp, "", null); return dcp.toString(); } public GradeScale getGradeScaleChain() { return super.getGradeScale() != null ? super.getGradeScale() : getDegree().getGradeScaleChain(); } @Deprecated public ExecutionDegree getExecutionDegreeByYear(ExecutionYear executionYear) { for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) { if (executionDegree.getExecutionYear() == executionYear) { return executionDegree; } } return null; } // FIXME: Optimization Required public ExecutionDegree getExecutionDegreeByAcademicInterval(AcademicInterval academicInterval) { AcademicCalendarEntry academicCalendarEntry = academicInterval.getAcademicCalendarEntry(); while (!(academicCalendarEntry instanceof AcademicCalendarRootEntry)) { if (academicCalendarEntry instanceof AcademicYearCE) { ExecutionYear year = ExecutionYear.getExecutionYear((AcademicYearCE) academicCalendarEntry); for (ExecutionDegree executionDegree : getExecutionDegreesSet()) { if (executionDegree.getExecutionYear().getAcademicInterval().equals(year.getAcademicInterval())) { return executionDegree; } } } academicCalendarEntry = academicCalendarEntry.getParentEntry(); } return null; } public Set<ExecutionYear> getExecutionYears() { Set<ExecutionYear> result = new HashSet<ExecutionYear>(); for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) { result.add(executionDegree.getExecutionYear()); } return result; } public ExecutionYear getMostRecentExecutionYear() { return getMostRecentExecutionDegree().getExecutionYear(); } public ExecutionDegree getExecutionDegreeByYearAndCampus(ExecutionYear executionYear, Space campus) { for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) { if (executionDegree.getExecutionYear() == executionYear && executionDegree.getCampus() == campus) { return executionDegree; } } return null; } public boolean hasExecutionDegreeByYearAndCampus(ExecutionYear executionYear, Space campus) { return getExecutionDegreeByYearAndCampus(executionYear, campus) != null; } public boolean hasAnyExecutionDegreeFor(ExecutionYear executionYear) { for (final ExecutionDegree executionDegree : this.getExecutionDegreesSet()) { if (executionDegree.getExecutionYear() == executionYear) { return true; } } return false; } public boolean hasExecutionDegreeFor(ExecutionYear executionYear) { return getExecutionDegreeByYear(executionYear) != null; } public ExecutionDegree getMostRecentExecutionDegree() { if (getExecutionDegreesSet().isEmpty()) { return null; } final ExecutionYear currentYear = ExecutionYear.readCurrentExecutionYear(); ExecutionDegree result = getExecutionDegreeByYear(currentYear); if (result != null) { return result; } final List<ExecutionDegree> sorted = new ArrayList<ExecutionDegree>(getExecutionDegreesSet()); Collections.sort(sorted, ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR); final ExecutionDegree first = sorted.iterator().next(); if (sorted.size() == 1) { return first; } if (first.getExecutionYear().isAfter(currentYear)) { return first; } else { final ListIterator<ExecutionDegree> iter = sorted.listIterator(sorted.size()); while (iter.hasPrevious()) { final ExecutionDegree executionDegree = iter.previous(); if (executionDegree.getExecutionYear().isBeforeOrEquals(currentYear)) { return executionDegree; } } } return null; } public ExecutionDegree getFirstExecutionDegree() { if (getExecutionDegreesSet().isEmpty()) { return null; } return Collections.min(getExecutionDegreesSet(), ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR); } public Set<ExecutionCourse> getExecutionCoursesByExecutionPeriod(ExecutionSemester executionSemester) { final Set<ExecutionCourse> result = new HashSet<ExecutionCourse>(); addExecutionCoursesForExecutionPeriod(result, executionSemester, getRoot().getChildContextsSet()); return result; } public SortedSet<DegreeModuleScope> getDegreeModuleScopes() { final SortedSet<DegreeModuleScope> degreeModuleScopes = new TreeSet<DegreeModuleScope>( DegreeModuleScope.COMPARATOR_BY_CURRICULAR_YEAR_AND_SEMESTER_AND_CURRICULAR_COURSE_NAME); for (final CurricularCourse curricularCourse : this.getCurricularCoursesSet()) { degreeModuleScopes.addAll(curricularCourse.getDegreeModuleScopes()); } return degreeModuleScopes; } public Set<DegreeModuleScope> getDegreeModuleScopesFor(final Integer year, final Integer semester) { final Set<DegreeModuleScope> result = new TreeSet<DegreeModuleScope>( DegreeModuleScope.COMPARATOR_BY_CURRICULAR_YEAR_AND_SEMESTER_AND_CURRICULAR_COURSE_NAME); for (final DegreeModuleScope each : getDegreeModuleScopes()) { if (each.isActive(year, semester)) { result.add(each); } } return result; } public Set<DegreeModuleScope> getDegreeModuleScopesFor(final ExecutionYear executionYear) { final Set<DegreeModuleScope> result = new TreeSet<DegreeModuleScope>( DegreeModuleScope.COMPARATOR_BY_CURRICULAR_YEAR_AND_SEMESTER_AND_CURRICULAR_COURSE_NAME); for (final DegreeModuleScope each : getDegreeModuleScopes()) { if (each.isActiveForExecutionYear(executionYear)) { result.add(each); } } return result; } public void addExecutionCoursesForExecutionPeriod(final Set<ExecutionCourse> executionCourses, final ExecutionSemester executionSemester, final Set<Context> contexts) { for (final Context context : contexts) { final DegreeModule degreeModule = context.getChildDegreeModule(); if (degreeModule instanceof CurricularCourse) { final CurricularCourse curricularCourse = (CurricularCourse) degreeModule; executionCourses.addAll(curricularCourse.getExecutionCoursesByExecutionPeriod(executionSemester)); } else if (degreeModule instanceof CourseGroup) { final CourseGroup courseGroup = (CourseGroup) degreeModule; addExecutionCoursesForExecutionPeriod(executionCourses, executionSemester, courseGroup.getChildContextsSet()); } } } public List<ExecutionCourse> getExecutionCoursesByExecutionPeriodAndSemesterAndYear(ExecutionSemester executionSemester, Integer curricularYear, Integer semester) { List<ExecutionCourse> result = new ArrayList<ExecutionCourse>(); for (final CurricularCourse curricularCourse : this.getCurricularCoursesSet()) { for (final DegreeModuleScope degreeModuleScope : curricularCourse.getDegreeModuleScopes()) { if (degreeModuleScope.getCurricularSemester().equals(semester) && degreeModuleScope.getCurricularYear().equals(curricularYear)) { for (final ExecutionCourse executionCourse : curricularCourse.getAssociatedExecutionCoursesSet()) { if (executionCourse.getExecutionPeriod().equals(executionSemester)) { result.add(executionCourse); } } break; } } } return result; } public Set<CurricularCourse> getAllCurricularCourses() { final Set<DegreeModule> curricularCourses = new TreeSet<DegreeModule>(DegreeModule.COMPARATOR_BY_NAME) { @Override public boolean add(DegreeModule degreeModule) { return degreeModule instanceof CurricularCourse && super.add(degreeModule); } }; getRoot().getAllDegreeModules(curricularCourses); return (Set) curricularCourses; } public List<CurricularCourse> getCurricularCoursesWithExecutionIn(ExecutionYear executionYear) { List<CurricularCourse> curricularCourses = new ArrayList<CurricularCourse>(); for (CurricularCourse curricularCourse : getCurricularCoursesSet()) { for (ExecutionSemester executionSemester : executionYear.getExecutionPeriodsSet()) { List<ExecutionCourse> executionCourses = curricularCourse.getExecutionCoursesByExecutionPeriod(executionSemester); if (!executionCourses.isEmpty()) { curricularCourses.add(curricularCourse); break; } } } return curricularCourses; } public List<CurricularCourse> getCurricularCoursesByBasicAttribute(final Boolean basic) { if (isBolonhaDegree()) { return Collections.emptyList(); } final List<CurricularCourse> curricularCourses = new ArrayList<CurricularCourse>(); for (final CurricularCourse curricularCourse : getCurricularCoursesSet()) { if (curricularCourse.getBasic().equals(basic)) { curricularCourses.add(curricularCourse); } } return curricularCourses; } public EnrolmentPeriodInCurricularCourses getActualEnrolmentPeriod() { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCourses && enrolmentPeriod.isValid()) { return (EnrolmentPeriodInCurricularCourses) enrolmentPeriod; } } return null; } public Optional<EnrolmentPeriod> getActiveCurricularCourseEnrolmentPeriod(ExecutionSemester executionSemester) { return getEnrolmentPeriodsSet() .stream() .filter(ep -> ep.getExecutionPeriod() == executionSemester && ep instanceof EnrolmentPeriodInCurricularCourses && ep.isValid()).findAny(); } public boolean hasActualEnrolmentPeriodInCurricularCourses() { return getActualEnrolmentPeriod() != null; } public EnrolmentPeriodInSpecialSeasonEvaluations getNextSpecialSeasonEnrolmentPeriod() { final List<EnrolmentPeriodInSpecialSeasonEvaluations> positivesSet = new ArrayList<EnrolmentPeriodInSpecialSeasonEvaluations>(); for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInSpecialSeasonEvaluations && enrolmentPeriod.isUpcomingPeriod()) { positivesSet.add((EnrolmentPeriodInSpecialSeasonEvaluations) enrolmentPeriod); } } return positivesSet.isEmpty() ? null : Collections.min(positivesSet, EnrolmentPeriodInSpecialSeasonEvaluations.COMPARATOR_BY_START); } public boolean hasOpenSpecialSeasonEnrolmentPeriod(ExecutionSemester executionSemester) { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInSpecialSeasonEvaluations && enrolmentPeriod.isFor(executionSemester) && enrolmentPeriod.isValid()) { return true; } } return false; } //TODO remove in next major it's no used and there can be more than one active, if don't specify the semester public EnrolmentPeriodInCurricularCoursesSpecialSeason getActualEnrolmentPeriodInCurricularCoursesSpecialSeason() { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesSpecialSeason && enrolmentPeriod.isValid()) { return (EnrolmentPeriodInCurricularCoursesSpecialSeason) enrolmentPeriod; } } return null; } public Optional<EnrolmentPeriod> getActiveEnrolmentPeriodInCurricularCoursesSpecialSeason(ExecutionSemester executionSemester) { return getEnrolmentPeriodsSet() .stream() .filter(ep -> ep.getExecutionPeriod() == executionSemester && ep instanceof EnrolmentPeriodInCurricularCoursesSpecialSeason && ep.isValid()).findAny(); } public boolean hasOpenEnrolmentPeriodInCurricularCoursesSpecialSeason(final ExecutionSemester executionSemester) { for (final EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesSpecialSeason) { final EnrolmentPeriodInCurricularCoursesSpecialSeason enrolmentPeriodInCurricularCourses = (EnrolmentPeriodInCurricularCoursesSpecialSeason) enrolmentPeriod; if (enrolmentPeriodInCurricularCourses.isFor(executionSemester) && enrolmentPeriodInCurricularCourses.isValid()) { return true; } } } return false; } //TODO remove in next major it's no used and there can be more than one active, if don't specify the semester public EnrolmentPeriodInCurricularCoursesFlunkedSeason getActualEnrolmentPeriodInCurricularCoursesFlunkedSeason() { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesFlunkedSeason && enrolmentPeriod.isValid()) { return (EnrolmentPeriodInCurricularCoursesFlunkedSeason) enrolmentPeriod; } } return null; } public Optional<EnrolmentPeriod> getActiveEnrolmentPeriodInCurricularCoursesFlunkedSeason(ExecutionSemester executionSemester) { return getEnrolmentPeriodsSet() .stream() .filter(ep -> ep.getExecutionPeriod() == executionSemester && ep instanceof EnrolmentPeriodInCurricularCoursesFlunkedSeason && ep.isValid()).findAny(); } public boolean hasOpenEnrolmentPeriodInCurricularCoursesFor(final ExecutionSemester executionSemester) { for (final EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCourses) { final EnrolmentPeriodInCurricularCourses enrolmentPeriodInCurricularCourses = (EnrolmentPeriodInCurricularCourses) enrolmentPeriod; if (enrolmentPeriodInCurricularCourses.isFor(executionSemester) && enrolmentPeriodInCurricularCourses.isValid()) { return true; } } } return false; } public EnrolmentPeriodInCurricularCourses getNextEnrolmentPeriod() { final DateTime now = new DateTime(); final List<EnrolmentPeriodInCurricularCourses> result = new ArrayList<EnrolmentPeriodInCurricularCourses>(); for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCourses && enrolmentPeriod.getStartDateDateTime().isAfter(now)) { result.add((EnrolmentPeriodInCurricularCourses) enrolmentPeriod); } } if (!result.isEmpty()) { Collections.sort(result, EnrolmentPeriodInCurricularCourses.COMPARATOR_BY_START); return result.iterator().next(); } return null; } public EnrolmentPeriodInCurricularCoursesSpecialSeason getNextEnrolmentPeriodInCurricularCoursesSpecialSeason() { final DateTime now = new DateTime(); final List<EnrolmentPeriodInCurricularCoursesSpecialSeason> result = new ArrayList<EnrolmentPeriodInCurricularCoursesSpecialSeason>(); for (EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesSpecialSeason && enrolmentPeriod.getStartDateDateTime().isAfter(now)) { result.add((EnrolmentPeriodInCurricularCoursesSpecialSeason) enrolmentPeriod); } } if (!result.isEmpty()) { Collections.sort(result, EnrolmentPeriodInCurricularCoursesSpecialSeason.COMPARATOR_BY_START); return result.iterator().next(); } return null; } public EnrolmentPeriodInCurricularCoursesFlunkedSeason getNextEnrolmentPeriodInCurricularCoursesFlunkedSeason() { final DateTime now = new DateTime(); final List<EnrolmentPeriodInCurricularCoursesFlunkedSeason> result = new ArrayList<EnrolmentPeriodInCurricularCoursesFlunkedSeason>(); for (EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesFlunkedSeason && enrolmentPeriod.getStartDateDateTime().isAfter(now)) { result.add((EnrolmentPeriodInCurricularCoursesFlunkedSeason) enrolmentPeriod); } } if (!result.isEmpty()) { Collections.sort(result, EnrolmentPeriodInCurricularCoursesFlunkedSeason.COMPARATOR_BY_START); return result.iterator().next(); } return null; } public EnrolmentPeriodInClasses getCurrentClassesEnrollmentPeriod() { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInClasses && enrolmentPeriod.getExecutionPeriod().getState().equals(PeriodState.CURRENT)) { return (EnrolmentPeriodInClasses) enrolmentPeriod; } } return null; } public Optional<EnrolmentPeriod> getClassesEnrollmentPeriod(ExecutionSemester executionSemester) { return getEnrolmentPeriodsSet().stream() .filter(ep -> ep instanceof EnrolmentPeriodInClasses && ep.getExecutionPeriod() == executionSemester).findAny(); } public Optional<EnrolmentPeriod> getClassesEnrollmentPeriodMobility(ExecutionSemester executionSemester) { return getEnrolmentPeriodsSet().stream() .filter(ep -> ep instanceof EnrolmentPeriodInClassesMobility && ep.getExecutionPeriod() == executionSemester) .findAny(); } public CandidacyPeriodInDegreeCurricularPlan getCurrentCandidacyPeriodInDCP() { for (final EnrolmentPeriod enrolmentPeriod : this.getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof CandidacyPeriodInDegreeCurricularPlan && enrolmentPeriod.getExecutionPeriod().getExecutionYear().isCurrent()) { return (CandidacyPeriodInDegreeCurricularPlan) enrolmentPeriod; } } return null; } public CandidacyPeriodInDegreeCurricularPlan getCandidacyPeriod(final ExecutionYear executionYear) { final List<EnrolmentPeriod> enrolmentPeriods = getEnrolmentPeriodsBy(executionYear.getFirstExecutionPeriod(), CandidacyPeriodInDegreeCurricularPlan.class); return (CandidacyPeriodInDegreeCurricularPlan) (!enrolmentPeriods.isEmpty() ? enrolmentPeriods.iterator().next() : null); } public boolean hasCandidacyPeriodFor(final ExecutionYear executionYear) { return hasEnrolmentPeriodFor(executionYear.getFirstExecutionPeriod(), CandidacyPeriodInDegreeCurricularPlan.class); } public RegistrationPeriodInDegreeCurricularPlan getRegistrationPeriod(final ExecutionYear executionYear) { final List<EnrolmentPeriod> enrolmentPeriods = getEnrolmentPeriodsBy(executionYear.getFirstExecutionPeriod(), RegistrationPeriodInDegreeCurricularPlan.class); return (RegistrationPeriodInDegreeCurricularPlan) (!enrolmentPeriods.isEmpty() ? enrolmentPeriods.iterator().next() : null); } public boolean hasRegistrationPeriodFor(final ExecutionYear executionYear) { return hasEnrolmentPeriodFor(executionYear.getFirstExecutionPeriod(), RegistrationPeriodInDegreeCurricularPlan.class); } private List<EnrolmentPeriod> getEnrolmentPeriodsBy(final ExecutionSemester executionSemester, Class clazz) { final List<EnrolmentPeriod> result = new ArrayList<EnrolmentPeriod>(); for (final EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (enrolmentPeriod.getClass().equals(clazz) && enrolmentPeriod.getExecutionPeriod() == executionSemester) { result.add(enrolmentPeriod); } } return result; } private boolean hasEnrolmentPeriodFor(final ExecutionSemester executionSemester, Class clazz) { for (final EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (enrolmentPeriod.getClass().equals(clazz) && enrolmentPeriod.getExecutionPeriod() == executionSemester) { return true; } } return false; } public Collection<ExecutionYear> getCandidacyPeriodsExecutionYears() { return getEnrolmentPeriodsExecutionYears(CandidacyPeriodInDegreeCurricularPlan.class); } private Set<ExecutionYear> getEnrolmentPeriodsExecutionYears(Class clazz) { Set<ExecutionYear> result = new HashSet<ExecutionYear>(); for (final EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (clazz == null || enrolmentPeriod.getClass().equals(clazz)) { result.add(enrolmentPeriod.getExecutionPeriod().getExecutionYear()); } } return result; } public EnrolmentPeriodInCurricularCoursesSpecialSeason getEnrolmentPeriodInCurricularCoursesSpecialSeasonByExecutionPeriod( ExecutionSemester executionSemester) { for (EnrolmentPeriod enrolmentPeriod : getEnrolmentPeriodsSet()) { if (enrolmentPeriod instanceof EnrolmentPeriodInCurricularCoursesSpecialSeason && enrolmentPeriod.getExecutionPeriod().equals(executionSemester)) { return (EnrolmentPeriodInCurricularCoursesSpecialSeason) enrolmentPeriod; } } return null; } public EnrolmentPeriodInCurricularCourses getEnrolmentPeriodInCurricularCoursesBy(final ExecutionSemester executionSemester) { for (final EnrolmentPeriod each : getEnrolmentPeriodsSet()) { if (each instanceof EnrolmentPeriodInCurricularCourses && each.getExecutionPeriod().equals(executionSemester)) { return (EnrolmentPeriodInCurricularCourses) each; } } return null; } public ReingressionPeriod getReingressionPeriod(final ExecutionSemester executionSemester) { for (final EnrolmentPeriod period : getEnrolmentPeriodsSet()) { if (period instanceof ReingressionPeriod && period.isFor(executionSemester)) { return (ReingressionPeriod) period; } } return null; } public boolean hasEnrolmentPeriodInCurricularCourses(final ExecutionSemester executionSemester) { return getEnrolmentPeriodInCurricularCoursesBy(executionSemester) != null; } // ------------------------------------------------------------- // BEGIN: Only for enrollment purposes // ------------------------------------------------------------- public CurricularCourse getCurricularCourseByCode(String code) { for (CurricularCourse curricularCourse : getCurricularCoursesSet()) { if (curricularCourse.getCode() != null && curricularCourse.getCode().equals(code)) { return curricularCourse; } } return null; } public CurricularCourse getCurricularCourseByAcronym(String acronym) { for (CurricularCourse curricularCourse : getCurricularCoursesSet()) { if (curricularCourse.getAcronym().equals(acronym)) { return curricularCourse; } } return null; } @Override public Set<CurricularCourse> getCurricularCoursesSet() { return this.getCurricularCourses((ExecutionYear) null); } public void doForAllCurricularCourses(final CurricularCourseFunctor curricularCourseFunctor) { getRoot().doForAllCurricularCourses(curricularCourseFunctor); } public Set<CurricularCourse> getCurricularCourses(final ExecutionSemester executionSemester) { final Set<CurricularCourse> curricularCourses = new HashSet<CurricularCourse>(); for (final CurricularCourse curricularCourse : super.getCurricularCoursesSet()) { if (curricularCourse.hasScopeInGivenSemesterAndCurricularYearInDCP(null, null, executionSemester)) { curricularCourses.add(curricularCourse); } } final ExecutionYear executionYear = executionSemester.getExecutionYear(); for (final DegreeModule degreeModule : getDcpDegreeModules(CurricularCourse.class, executionYear)) { curricularCourses.add((CurricularCourse) degreeModule); } return curricularCourses; } /** * Method to get a filtered list of a dcp's curricular courses, with at * least one open context in the given execution year * * @return All curricular courses that are present in the dcp */ private Set<CurricularCourse> getCurricularCourses(final ExecutionYear executionYear) { final Set<CurricularCourse> result = new HashSet<CurricularCourse>(); for (final DegreeModule degreeModule : getDcpDegreeModules(CurricularCourse.class, executionYear)) { result.add((CurricularCourse) degreeModule); } return result; } public void applyToCurricularCourses(final ExecutionYear executionYear, final Predicate predicate) { getRoot().applyToCurricularCourses(executionYear, predicate); } /** * Method to get an unfiltered list of a bolonha dcp's competence courses * * @return All competence courses that were or still are present in the dcp, * ordered by name */ public List<CompetenceCourse> getCompetenceCourses() { if (isBolonhaDegree()) { return getCompetenceCourses(null); } else { return new ArrayList<CompetenceCourse>(); } } /** * Method to get a filtered list of a dcp's competence courses in the given * execution year. Each competence courses is connected with a curricular * course with at least one open context in the execution year * * @return All competence courses that are present in the dcp */ public List<CompetenceCourse> getCompetenceCourses(ExecutionYear executionYear) { SortedSet<CompetenceCourse> result = new TreeSet<CompetenceCourse>(CompetenceCourse.COMPETENCE_COURSE_COMPARATOR_BY_NAME); if (isBolonhaDegree()) { for (final CurricularCourse curricularCourse : getCurricularCourses(executionYear)) { if (!curricularCourse.isOptionalCurricularCourse()) { result.add(curricularCourse.getCompetenceCourse()); } } return new ArrayList<CompetenceCourse>(result); } else { return new ArrayList<CompetenceCourse>(); } } public List<Branch> getCommonAreas() { return (List<Branch>) CollectionUtils.select(getAreasSet(), new Predicate() { @Override public boolean evaluate(Object obj) { Branch branch = (Branch) obj; if (branch.getBranchType() == null) { return branch.getName().equals("") && branch.getCode().equals(""); } return branch.getBranchType().equals(org.fenixedu.academic.domain.branch.BranchType.COMNBR); } }); } public Set<CurricularCourse> getActiveCurricularCourses() { final Set<CurricularCourse> result = new HashSet<CurricularCourse>(); for (final CurricularCourse curricularCourse : getCurricularCoursesSet()) { if (curricularCourse.hasAnyActiveDegreModuleScope()) { result.add(curricularCourse); } } return result; } public Set<CurricularCourse> getActiveCurricularCourses(final ExecutionSemester executionSemester) { final Set<CurricularCourse> result = new HashSet<CurricularCourse>(); for (final CurricularCourse curricularCourse : getCurricularCoursesSet()) { if (curricularCourse.hasAnyActiveDegreModuleScope(executionSemester)) { result.add(curricularCourse); } } return result; } public List<CurricularCourseScope> getActiveCurricularCourseScopes() { final List<CurricularCourseScope> result = new ArrayList<CurricularCourseScope>(); for (final CurricularCourse course : getCurricularCoursesSet()) { result.addAll(course.getActiveScopes()); } return result; } /** * Used to create a CurricularCourse to non box structure * @deprecated Curricular courses should no longer be created without a competence. */ @Deprecated public CurricularCourse createCurricularCourse(String name, String code, String acronym, Boolean enrolmentAllowed, CurricularStage curricularStage) { return new CurricularCourse(this, name, code, acronym, enrolmentAllowed, curricularStage); } public CourseGroup createCourseGroup(final CourseGroup parentCourseGroup, final String name, final String nameEn, final ExecutionSemester begin, final ExecutionSemester end) { return new CourseGroup(parentCourseGroup, name, nameEn, begin, end, null); } public CourseGroup createCourseGroup(final CourseGroup parentCourseGroup, final String name, final String nameEn, final ExecutionSemester begin, final ExecutionSemester end, final ProgramConclusion programConclusion) { return new CourseGroup(parentCourseGroup, name, nameEn, begin, end, programConclusion); } public BranchCourseGroup createBranchCourseGroup(final CourseGroup parentCourseGroup, final String name, final String nameEn, final BranchType branchType, final ExecutionSemester begin, final ExecutionSemester end) { return new BranchCourseGroup(parentCourseGroup, name, nameEn, branchType, begin, end); } public CurricularCourse createCurricularCourse(Double weight, String prerequisites, String prerequisitesEn, CurricularStage curricularStage, CompetenceCourse competenceCourse, CourseGroup parentCourseGroup, CurricularPeriod curricularPeriod, ExecutionSemester beginExecutionPeriod, ExecutionSemester endExecutionPeriod) { if (competenceCourse.getCurricularCourse(this) != null) { throw new DomainException("competenceCourse.already.has.a.curricular.course.in.degree.curricular.plan"); } checkIfAnualBeginsInFirstPeriod(competenceCourse, curricularPeriod); return new CurricularCourse(weight, prerequisites, prerequisitesEn, curricularStage, competenceCourse, parentCourseGroup, curricularPeriod, beginExecutionPeriod, endExecutionPeriod); } public CurricularCourse createOptionalCurricularCourse(CourseGroup parentCourseGroup, String name, String nameEn, CurricularStage curricularStage, CurricularPeriod curricularPeriod, ExecutionSemester beginExecutionPeriod, ExecutionSemester endExecutionPeriod) { return new OptionalCurricularCourse(parentCourseGroup, name, nameEn, curricularStage, curricularPeriod, beginExecutionPeriod, endExecutionPeriod); } private void checkIfAnualBeginsInFirstPeriod(final CompetenceCourse competenceCourse, final CurricularPeriod curricularPeriod) { if (competenceCourse.isAnual() && !curricularPeriod.hasChildOrderValue(1)) { throw new DomainException("competenceCourse.anual.but.trying.to.associate.curricular.course.not.to.first.period"); } } public List<DegreeModule> getDcpDegreeModules(final Class<? extends DegreeModule> clazz) { return getDcpDegreeModules(clazz, (ExecutionYear) null); } public List<DegreeModule> getDcpDegreeModules(final Class<? extends DegreeModule> clazz, final ExecutionYear executionYear) { return new ArrayList<DegreeModule>(getRoot().collectAllChildDegreeModules(clazz, executionYear)); } public List<DegreeModule> getDcpDegreeModules(final Class<? extends DegreeModule> clazz, final ExecutionSemester executionSemester) { return new ArrayList<DegreeModule>(getRoot().collectAllChildDegreeModules(clazz, executionSemester)); } public List<List<DegreeModule>> getDcpDegreeModulesIncludingFullPath(Class<? extends DegreeModule> clazz, ExecutionYear executionYear) { final List<List<DegreeModule>> result = new ArrayList<List<DegreeModule>>(); final List<DegreeModule> path = new ArrayList<DegreeModule>(); if (clazz.isAssignableFrom(CourseGroup.class)) { path.add(this.getRoot()); result.add(path); } this.getRoot().collectChildDegreeModulesIncludingFullPath(clazz, result, path, executionYear); return result; } public Branch getBranchByName(final String branchName) { if (branchName != null) { for (final Branch branch : getAreasSet()) { if (branchName.equals(branch.getName())) { return branch; } } } return null; } public Boolean getUserCanBuild() { Person person = AccessControl.getPerson(); return AcademicPredicates.MANAGE_DEGREE_CURRICULAR_PLANS.evaluate(this.getDegree()) || this.getCurricularPlanMembersGroup().isMember(person.getUser()); } public Boolean getCanModify() { if (isApproved()) { return false; } final Collection<ExecutionDegree> executionDegrees = getExecutionDegreesSet(); return executionDegrees.size() > 1 ? false : executionDegrees.isEmpty() || executionDegrees.iterator().next().getExecutionYear().isCurrent(); } public Group getCurricularPlanMembersGroup() { return getMembersGroup() != null ? getMembersGroup().toGroup() : Group.nobody(); } public void setCurricularPlanMembersGroup(Group group) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); setMembersGroup(group.toPersistentGroup()); } @Override public void setCurricularStage(CurricularStage curricularStage) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); super.setCurricularStage(curricularStage); } @Override public void setDegree(Degree degree) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); super.setDegree(degree); } @Override public void setRoot(RootCourseGroup courseGroup) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); super.setRoot(courseGroup); } @Override public void setDegreeStructure(CurricularPeriod degreeStructure) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); if (degreeStructure == null || !(degreeStructure.getAcademicPeriod() instanceof AcademicYears)) { throw new DomainException("error.degreeCurricularPlan.degreeStructure.must.be.specified.in.years"); } final CurricularPeriod currentDegreeStructure = super.getDegreeStructure(); if (currentDegreeStructure != degreeStructure) { if (!getAllCurricularCourses().isEmpty()) { throw new DomainException( "error.degreeCurricularPlan.degreeStructure.cannot.be.changed.when.already.has.curricular.courses"); } //not the most elegant solution but avoids breaking API if (currentDegreeStructure != null) { currentDegreeStructure.delete(); } } super.setDegreeStructure(degreeStructure); } @Override public void setGradeScale(GradeScale gradeScale) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); super.setGradeScale(gradeScale); } @Override public void setName(String name) { check(this, DegreeCurricularPlanPredicates.scientificCouncilWritePredicate); super.setName(name); } public String getPresentationName() { return getPresentationName(ExecutionYear.readCurrentExecutionYear(), I18N.getLocale()); } public String getPresentationName(final ExecutionYear executionYear) { return getPresentationName(executionYear, I18N.getLocale()); } public String getPresentationName(final ExecutionYear executionYear, final Locale locale) { return getDegree().getPresentationName(executionYear, locale) + " - " + getName(); } // ------------------------------------------------------------- // read static methods // ------------------------------------------------------------- public static List<DegreeCurricularPlan> readNotEmptyDegreeCurricularPlans() { final List<DegreeCurricularPlan> result = new ArrayList<DegreeCurricularPlan>(Bennu.getInstance().getDegreeCurricularPlansSet()); result.remove(readEmptyDegreeCurricularPlan()); return result; } public static DegreeCurricularPlan readEmptyDegreeCurricularPlan() { return EmptyDegreeCurricularPlan.getInstance(); } public static Set<DegreeCurricularPlan> readBolonhaDegreeCurricularPlans() { final Set<DegreeCurricularPlan> result = new HashSet<DegreeCurricularPlan>(); for (final Degree degree : Degree.readBolonhaDegrees()) { result.addAll(degree.getDegreeCurricularPlansSet()); } return result; } public static Set<DegreeCurricularPlan> readPreBolonhaDegreeCurricularPlans() { final Set<DegreeCurricularPlan> result = new HashSet<DegreeCurricularPlan>(); for (final Degree degree : Degree.readOldDegrees()) { result.addAll(degree.getDegreeCurricularPlansSet()); } return result; } static public List<DegreeCurricularPlan> readByCurricularStage(final CurricularStage curricularStage) { final List<DegreeCurricularPlan> result = new ArrayList<DegreeCurricularPlan>(); for (final DegreeCurricularPlan degreeCurricularPlan : readNotEmptyDegreeCurricularPlans()) { if (degreeCurricularPlan.getCurricularStage().equals(curricularStage)) { result.add(degreeCurricularPlan); } } return result; } /** * If state is null then just degree type is checked */ public static List<DegreeCurricularPlan> readByDegreeTypeAndState(java.util.function.Predicate<DegreeType> degreeType, DegreeCurricularPlanState state) { List<DegreeCurricularPlan> result = new ArrayList<DegreeCurricularPlan>(); for (DegreeCurricularPlan degreeCurricularPlan : readNotEmptyDegreeCurricularPlans()) { if (degreeType.test(degreeCurricularPlan.getDegree().getDegreeType()) && (state == null || degreeCurricularPlan.getState() == state)) { result.add(degreeCurricularPlan); } } return result; } /** * If state is null then just degree type is checked */ public static List<DegreeCurricularPlan> readByDegreeTypesAndState(java.util.function.Predicate<DegreeType> predicate, DegreeCurricularPlanState state) { List<DegreeCurricularPlan> result = new ArrayList<DegreeCurricularPlan>(); for (DegreeCurricularPlan degreeCurricularPlan : readNotEmptyDegreeCurricularPlans()) { if (predicate.test(degreeCurricularPlan.getDegree().getDegreeType()) && (state == null || degreeCurricularPlan.getState() == state)) { result.add(degreeCurricularPlan); } } return result; } public static DegreeCurricularPlan readByNameAndDegreeSigla(String name, String degreeSigla) { for (final DegreeCurricularPlan degreeCurricularPlan : readNotEmptyDegreeCurricularPlans()) { if (degreeCurricularPlan.getName().equalsIgnoreCase(name) && degreeCurricularPlan.getDegree().getSigla().equalsIgnoreCase(degreeSigla)) { return degreeCurricularPlan; } } return null; } public Set<CurricularCourseScope> findCurricularCourseScopesIntersectingPeriod(final Date beginDate, final Date endDate) { final Set<CurricularCourseScope> curricularCourseScopes = new HashSet<CurricularCourseScope>(); for (final CurricularCourse curricularCourse : getCurricularCoursesSet()) { curricularCourseScopes.addAll(curricularCourse.findCurricularCourseScopesIntersectingPeriod(beginDate, endDate)); } return curricularCourseScopes; } public ExecutionDegree createExecutionDegree(ExecutionYear executionYear, Space campus, Boolean publishedExamMap) { if (isBolonhaDegree() && isDraft()) { throw new DomainException("degree.curricular.plan.not.approved.cannot.create.execution.degree", this.getName()); } if (this.hasAnyExecutionDegreeFor(executionYear)) { throw new DomainException("degree.curricular.plan.already.has.execution.degree.for.this.year", this.getName(), executionYear.getYear()); } return new ExecutionDegree(this, executionYear, campus, publishedExamMap); } public CurricularPeriod getCurricularPeriodFor(int year, int semester) { final CurricularPeriodInfoDTO[] curricularPeriodInfos = buildCurricularPeriodInfoDTOsFor(year, semester); return getDegreeStructure().getCurricularPeriod(curricularPeriodInfos); } private CurricularPeriodInfoDTO[] buildCurricularPeriodInfoDTOsFor(int year, int semester) { final CurricularPeriodInfoDTO[] curricularPeriodInfos; if (getDurationInYears() > 1) { curricularPeriodInfos = new CurricularPeriodInfoDTO[] { new CurricularPeriodInfoDTO(year, AcademicPeriod.YEAR), new CurricularPeriodInfoDTO(semester, AcademicPeriod.SEMESTER) }; } else { curricularPeriodInfos = new CurricularPeriodInfoDTO[] { new CurricularPeriodInfoDTO(semester, AcademicPeriod.SEMESTER) }; } return curricularPeriodInfos; } public CurricularPeriod createCurricularPeriodFor(int year, int semester) { final CurricularPeriodInfoDTO[] curricularPeriodInfos = buildCurricularPeriodInfoDTOsFor(year, semester); return getDegreeStructure().addCurricularPeriod(curricularPeriodInfos); } @Override public YearMonthDay getInitialDateYearMonthDay() { if (isBolonhaDegree() && !getExecutionDegreesSet().isEmpty()) { final ExecutionDegree firstExecutionDegree = getFirstExecutionDegree(); return firstExecutionDegree.getExecutionYear().getBeginDateYearMonthDay(); } else { return super.getInitialDateYearMonthDay(); } } @Override public YearMonthDay getEndDateYearMonthDay() { if (isBolonhaDegree() && !getExecutionDegreesSet().isEmpty()) { final ExecutionDegree mostRecentExecutionDegree = getMostRecentExecutionDegree(); if (mostRecentExecutionDegree.getExecutionYear() == ExecutionYear.readCurrentExecutionYear()) { return null; } else { return mostRecentExecutionDegree.getExecutionYear().getBeginDateYearMonthDay(); } } else { return super.getEndDateYearMonthDay(); } } public Collection<StudentCurricularPlan> getActiveStudentCurricularPlans() { final Collection<StudentCurricularPlan> result = new HashSet<StudentCurricularPlan>(); for (StudentCurricularPlan studentCurricularPlan : getStudentCurricularPlansSet()) { if (studentCurricularPlan.isActive()) { result.add(studentCurricularPlan); } } return result; } public Set<Registration> getRegistrations() { final Set<Registration> registrations = new HashSet<Registration>(); for (StudentCurricularPlan studentCurricularPlan : getActiveStudentCurricularPlans()) { registrations.add(studentCurricularPlan.getRegistration()); } return registrations; } public Collection<Registration> getActiveRegistrations() { final Collection<Registration> result = new HashSet<Registration>(); for (StudentCurricularPlan studentCurricularPlan : getActiveStudentCurricularPlans()) { final Registration registration = studentCurricularPlan.getRegistration(); if (registration.isActive()) { result.add(registration); } } return result; } public boolean isPast() { return getState().equals(DegreeCurricularPlanState.PAST); } public Space getCampus(final ExecutionYear executionYear) { for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) { if (executionDegree.getExecutionYear() == executionYear) { return executionDegree.getCampus(); } } return null; } public Space getCurrentCampus() { for (final ExecutionDegree executionDegree : getExecutionDegreesSet()) { final ExecutionYear executionYear = executionDegree.getExecutionYear(); if (executionYear.isCurrent()) { return executionDegree.getCampus(); } } return null; } public Space getLastCampus() { if (!getExecutionDegreesSet().isEmpty()) { return getMostRecentExecutionDegree().getCampus(); } return SpaceUtils.getDefaultCampus(); } @Override public Integer getDegreeDuration() { final Integer degreeDuration = super.getDegreeDuration(); return degreeDuration == null ? getDurationInYears() : degreeDuration; } public DegreeType getDegreeType() { return getDegree().getDegreeType(); } public boolean isDegreeOrBolonhaDegreeOrBolonhaIntegratedMasterDegree() { return getDegreeType().isDegreeOrBolonhaDegreeOrBolonhaIntegratedMasterDegree(); } public boolean isFirstCycle() { return getDegree().isFirstCycle(); } public CycleCourseGroup getFirstCycleCourseGroup() { return isFirstCycle() ? getRoot().getFirstCycleCourseGroup() : null; } public boolean isSecondCycle() { return getDegree().isSecondCycle(); } public CycleCourseGroup getSecondCycleCourseGroup() { return isSecondCycle() ? getRoot().getSecondCycleCourseGroup() : null; } public CycleCourseGroup getThirdCycleCourseGroup() { return isBolonhaDegree() ? getRoot().getThirdCycleCourseGroup() : null; } public CycleCourseGroup getCycleCourseGroup(final CycleType cycleType) { return isBolonhaDegree() ? getRoot().getCycleCourseGroup(cycleType) : null; } public CycleCourseGroup getLastOrderedCycleCourseGroup() { return isBolonhaDegree() ? getCycleCourseGroup(getDegreeType().getLastOrderedCycleType()) : null; } public String getGraduateTitle(final ExecutionYear executionYear, final ProgramConclusion programConclusion, final Locale locale) { return programConclusion.groupFor(this).map(cg -> cg.getGraduateTitle(executionYear, locale)).orElse(null); } public List<CurricularCourse> getDissertationCurricularCourses(ExecutionYear year) { List<CurricularCourse> result = new ArrayList<CurricularCourse>(); List<ExecutionYear> years = new ArrayList<ExecutionYear>(); if (year == null) { year = ExecutionYear.readCurrentExecutionYear(); while (year != null) { years.add(year); year = year.getPreviousExecutionYear(); } } else { years.add(year); } for (ExecutionYear executionYear : years) { for (CurricularCourse curricularCourse : getCurricularCourses(executionYear)) { if (curricularCourse.isDissertation()) { result.add(curricularCourse); } } } return result; } public Set<Thesis> getThesis(ExecutionYear executionYear) { final Set<Thesis> thesis = new HashSet<Thesis>(); for (CurricularCourse curricularCourse : getDissertationCurricularCourses(executionYear)) { for (Enrolment enrolment : curricularCourse.getEnrolmentsByExecutionYear(executionYear)) { StudentCurricularPlan studentCurricularPlan = enrolment.getStudentCurricularPlan(); if (studentCurricularPlan.getDegreeCurricularPlan() != this) { continue; } thesis.addAll(enrolment.getThesesSet()); } } return thesis; } public Set<Enrolment> getDissertationEnrolments(ExecutionYear executionYear) { final Set<Enrolment> enrolments = new HashSet<Enrolment>(); for (CurricularCourse curricularCourse : getDissertationCurricularCourses(executionYear)) { for (Enrolment enrolment : curricularCourse.getEnrolmentsByExecutionYear(executionYear)) { enrolments.add(enrolment); } } return enrolments; } public List<CurricularCourse> getDissertationCurricularCourses() { return getDissertationCurricularCourses(ExecutionYear.readCurrentExecutionYear()); } public DegreeCurricularPlanEquivalencePlan createEquivalencePlan(final DegreeCurricularPlan sourceDegreeCurricularPlan) { return new DegreeCurricularPlanEquivalencePlan(this, sourceDegreeCurricularPlan); } public boolean hasDegreeModule(final DegreeModule degreeModule) { return getRoot().hasDegreeModule(degreeModule); } public final List<StudentCurricularPlan> getLastStudentCurricularPlan() { List<StudentCurricularPlan> studentCurricularPlans = new ArrayList<StudentCurricularPlan>(); for (StudentCurricularPlan studentCurricularPlan : this.getStudentCurricularPlansSet()) { studentCurricularPlans.add(studentCurricularPlan.getRegistration().getLastStudentCurricularPlan()); } return studentCurricularPlans; } public Set<CourseGroup> getAllCoursesGroups() { final Set<DegreeModule> courseGroups = new TreeSet<DegreeModule>(DegreeModule.COMPARATOR_BY_NAME) { @Override public boolean add(DegreeModule degreeModule) { return degreeModule instanceof CourseGroup && super.add(degreeModule); } }; courseGroups.add(getRoot()); getRoot().getAllDegreeModules(courseGroups); return (Set) courseGroups; } public Set<BranchCourseGroup> getAllBranches() { final Set<DegreeModule> branches = new TreeSet<DegreeModule>(DegreeModule.COMPARATOR_BY_NAME) { @Override public boolean add(DegreeModule degreeModule) { return degreeModule instanceof BranchCourseGroup && super.add(degreeModule); } }; branches.add(getRoot()); getRoot().getAllDegreeModules(branches); return (Set) branches; } public Set<BranchCourseGroup> getBranchesByType(org.fenixedu.academic.domain.degreeStructure.BranchType branchType) { final Set<BranchCourseGroup> branchesByType = new TreeSet<BranchCourseGroup>(DegreeModule.COMPARATOR_BY_NAME); final Set<BranchCourseGroup> branches = getAllBranches(); if (branches == null) { return null; } for (BranchCourseGroup branch : branches) { if (branch.getBranchType() == branchType) { branchesByType.add(branch); } } return branchesByType; } public Set<BranchCourseGroup> getMajorBranches() { return getBranchesByType(org.fenixedu.academic.domain.degreeStructure.BranchType.MAJOR); } public Set<BranchCourseGroup> getMinorBranches() { return getBranchesByType(org.fenixedu.academic.domain.degreeStructure.BranchType.MINOR); } public boolean hasBranches() { return getAllBranches().isEmpty() ? false : true; } public boolean hasBranchesByType(org.fenixedu.academic.domain.degreeStructure.BranchType branchType) { return getBranchesByType(branchType).isEmpty() ? false : true; } public Set<DegreeModule> getAllDegreeModules() { final Set<DegreeModule> degreeModules = new TreeSet<DegreeModule>(DegreeModule.COMPARATOR_BY_NAME); final RootCourseGroup rootCourseGroup = getRoot(); rootCourseGroup.getAllDegreeModules(degreeModules); return degreeModules; } public static Set<DegreeCurricularPlan> getDegreeCurricularPlans(java.util.function.Predicate<DegreeType> predicate) { final Set<DegreeCurricularPlan> degreeCurricularPlans = new TreeSet<DegreeCurricularPlan>(DegreeCurricularPlan.COMPARATOR_BY_PRESENTATION_NAME); for (final Degree degree : Degree.readNotEmptyDegrees()) { if (predicate.test(degree.getDegreeType())) { for (final DegreeCurricularPlan degreeCurricularPlan : degree.getDegreeCurricularPlansSet()) { if (degreeCurricularPlan.isActive()) { degreeCurricularPlans.add(degreeCurricularPlan); } } } } return degreeCurricularPlans; } public List<StudentCurricularPlan> getStudentsCurricularPlanGivenEntryYear(ExecutionYear entryYear) { List<StudentCurricularPlan> studentsGivenEntryYear = new ArrayList<StudentCurricularPlan>(); ExecutionYear currentExecutionYear = ExecutionYear.readCurrentExecutionYear(); for (Registration registration : this.getActiveRegistrations()) { if (registration.getStartDate() != null && registration.getStartExecutionYear().equals(entryYear) && registration.isRegistered(currentExecutionYear) && !registration.getRegistrationProtocol().isMobilityAgreement()) { studentsGivenEntryYear.add(registration.getActiveStudentCurricularPlan()); } } return studentsGivenEntryYear; } public Set<CurricularCourse> getCurricularCoursesByExecutionYearAndCurricularYear(ExecutionYear executionYear, Integer curricularYear) { Set<CurricularCourse> result = new HashSet<CurricularCourse>(); for (final CurricularCourse curricularCourse : getCurricularCoursesWithExecutionIn(executionYear)) { for (final DegreeModuleScope degreeModuleScope : curricularCourse.getDegreeModuleScopes()) { if (degreeModuleScope.getCurricularYear().equals(curricularYear)) { result.add(curricularCourse); } } } return result; } /** * This must be completely refactored. A pattern of some sort is desirable * in order to make this instance-dependent. Just did this due to time * constrains. */ public Set<Registration> getRegistrations(ExecutionYear executionYear, Set<Registration> registrations) { for (final StudentCurricularPlan studentCurricularPlan : this.getStudentCurricularPlansSet()) { if (studentCurricularPlan.isActive(executionYear)) { if (studentCurricularPlan.getRegistration() != null) { registrations.add(studentCurricularPlan.getRegistration()); } } } return registrations; } public List<StudentCurricularPlan> getStudentsCurricularPlans(ExecutionYear executionYear, List<StudentCurricularPlan> result) { for (final StudentCurricularPlan studentCurricularPlan : this.getStudentCurricularPlansSet()) { if (studentCurricularPlan.isActive(executionYear)) { result.add(studentCurricularPlan); } } return result; } public boolean isToApplyPreviousYearsEnrolmentRule() { return getApplyPreviousYearsEnrolmentRule(); } public ExecutionSemester getFirstExecutionPeriodEnrolments() { return ExecutionSemester.readFirstEnrolmentsExecutionPeriod(); } public boolean hasTargetEquivalencePlanFor(final DegreeCurricularPlan degreeCurricularPlan) { for (final DegreeCurricularPlanEquivalencePlan equivalencePlan : getTargetEquivalencePlansSet()) { if (equivalencePlan.getDegreeCurricularPlan().equals(degreeCurricularPlan)) { return true; } } return false; } public boolean canSubmitImprovementMarkSheets(final ExecutionYear executionYear) { SortedSet<ExecutionDegree> sortedExecutionDegrees = new TreeSet<ExecutionDegree>(ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR); sortedExecutionDegrees.addAll(getExecutionDegreesSet()); return sortedExecutionDegrees.last().getExecutionYear().equals(executionYear.getPreviousExecutionYear()); } public ExecutionInterval getBegin() { Set<ExecutionYear> beginContextExecutionYears = getBeginContextExecutionYears(); return beginContextExecutionYears.isEmpty() ? null : Collections.min(beginContextExecutionYears, ExecutionYear.COMPARATOR_BY_YEAR); } public Set<ExecutionYear> getBeginContextExecutionYears() { return getRoot().getBeginContextExecutionYears(); } public ExecutionYear getOldestContextExecutionYear() { List<ExecutionYear> beginContextExecutionYears = new ArrayList<ExecutionYear>(getBeginContextExecutionYears()); Collections.sort(beginContextExecutionYears, ExecutionYear.COMPARATOR_BY_YEAR); return beginContextExecutionYears.isEmpty() ? null : beginContextExecutionYears.iterator().next(); } public MultiLanguageString getDescriptionI18N() { MultiLanguageString result = new MultiLanguageString(); if (!StringUtils.isEmpty(getDescription())) { result = result.with(MultiLanguageString.pt, getDescription()); } if (!StringUtils.isEmpty(getDescriptionEn())) { result = result.with(MultiLanguageString.en, getDescriptionEn()); } return result; } public Collection<CycleCourseGroup> getDestinationAffinities(final CycleType sourceCycleType) { final CycleCourseGroup cycleCourseGroup = getRoot().getCycleCourseGroup(sourceCycleType); if (cycleCourseGroup != null) { return cycleCourseGroup.getDestinationAffinitiesSet(); } return Collections.EMPTY_LIST; } public Double getEctsCredits() { return getDegree().getEctsCredits(); } public boolean isScientificCommissionMember(final ExecutionYear executionYear) { final ExecutionDegree executionDegree = getExecutionDegreeByYear(executionYear); return executionDegree != null && executionDegree.isScientificCommissionMember(); } public boolean isScientificCommissionMember(final ExecutionYear executionYear, final Person person) { final ExecutionDegree executionDegree = getExecutionDegreeByYear(executionYear); return executionDegree != null && executionDegree.isScientificCommissionMember(person); } public void checkUserIsScientificCommissionMember(final ExecutionYear executionYear) { if (!isScientificCommissionMember(executionYear)) { throw new DomainException("degree.scientificCommission.notMember"); } } public static List<DegreeCurricularPlan> readByDegreeTypesAndStateWithExecutionDegreeForYear( java.util.function.Predicate<DegreeType> degreeTypes, DegreeCurricularPlanState state, ExecutionYear executionYear) { final List<DegreeCurricularPlan> result = new ArrayList<DegreeCurricularPlan>(); for (final DegreeCurricularPlan degreeCurricularPlan : readByDegreeTypesAndState(degreeTypes, state)) { if (degreeCurricularPlan.hasExecutionDegreeFor(executionYear)) { result.add(degreeCurricularPlan); } } return result; } public static Collection<DegreeCurricularPlan> readByAcademicInterval(AcademicInterval academicInterval) { ExecutionYear year = ExecutionYear.readByAcademicInterval(academicInterval); return year.getDegreeCurricularPlans(); } public boolean isCurrentUserScientificCommissionMember(final ExecutionYear executionYear) { final Person person = AccessControl.getPerson(); return person != null && getDegree().isMemberOfCurrentScientificCommission(person, executionYear); } public ExecutionYear getInauguralExecutionYear() { return getExecutionDegreesSet().stream().min(ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR) .map(ExecutionDegree::getExecutionYear).orElse(null); } public ExecutionYear getLastExecutionYear() { return getExecutionDegreesSet().stream().max(ExecutionDegree.EXECUTION_DEGREE_COMPARATORY_BY_YEAR) .map(ExecutionDegree::getExecutionYear).orElse(null); } @Deprecated public java.util.Date getEndDate() { org.joda.time.YearMonthDay ymd = getEndDateYearMonthDay(); return ymd == null ? null : new java.util.Date(ymd.getYear() - 1900, ymd.getMonthOfYear() - 1, ymd.getDayOfMonth()); } @Deprecated public void setEndDate(java.util.Date date) { if (date == null) { setEndDateYearMonthDay(null); } else { setEndDateYearMonthDay(org.joda.time.YearMonthDay.fromDateFields(date)); } } @Deprecated public java.util.Date getInitialDate() { org.joda.time.YearMonthDay ymd = getInitialDateYearMonthDay(); return ymd == null ? null : new java.util.Date(ymd.getYear() - 1900, ymd.getMonthOfYear() - 1, ymd.getDayOfMonth()); } @Deprecated public void setInitialDate(java.util.Date date) { if (date == null) { setInitialDateYearMonthDay(null); } else { setInitialDateYearMonthDay(org.joda.time.YearMonthDay.fromDateFields(date)); } } public Set<CurricularCourse> getCurricularCoursesWithoutExecutionCourseFor(final ExecutionSemester semester) { Set<CurricularCourse> curricularCourses = getCurricularCourses(semester); Set<CurricularCourse> result = new HashSet<CurricularCourse>(); for (CurricularCourse curricularCourse : curricularCourses) { if (curricularCourse.getExecutionCoursesByExecutionPeriod(semester).isEmpty()) { result.add(curricularCourse); } } return result; } @Deprecated public java.util.Set<org.fenixedu.academic.domain.DegreeCurricularPlanEquivalencePlan> getTargetEquivalencePlans() { return getTargetEquivalencePlansSet(); } public int getDurationInYears() { if (getDegreeStructure() != null) { return Float.valueOf(getDegreeStructure().getAcademicPeriod().getWeight()).intValue(); } return 0; } public int getDurationInSemesters() { return Float.valueOf(getDurationInYears() / AcademicPeriod.SEMESTER.getWeight()).intValue(); } public int getDurationInYears(final CycleType cycleType) { if (cycleType == null || getDegreeType().hasExactlyOneCycleType()) { return getDurationInYears(); } if (!getDegreeType().hasAnyCycleTypes()) { return 0; } return calculateCycleDuration(cycleType, ctx -> ctx.getCurricularPeriod().getParent(), cp -> cp.getAcademicPeriod() .equals(AcademicPeriod.YEAR)); } public int getDurationInSemesters(final CycleType cycleType) { return Float.valueOf(getDurationInYears(cycleType) / AcademicPeriod.SEMESTER.getWeight()).intValue(); } private int calculateCycleDuration(CycleType cycleType, Function<Context, CurricularPeriod> curricularPeriodCollector, java.util.function.Predicate<CurricularPeriod> curricularPeriodFilter) { final CycleCourseGroup cycleCourseGroup = getRoot().getCycleCourseGroup(cycleType); if (cycleCourseGroup == null) { //structure is not correct throw new DomainException("error.degreeCurricularPlan.unable.to.find.cycle.in.structure.to.calculate.duration", cycleType.getDescription()); } return Math.toIntExact(getAllCoursesGroups().stream().filter(cg -> cg.getParentCycleCourseGroups().contains(cycleCourseGroup)) .flatMap(cg -> cg.getChildContextsSet().stream()).filter(ctx -> ctx.getChildDegreeModule().isLeaf()) .map(curricularPeriodCollector).filter(curricularPeriodFilter).count()); } @Atomic public void editDuration(AcademicPeriod duration) { if (duration == null) { throw new DomainException("error.degreeCurricularPlan.duration.cannot.be.null"); } if (!getDegreeStructure().getAcademicPeriod().equals(duration)) { setDegreeStructure(new CurricularPeriod(duration)); } } }