/**
* 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.math.BigDecimal;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.lang.StringUtils;
import org.fenixedu.academic.domain.degree.DegreeType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.person.RoleType;
import org.fenixedu.academic.domain.student.Registration;
import org.fenixedu.academic.domain.util.email.ExecutionCourseSender;
import org.fenixedu.academic.domain.util.email.Message;
import org.fenixedu.academic.domain.util.email.Recipient;
import org.fenixedu.academic.predicate.AccessControl;
import org.fenixedu.academic.predicate.ResourceAllocationRolePredicates;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.academic.util.DiaSemana;
import org.fenixedu.academic.util.WeekDay;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.core.groups.UserGroup;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.joda.time.Duration;
import pt.ist.fenixframework.Atomic;
import pt.ist.fenixframework.dml.runtime.RelationAdapter;
public class Shift extends Shift_Base {
public static final Comparator<Shift> SHIFT_COMPARATOR_BY_NAME = new Comparator<Shift>() {
@Override
public int compare(Shift o1, Shift o2) {
return Collator.getInstance().compare(o1.getNome(), o2.getNome());
}
};
public static final Comparator<Shift> SHIFT_COMPARATOR_BY_TYPE_AND_ORDERED_LESSONS = new Comparator<Shift>() {
@Override
public int compare(Shift o1, Shift o2) {
final int ce = o1.getExecutionCourse().getNome().compareTo(o2.getExecutionCourse().getNome());
if (ce != 0) {
return ce;
}
final int cs = o1.getShiftTypesIntegerComparator().compareTo(o2.getShiftTypesIntegerComparator());
if (cs != 0) {
return cs;
}
final int cl = o1.getLessonsStringComparator().compareTo(o2.getLessonsStringComparator());
return cl == 0 ? DomainObjectUtil.COMPARATOR_BY_ID.compare(o1, o2) : cl;
}
};
static {
Registration.getRelationShiftStudent().addListener(new ShiftStudentListener());
}
public Shift(final ExecutionCourse executionCourse, Collection<ShiftType> types, final Integer lotacao) {
// check(this, ResourceAllocationRolePredicates.checkPermissionsToManageShifts);
super();
setRootDomainObject(Bennu.getInstance());
shiftTypeManagement(types, executionCourse);
setLotacao(lotacao);
executionCourse.setShiftNames();
if (getCourseLoadsSet().isEmpty()) {
throw new DomainException("error.Shift.empty.courseLoads");
}
}
public void edit(List<ShiftType> newTypes, Integer newCapacity, ExecutionCourse newExecutionCourse, String newName,
String comment) {
check(this, ResourceAllocationRolePredicates.checkPermissionsToManageShifts);
ExecutionCourse beforeExecutionCourse = getExecutionCourse();
final Shift otherShiftWithSameNewName = newExecutionCourse.findShiftByName(newName);
if (otherShiftWithSameNewName != null && otherShiftWithSameNewName != this) {
throw new DomainException("error.Shift.with.this.name.already.exists");
}
if (newCapacity != null && getStudentsSet().size() > newCapacity.intValue()) {
throw new DomainException("errors.exception.invalid.finalAvailability");
}
setLotacao(newCapacity);
shiftTypeManagement(newTypes, newExecutionCourse);
beforeExecutionCourse.setShiftNames();
if (!beforeExecutionCourse.equals(newExecutionCourse)) {
newExecutionCourse.setShiftNames();
}
if (getCourseLoadsSet().isEmpty()) {
throw new DomainException("error.Shift.empty.courseLoads");
}
setComment(comment);
}
@Override
public Set<StudentGroup> getAssociatedStudentGroupsSet() {
Set<StudentGroup> result = new HashSet<StudentGroup>();
for (StudentGroup sg : super.getAssociatedStudentGroupsSet()) {
if (sg.getValid()) {
result.add(sg);
}
}
return Collections.unmodifiableSet(result);
}
public void delete() {
check(this, ResourceAllocationRolePredicates.checkPermissionsToManageShifts);
DomainException.throwWhenDeleteBlocked(getDeletionBlockers());
final ExecutionCourse executionCourse = getExecutionCourse();
for (; !getAssociatedLessonsSet().isEmpty(); getAssociatedLessonsSet().iterator().next().delete()) {
;
}
for (; !getAssociatedShiftProfessorshipSet().isEmpty(); getAssociatedShiftProfessorshipSet().iterator().next().delete()) {
;
}
for (; !getShiftDistributionEntriesSet().isEmpty(); getShiftDistributionEntriesSet().iterator().next().delete()) {
;
}
getAssociatedClassesSet().clear();
getCourseLoadsSet().clear();
if (getShiftGroupingProperties() != null) {
getShiftGroupingProperties().delete();
}
setRootDomainObject(null);
super.deleteDomainObject();
executionCourse.setShiftNames();
}
@jvstm.cps.ConsistencyPredicate
protected boolean checkRequiredParameters() {
return getLotacao() != null && !StringUtils.isEmpty(getNome());
}
@Deprecated
public ExecutionCourse getDisciplinaExecucao() {
return getExecutionCourse();
}
public ExecutionCourse getExecutionCourse() {
CourseLoad courseLoad = getCourseLoadsSet().iterator().next();
if (courseLoad != null) {
return courseLoad.getExecutionCourse();
} else {
return null;
}
}
public ExecutionSemester getExecutionPeriod() {
return getExecutionCourse().getExecutionPeriod();
}
private void shiftTypeManagement(Collection<ShiftType> types, ExecutionCourse executionCourse) {
if (executionCourse != null) {
getCourseLoadsSet().clear();
for (ShiftType shiftType : types) {
CourseLoad courseLoad = executionCourse.getCourseLoadByShiftType(shiftType);
if (courseLoad != null) {
addCourseLoads(courseLoad);
}
}
}
}
public List<ShiftType> getTypes() {
List<ShiftType> result = new ArrayList<ShiftType>();
for (CourseLoad courseLoad : getCourseLoadsSet()) {
result.add(courseLoad.getType());
}
return result;
}
public SortedSet<ShiftType> getSortedTypes() {
SortedSet<ShiftType> result = new TreeSet<ShiftType>();
for (CourseLoad courseLoad : getCourseLoadsSet()) {
result.add(courseLoad.getType());
}
return result;
}
public boolean containsType(ShiftType shiftType) {
if (shiftType != null) {
for (CourseLoad courseLoad : getCourseLoadsSet()) {
if (courseLoad.getType().equals(shiftType)) {
return true;
}
}
}
return false;
}
@Override
protected void checkForDeletionBlockers(Collection<String> blockers) {
super.checkForDeletionBlockers(blockers);
if (!getAssociatedStudentGroupsSet().isEmpty()) {
blockers.add(BundleUtil.getString(Bundle.RESOURCE_ALLOCATION, "error.deleteShift.with.studentGroups", getNome()));
}
if (!getStudentsSet().isEmpty()) {
blockers.add(BundleUtil.getString(Bundle.RESOURCE_ALLOCATION, "error.deleteShift.with.students", getNome()));
}
if (!getAssociatedSummariesSet().isEmpty()) {
blockers.add(BundleUtil.getString(Bundle.RESOURCE_ALLOCATION, "error.deleteShift.with.summaries", getNome()));
}
}
public BigDecimal getTotalHours() {
Collection<Lesson> lessons = getAssociatedLessonsSet();
BigDecimal lessonTotalHours = BigDecimal.ZERO;
for (Lesson lesson : lessons) {
lessonTotalHours = lessonTotalHours.add(lesson.getTotalHours());
}
return lessonTotalHours;
}
public Duration getTotalDuration() {
Duration duration = Duration.ZERO;
Collection<Lesson> lessons = getAssociatedLessonsSet();
for (Lesson lesson : lessons) {
duration = duration.plus(lesson.getTotalDuration());
}
return duration;
}
public BigDecimal getMaxLessonDuration() {
BigDecimal maxHours = BigDecimal.ZERO;
for (Lesson lesson : getAssociatedLessonsSet()) {
BigDecimal lessonHours = lesson.getUnitHours();
if (maxHours.compareTo(lessonHours) == -1) {
maxHours = lessonHours;
}
}
return maxHours;
}
public BigDecimal getUnitHours() {
BigDecimal hours = BigDecimal.ZERO;
Collection<Lesson> lessons = getAssociatedLessonsSet();
for (Lesson lesson : lessons) {
hours = hours.add(lesson.getUnitHours());
}
return hours;
}
public double getHoursOnSaturdaysOrNightHours(int nightHour) {
double hours = 0;
Collection<Lesson> lessons = this.getAssociatedLessonsSet();
for (Lesson lesson : lessons) {
if (lesson.getDiaSemana().equals(new DiaSemana(DiaSemana.SABADO))) {
hours += lesson.getUnitHours().doubleValue();
} else {
hours += lesson.hoursAfter(nightHour);
}
}
return hours;
}
public int getNumberOfLessonInstances() {
Collection<Lesson> lessons = getAssociatedLessonsSet();
int totalLessonsDates = 0;
for (Lesson lesson : lessons) {
totalLessonsDates += lesson.getFinalNumberOfLessonInstances();
}
return totalLessonsDates;
}
public BigDecimal getCourseLoadWeeklyAverage() {
BigDecimal weeklyHours = BigDecimal.ZERO;
for (CourseLoad courseLoad : getCourseLoadsSet()) {
weeklyHours = weeklyHours.add(courseLoad.getWeeklyHours());
}
return weeklyHours;
}
public BigDecimal getCourseLoadTotalHours() {
BigDecimal weeklyHours = BigDecimal.ZERO;
for (CourseLoad courseLoad : getCourseLoadsSet()) {
weeklyHours = weeklyHours.add(courseLoad.getTotalQuantity());
}
return weeklyHours;
}
public void associateSchoolClass(SchoolClass schoolClass) {
if (schoolClass == null) {
throw new NullPointerException();
}
if (!this.getAssociatedClassesSet().contains(schoolClass)) {
this.getAssociatedClassesSet().add(schoolClass);
}
if (!schoolClass.getAssociatedShiftsSet().contains(this)) {
schoolClass.getAssociatedShiftsSet().add(this);
}
}
public SortedSet<Lesson> getLessonsOrderedByWeekDayAndStartTime() {
final SortedSet<Lesson> lessons = new TreeSet<Lesson>(Lesson.LESSON_COMPARATOR_BY_WEEKDAY_AND_STARTTIME);
lessons.addAll(getAssociatedLessonsSet());
return lessons;
}
public String getLessonsStringComparator() {
final StringBuilder stringBuilder = new StringBuilder();
for (final Lesson lesson : getLessonsOrderedByWeekDayAndStartTime()) {
stringBuilder.append(lesson.getDiaSemana().getDiaSemana().toString());
stringBuilder.append(lesson.getBeginHourMinuteSecond().toString());
}
return stringBuilder.toString();
}
public Integer getShiftTypesIntegerComparator() {
final StringBuilder stringBuilder = new StringBuilder();
for (ShiftType shiftType : getSortedTypes()) {
stringBuilder.append(shiftType.ordinal() + 1);
}
return Integer.valueOf(stringBuilder.toString());
}
public boolean reserveForStudent(final Registration registration) {
final boolean result = getLotacao().intValue() > getStudentsSet().size();
if (result || isResourceAllocationManager()) {
GroupsAndShiftsManagementLog.createLog(getExecutionCourse(), Bundle.MESSAGING,
"log.executionCourse.groupAndShifts.shifts.attends.added", registration.getNumber().toString(), getNome(),
getExecutionCourse().getNome(), getExecutionCourse().getDegreePresentationString());
addStudents(registration);
}
return result;
}
private boolean isResourceAllocationManager() {
final Person person = AccessControl.getPerson();
return person != null && person.hasRole(RoleType.RESOURCE_ALLOCATION_MANAGER);
}
public SortedSet<ShiftEnrolment> getShiftEnrolmentsOrderedByDate() {
final SortedSet<ShiftEnrolment> shiftEnrolments = new TreeSet<ShiftEnrolment>(ShiftEnrolment.COMPARATOR_BY_DATE);
shiftEnrolments.addAll(getShiftEnrolmentsSet());
return shiftEnrolments;
}
public String getClassesPrettyPrint() {
StringBuilder builder = new StringBuilder();
int index = 0;
for (SchoolClass schoolClass : getAssociatedClassesSet()) {
builder.append(schoolClass.getNome());
index++;
if (index < getAssociatedClassesSet().size()) {
builder.append(", ");
}
}
return builder.toString();
}
public String getShiftTypesPrettyPrint() {
StringBuilder builder = new StringBuilder();
int index = 0;
SortedSet<ShiftType> sortedTypes = getSortedTypes();
for (ShiftType shiftType : sortedTypes) {
builder.append(BundleUtil.getString(Bundle.ENUMERATION, shiftType.getName()));
index++;
if (index < sortedTypes.size()) {
builder.append(", ");
}
}
return builder.toString();
}
public String getShiftTypesCapitalizedPrettyPrint() {
StringBuilder builder = new StringBuilder();
int index = 0;
SortedSet<ShiftType> sortedTypes = getSortedTypes();
for (ShiftType shiftType : sortedTypes) {
builder.append(shiftType.getFullNameTipoAula());
index++;
if (index < sortedTypes.size()) {
builder.append(", ");
}
}
return builder.toString();
}
public String getShiftTypesCodePrettyPrint() {
StringBuilder builder = new StringBuilder();
int index = 0;
SortedSet<ShiftType> sortedTypes = getSortedTypes();
for (ShiftType shiftType : sortedTypes) {
builder.append(shiftType.getSiglaTipoAula());
index++;
if (index < sortedTypes.size()) {
builder.append(", ");
}
}
return builder.toString();
}
public List<Summary> getExtraSummaries() {
List<Summary> result = new ArrayList<Summary>();
Set<Summary> summaries = getAssociatedSummariesSet();
for (Summary summary : summaries) {
if (summary.isExtraSummary()) {
result.add(summary);
}
}
return result;
}
private static class ShiftStudentListener extends RelationAdapter<Registration, Shift> {
@Override
public void afterAdd(Registration registration, Shift shift) {
if (!shift.hasShiftEnrolment(registration)) {
new ShiftEnrolment(shift, registration);
}
}
@Override
public void afterRemove(Registration registration, Shift shift) {
shift.unEnrolStudent(registration);
}
}
private boolean hasShiftEnrolment(final Registration registration) {
for (final ShiftEnrolment shiftEnrolment : getShiftEnrolmentsSet()) {
if (shiftEnrolment.hasRegistration(registration)) {
return true;
}
}
return false;
}
public void unEnrolStudent(final Registration registration) {
final ShiftEnrolment shiftEnrolment = findShiftEnrolment(registration);
if (shiftEnrolment != null) {
shiftEnrolment.delete();
}
}
private ShiftEnrolment findShiftEnrolment(final Registration registration) {
for (final ShiftEnrolment shiftEnrolment : getShiftEnrolmentsSet()) {
if (shiftEnrolment.getRegistration() == registration) {
return shiftEnrolment;
}
}
return null;
}
public int getCapacityBasedOnSmallestRoom() {
int capacity =
getAssociatedLessonsSet().stream().filter(Lesson::hasSala)
.mapToInt(lesson -> lesson.getSala().getAllocatableCapacity()).min().orElse(0);
return capacity + (capacity / 10);
}
public boolean hasShiftType(final ShiftType shiftType) {
for (CourseLoad courseLoad : getCourseLoadsSet()) {
if (courseLoad.getType() == shiftType) {
return true;
}
}
return false;
}
public boolean hasSchoolClassForDegreeType(DegreeType degreeType) {
for (SchoolClass schoolClass : getAssociatedClassesSet()) {
if (schoolClass.getExecutionDegree().getDegreeType() == degreeType) {
return true;
}
}
return false;
}
@Atomic
public void removeAttendFromShift(Registration registration, ExecutionCourse executionCourse) {
GroupsAndShiftsManagementLog.createLog(getExecutionCourse(), Bundle.MESSAGING,
"log.executionCourse.groupAndShifts.shifts.attends.removed", registration.getNumber().toString(), getNome(),
getExecutionCourse().getNome(), getExecutionCourse().getDegreePresentationString());
registration.removeShifts(this);
ExecutionCourseSender sender = ExecutionCourseSender.newInstance(executionCourse);
Collection<Recipient> recipients =
Collections.singletonList(new Recipient(UserGroup.of(registration.getPerson().getUser())));
final String subject = BundleUtil.getString(Bundle.APPLICATION, "label.shift.remove.subject");
final String body = BundleUtil.getString(Bundle.APPLICATION, "label.shift.remove.body", getNome());
new Message(sender, sender.getConcreteReplyTos(), recipients, subject, body, "");
}
public boolean hasAnyStudentsInAssociatedStudentGroups() {
for (final StudentGroup studentGroup : getAssociatedStudentGroupsSet()) {
if (studentGroup.getAttendsSet().size() > 0) {
return true;
}
}
return false;
}
public String getPresentationName() {
StringBuilder stringBuilder = new StringBuilder(this.getNome());
if (!this.getAssociatedLessonsSet().isEmpty()) {
stringBuilder.append(" ( ");
for (Iterator<Lesson> iterator = this.getAssociatedLessonsSet().iterator(); iterator.hasNext();) {
Lesson lesson = iterator.next();
stringBuilder.append(WeekDay.getWeekDay(lesson.getDiaSemana()).getLabelShort());
stringBuilder.append(" ");
stringBuilder.append(lesson.getBeginHourMinuteSecond().toString("HH:mm"));
stringBuilder.append(" - ");
stringBuilder.append(lesson.getEndHourMinuteSecond().toString("HH:mm"));
if (lesson.hasSala()) {
stringBuilder.append(" - ");
stringBuilder.append(lesson.getSala().getName());
}
if (iterator.hasNext()) {
stringBuilder.append(" ; ");
}
}
stringBuilder.append(" ) ");
}
return stringBuilder.toString();
}
public String getLessonPresentationString() {
StringBuilder stringBuilder = new StringBuilder(this.getNome());
if (!this.getAssociatedLessonsSet().isEmpty()) {
for (Iterator<Lesson> iterator = this.getAssociatedLessonsSet().iterator(); iterator.hasNext();) {
Lesson lesson = iterator.next();
stringBuilder.append(" ");
stringBuilder.append(WeekDay.getWeekDay(lesson.getDiaSemana()).getLabelShort());
stringBuilder.append(" ");
stringBuilder.append(lesson.getBeginHourMinuteSecond().toString("HH:mm"));
stringBuilder.append(" - ");
stringBuilder.append(lesson.getEndHourMinuteSecond().toString("HH:mm"));
if (lesson.hasSala()) {
stringBuilder.append(" - ");
stringBuilder.append(lesson.getSala().getName());
}
if (iterator.hasNext()) {
stringBuilder.append(" ; ");
}
}
}
return stringBuilder.toString();
}
public List<StudentGroup> getAssociatedStudentGroups(Grouping grouping) {
List<StudentGroup> result = new ArrayList<StudentGroup>();
for (StudentGroup studentGroup : getAssociatedStudentGroupsSet()) {
if (studentGroup.getGrouping() == grouping) {
result.add(studentGroup);
}
}
return result;
}
public boolean isTotalShiftLoadExceeded() {
final BigDecimal totalHours = getTotalHours();
for (final CourseLoad courseLoad : getCourseLoadsSet()) {
if (totalHours.compareTo(courseLoad.getTotalQuantity()) == 1) {
return true;
}
}
return false;
}
}