/** * 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.dto.resourceAllocationManager; import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.stream.Collectors; import java.util.stream.StreamSupport; import org.fenixedu.academic.domain.CurricularYearList; import org.fenixedu.academic.domain.ExecutionDegree; import org.fenixedu.academic.domain.ExecutionSemester; import org.fenixedu.academic.domain.OccupationPeriod; import org.fenixedu.academic.domain.OccupationPeriodReference; import org.fenixedu.academic.domain.OccupationPeriodType; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.util.Bundle; import org.fenixedu.academic.util.date.IntervalTools; import org.fenixedu.bennu.core.i18n.BundleUtil; import org.fenixedu.commons.i18n.I18N; import org.joda.time.DateTime; import org.joda.time.Interval; import org.joda.time.LocalDate; import org.joda.time.format.DateTimeFormat; import org.joda.time.format.DateTimeFormatter; import pt.ist.fenixframework.Atomic; import pt.ist.fenixframework.FenixFramework; import com.google.common.base.Preconditions; import com.google.common.base.Splitter; import com.google.common.collect.ComparisonChain; import com.google.common.collect.Lists; /** * @author Joao Carvalho (joao.pedro.carvalho@ist.utl.pt) * */ public class OccupationPeriodBean implements Serializable, Comparable<OccupationPeriodBean> { /** * */ private static final long serialVersionUID = -9196229454898126127L; private List<Interval> intervals = new ArrayList<Interval>(); private OccupationPeriodType occupationPeriodType; private OccupationPeriod occupationPeriod; private Integer semester; private List<OccupationPeriodReference> references = new ArrayList<OccupationPeriodReference>(); private final int id; public OccupationPeriodBean(int id) { this.id = id; this.semester = ExecutionSemester.readActualExecutionSemester().getSemester(); this.occupationPeriodType = OccupationPeriodType.LESSONS; this.intervals = Lists.newArrayList(new Interval(new DateTime(), new DateTime().plusDays(1))); } public OccupationPeriodBean(OccupationPeriodReference reference, int id) { this.occupationPeriod = reference.getOccupationPeriod(); reloadIntervals(); this.semester = reference.getSemester(); this.occupationPeriodType = reference.getPeriodType(); this.id = id; } private void reloadIntervals() { this.intervals = occupationPeriod.getIntervals(); } public int getId() { return id; } public List<Interval> getIntervals() { return intervals; } public void setIntervals(List<Interval> intervals) { this.intervals = intervals; } public OccupationPeriodType getOccupationPeriodType() { return occupationPeriodType; } public void setOccupationPeriodType(OccupationPeriodType occupationPeriodType) { this.occupationPeriodType = occupationPeriodType; } public OccupationPeriod getOccupationPeriod() { return occupationPeriod; } public void setOccupationPeriod(OccupationPeriod occupationPeriod) { this.occupationPeriod = occupationPeriod; } public List<OccupationPeriodReference> getReferences() { return references; } public void setReferences(List<OccupationPeriodReference> references) { this.references = references; } public void addReference(OccupationPeriodReference reference) { references.add(reference); } public Integer getSemester() { return semester; } public void setSemester(Integer semester) { this.semester = semester; } // Note: If there ever is a need to create more than one semester, this is // the place it should be done! public Collection<Integer> getPossibleSemesters() { return Lists.newArrayList(1, 2); } public boolean getNewObject() { return occupationPeriod == null; } // Presentation Utility Methods public String getDatesString() { if (intervals.size() == 0 || occupationPeriod == null) { return BundleUtil.getString(Bundle.RESOURCE_ALLOCATION, "label.periods.no.dates"); } DateTimeFormatter formatter = DateTimeFormat.forPattern("dd MMM").withLocale(I18N.getLocale()); StringBuilder builder = new StringBuilder(); for (Interval interval : getIntervals()) { if (builder.length() > 0) { builder.append(", "); } builder.append(formatter.print(interval.getStart())); builder.append(" - "); builder.append(formatter.print(interval.getEnd())); } return builder.toString(); } public String getName() { if (occupationPeriod == null) { return "Novo período"; } return "Período de " + occupationPeriodType.getLocalizedName() + (semester == null ? "" : " - " + semester + "º Semestre"); } @Override public int compareTo(OccupationPeriodBean other) { return ComparisonChain .start() .compare(this.getOccupationPeriod().getPeriodInterval().getStartMillis(), other.getOccupationPeriod().getPeriodInterval().getStartMillis()) .compare(this.getReferences().size(), this.getReferences().size()).result(); } // Actual bean operations @Atomic public void updateDates(String parameter) { Iterable<Interval> intervals = extractIntervals(parameter); consolidateReferences(); // The occupation period is shared by multiple types, a new one must be // created! if (this.occupationPeriod.getExecutionDegreesSet().size() != getReferences().size()) { this.occupationPeriod = new OccupationPeriod(intervals.iterator()); // Period has changed, lets change the references so they point to // the new period. consolidateReferences(); } else { this.occupationPeriod.editDates(intervals.iterator()); } reloadIntervals(); } @Atomic public void updateCourses(String parameter) { Map<ExecutionDegree, CurricularYearList> degreeMap = extractCourses(parameter); // Step 1, Remove non-existing references for (Iterator<OccupationPeriodReference> references = getReferences().iterator(); references.hasNext();) { OccupationPeriodReference reference = references.next(); if (!degreeMap.containsKey(reference.getExecutionDegree())) { reference.delete(); references.remove(); } } // Step 2, Add new references, and update old ones for (final Entry<ExecutionDegree, CurricularYearList> entry : degreeMap.entrySet()) { OccupationPeriodReference reference = null; for (OccupationPeriodReference ref : getReferences()) { if (ref.getExecutionDegree().equals(entry.getKey())) { reference = ref; break; } } if (reference == null) { references.add(new OccupationPeriodReference(occupationPeriod, entry.getKey(), occupationPeriodType, semester, entry.getValue())); } else { reference.setOccupationPeriod(occupationPeriod); reference.setPeriodType(occupationPeriodType); reference.setSemester(semester); reference.setCurricularYears(entry.getValue()); } } } @Atomic public void deletePeriod() { for (OccupationPeriodReference ref : getReferences()) { ref.delete(); } } @Atomic public void create(String intervalsStr, String courses) { Iterable<Interval> intervals = extractIntervals(intervalsStr); this.occupationPeriod = new OccupationPeriod(intervals.iterator()); updateCourses(courses); reloadIntervals(); } // Private Utility Methods private static final Splitter SPLITTER = Splitter.on(';').trimResults().omitEmptyStrings(); private static final DateTimeFormatter FORMATTER = DateTimeFormat.forPattern("dd/MM/yyyy"); private void consolidateReferences() { Preconditions.checkNotNull(occupationPeriod); for (OccupationPeriodReference reference : references) { reference.setOccupationPeriod(occupationPeriod); } } private Iterable<Interval> extractIntervals(String parameter) { Iterable<Interval> intervals = StreamSupport.stream(SPLITTER.split(parameter).spliterator(), false).map(string -> { String[] dates = string.split(","); if (dates.length != 2) { throw new RuntimeException("Error while recreating intervals, '" + string + "' cannot be parsed!"); } LocalDate start = FORMATTER.parseDateTime(dates[0]).toLocalDate(); LocalDate end = FORMATTER.parseDateTime(dates[1]).toLocalDate(); return IntervalTools.getInterval(start, end); }).collect(Collectors.toList()); Iterator<Interval> iter = intervals.iterator(); Interval last = iter.next(); while (iter.hasNext()) { Interval current = iter.next(); if (!current.isAfter(last)) { throw new DomainException("label.occupation.period.invalid.dates"); } last = current; } return intervals; } private Map<ExecutionDegree, CurricularYearList> extractCourses(String parameter) { Map<ExecutionDegree, CurricularYearList> degreeMap = new HashMap<ExecutionDegree, CurricularYearList>(); for (String string : SPLITTER.split(parameter)) { String[] parts = string.split(":"); if (parts.length != 2) { throw new RuntimeException("Error while recreating execution degree, '" + string + "' cannot be parsed!"); } String oid = parts[0]; ExecutionDegree degree = FenixFramework.getDomainObject(oid); degreeMap.put(degree, CurricularYearList.internalize(parts[1])); } return degreeMap; } @Atomic public OccupationPeriodBean duplicate(int newId, OccupationPeriodType newPeriodType) { if (newPeriodType == null) { throw new DomainException("label.occupation.period.duplicate.message"); } OccupationPeriodBean newBean = new OccupationPeriodBean(newId); newBean.setOccupationPeriodType(newPeriodType); newBean.setIntervals(intervals); newBean.setOccupationPeriod(occupationPeriod); newBean.setSemester(semester); for (OccupationPeriodReference reference : references) { newBean.addReference(new OccupationPeriodReference(reference.getOccupationPeriod(), reference.getExecutionDegree(), newPeriodType, semester, reference.getCurricularYears())); } return newBean; } }