/**
* 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.ui.struts.action.resourceAllocationManager;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.TreeSet;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;
import org.fenixedu.academic.domain.CourseLoad;
import org.fenixedu.academic.domain.ExecutionCourse;
import org.fenixedu.academic.domain.ExecutionDegree;
import org.fenixedu.academic.domain.ExecutionInterval;
import org.fenixedu.academic.domain.ExecutionSemester;
import org.fenixedu.academic.domain.ExecutionYear;
import org.fenixedu.academic.domain.Lesson;
import org.fenixedu.academic.domain.LessonInstance;
import org.fenixedu.academic.domain.Professorship;
import org.fenixedu.academic.domain.SchoolClass;
import org.fenixedu.academic.domain.Shift;
import org.fenixedu.academic.domain.space.LessonSpaceOccupation;
import org.fenixedu.academic.domain.space.SpaceUtils;
import org.fenixedu.academic.domain.time.calendarStructure.AcademicInterval;
import org.fenixedu.academic.domain.time.calendarStructure.AcademicPeriod;
import org.fenixedu.academic.dto.InfoLesson;
import org.fenixedu.academic.ui.struts.action.base.FenixDispatchAction;
import org.fenixedu.academic.ui.struts.action.resourceAllocationManager.RAMApplication.RAMSchedulesApp;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.academic.util.HourMinuteSecond;
import org.fenixedu.academic.util.WeekDay;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.fenixedu.bennu.struts.annotations.Forward;
import org.fenixedu.bennu.struts.annotations.Forwards;
import org.fenixedu.bennu.struts.annotations.Mapping;
import org.fenixedu.bennu.struts.portal.EntryPoint;
import org.fenixedu.bennu.struts.portal.StrutsFunctionality;
import org.fenixedu.commons.i18n.I18N;
import org.fenixedu.spaces.domain.Space;
import org.fenixedu.spaces.domain.SpaceClassification;
import org.joda.time.DateTime;
import pt.ist.fenixWebFramework.renderers.utils.RenderUtils;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet.Row;
import com.google.common.collect.Ordering;
/**
* @author Luis Cruz e Sara Ribeiro
*/
@StrutsFunctionality(app = RAMSchedulesApp.class, path = "room-schedules", titleKey = "link.schedules.listAllByRoom")
@Mapping(path = "/viewAllRoomsSchedulesDA", module = "resourceAllocationManager")
@Forwards({ @Forward(name = "choose", path = "/resourceAllocationManager/choosePavillionsToViewRoomsSchedules.jsp"),
@Forward(name = "list", path = "/resourceAllocationManager/viewAllRoomsSchedules.jsp") })
public class ViewAllRoomsSchedulesDA extends FenixDispatchAction {
public static class ChooseBuildingBean implements Serializable {
private static final long serialVersionUID = -663198492313971329L;
private AcademicInterval academicInterval;
private List<Space> selectedBuildings;
public ChooseBuildingBean() {
this.academicInterval = AcademicInterval.readDefaultAcademicInterval(AcademicPeriod.SEMESTER);
}
public AcademicInterval getAcademicInterval() {
return academicInterval;
}
public void setAcademicInterval(AcademicInterval academicInterval) {
this.academicInterval = academicInterval;
}
public List<Space> getAvailableBuildings() {
return Ordering.from(SpaceUtils.COMPARATOR_BY_PRESENTATION_NAME).sortedCopy(SpaceUtils.buildings());
}
public List<AcademicInterval> getAvailableIntervals() {
return Ordering.from(AcademicInterval.COMPARATOR_BY_BEGIN_DATE).reverse()
.sortedCopy(AcademicInterval.readAcademicIntervals(AcademicPeriod.SEMESTER));
}
public List<Space> getSelectedBuildings() {
return selectedBuildings;
}
public void setSelectedBuildings(List<Space> selectedBuildings) {
this.selectedBuildings = selectedBuildings;
}
}
@EntryPoint
public ActionForward choose(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws Exception {
ChooseBuildingBean bean = getRenderedObject();
if (bean == null) {
bean = new ChooseBuildingBean();
}
RenderUtils.invalidateViewState();
request.setAttribute("bean", bean);
return mapping.findForward("choose");
}
public ActionForward list(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response)
throws Exception {
ChooseBuildingBean bean = getRenderedObject();
List<Space> rooms = new ArrayList<Space>();
for (Space building : bean.getSelectedBuildings()) {
rooms.addAll(SpaceUtils.getAllActiveSubRoomsForEducation(building));
}
final List<RoomLessonsBean> beans = new ArrayList<RoomLessonsBean>();
for (final Space room : rooms) {
if (!StringUtils.isEmpty(room.getName())) {
final List<Lesson> lessons = SpaceUtils.getAssociatedLessons(room, bean.getAcademicInterval());
final List<InfoLesson> infoLessons = new ArrayList<InfoLesson>(lessons.size());
for (Lesson lesson : lessons) {
infoLessons.add(InfoLesson.newInfoFromDomain(lesson));
}
beans.add(new RoomLessonsBean(room, infoLessons));
}
}
request.setAttribute("academicInterval", bean.getAcademicInterval());
request.setAttribute("beans", beans);
return mapping.findForward("list");
}
public static class RoomLessonsBean {
private final Space room;
private final List<InfoLesson> lessons;
public RoomLessonsBean(Space room, List<InfoLesson> lessons) {
super();
this.room = room;
this.lessons = lessons;
}
public Space getRoom() {
return room;
}
public List<InfoLesson> getLessons() {
return lessons;
}
}
private static final int WEEKDAY_COUNT = 6;
private static final int HOUR_COUNT = (24 - 8) * 2;
private static class RoomMap extends HashMap<Space, boolean[][]> {
@Override
public boolean[][] get(final Object key) {
boolean[][] value = super.get(key);
if (value == null) {
value = new boolean[WEEKDAY_COUNT][HOUR_COUNT];
put((Space) key, value);
}
return value;
}
public void register(final Space allocatableSpace) {
if (allocatableSpace != null) {
get(allocatableSpace);
}
}
public void register(final Lesson lesson) {
final LessonSpaceOccupation lessonSpaceOccupation = lesson.getLessonSpaceOccupation();
if (lessonSpaceOccupation != null) {
final Space allocatableSpace = lessonSpaceOccupation.getRoom();
if (allocatableSpace != null) {
final boolean[][] slots = get(allocatableSpace);
final int weekDayOffSet = lesson.getDiaSemana().getDiaSemana().intValue() - 2;
final int startOffSet = getHourOffSet(lesson.getBeginHourMinuteSecond());
final int endOffSet = getHourOffSet(lesson.getEndHourMinuteSecond());
for (int i = startOffSet; i < endOffSet; slots[weekDayOffSet][i++] = true) {
;
}
}
}
for (final LessonInstance lessonInstance : lesson.getLessonInstancesSet()) {
final Space allocatableSpace = lessonInstance.getRoom();
if (allocatableSpace != null) {
final boolean[][] slots = get(allocatableSpace);
final int weekDayOffSet = lessonInstance.getBeginDateTime().getDayOfWeek() - 1;
final int startOffSet = getHourOffSet(lessonInstance.getBeginDateTime());
final int endOffSet = getHourOffSet(lessonInstance.getEndDateTime());
for (int i = startOffSet; i < endOffSet; slots[weekDayOffSet][i++] = true) {
;
}
}
}
}
private int getHourOffSet(final HourMinuteSecond hourMinuteSecond) {
final int hour = hourMinuteSecond.getHour();
final int minutes = hourMinuteSecond.getMinuteOfHour();
final int minutesOffSet = minutes < 30 ? 0 : 1;
return ((hour - 8) * 2) + minutesOffSet;
}
private int getHourOffSet(final DateTime dateTime) {
final int hour = dateTime.getHourOfDay();
final int minutes = dateTime.getMinuteOfHour();
final int minutesOffSet = minutes < 30 ? 0 : 1;
return ((hour - 8) * 2) + minutesOffSet;
}
}
public ActionForward downloadRoomLessonOccupationInfo(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException {
final ExecutionSemester executionSemester = getExecutionSemester(request);
final ExecutionYear executionYear = executionSemester.getExecutionYear();
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=occupationMap"
+ executionYear.getYear().replace('/', '_') + "_" + executionSemester.getSemester() + ".xls");
final RoomMap occupationMap = new RoomMap();
Space.getSpaces().forEach(s -> occupationMap.register(s));
for (final ExecutionCourse executionCourse : executionSemester.getAssociatedExecutionCoursesSet()) {
for (final CourseLoad courseLoad : executionCourse.getCourseLoadsSet()) {
for (final Shift shift : courseLoad.getShiftsSet()) {
for (final Lesson lesson : shift.getAssociatedLessonsSet()) {
occupationMap.register(lesson);
}
}
}
}
final Spreadsheet spreadsheet = new Spreadsheet("OccupationMap");
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.building"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.identification"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.blueprintNumber"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.doorNumber"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.description"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.classification"));
final DateTime now = new DateTime();
for (int weekDay = 0; weekDay < 6; weekDay++) {
final DateTime dateTime = now.withDayOfWeek(weekDay + 1);
final String weekDayString = dateTime.dayOfWeek().getAsText(I18N.getLocale());
for (int hour = 0; hour < 16; hour++) {
spreadsheet.setHeader(weekDayString + " " + (hour + 8) + ":00");
spreadsheet.setHeader(weekDayString + " " + (hour + 8) + ":30");
}
}
for (final Entry<Space, boolean[][]> entry : occupationMap.entrySet()) {
final Space space = entry.getKey();
final String identification = space.getName();
final Space building = SpaceUtils.getSpaceBuilding(space);
final String buildingName = building == null ? "" : building.getPresentationName();
final boolean[][] slots = entry.getValue();
final Row row = spreadsheet.addRow();
row.setCell(buildingName);
row.setCell(identification == null ? " " : identification);
final String blueprintNumber = findClosestBlueprintNumber(space);
row.setCell(blueprintNumber);
if (SpaceUtils.isRoom(space)) {
Optional<String> doorNumber = space.getMetadata("doorNumber");
Optional<String> description = space.getMetadata("description");
row.setCell(doorNumber.isPresent() ? doorNumber.get() : " ");
row.setCell(description.isPresent() ? description.get() : " ");
} else if (SpaceUtils.isRoomSubdivision(space)) {
final Space room = findSurroundingRoom(space);
if (room == null) {
row.setCell(" ");
row.setCell(" ");
} else {
Optional<String> doorNumber = space.getMetadata("doorNumber");
Optional<String> description = space.getMetadata("description");
row.setCell(doorNumber.isPresent() ? doorNumber.get() : " ");
row.setCell(description.isPresent() ? description.get() : " ");
}
} else {
row.setCell(" ");
row.setCell(" ");
}
SpaceClassification classification = space.getClassification();
if (classification == null) {
row.setCell(" ");
} else {
row.setCell(classification.getAbsoluteCode() + " " + classification.getName().getContent());
}
for (int i = 0; i < WEEKDAY_COUNT; i++) {
for (int j = 0; j < HOUR_COUNT; j++) {
row.setCell(Boolean.toString(slots[i][j]));
}
}
}
final ServletOutputStream writer = response.getOutputStream();
spreadsheet.exportToXLSSheet(writer);
writer.flush();
response.flushBuffer();
return null;
}
private ExecutionSemester getExecutionSemester(HttpServletRequest request) {
String academicIntervalString = (String) getFromRequest(request, "academicIntervalString");
AcademicInterval academicInterval = AcademicInterval.getAcademicIntervalFromString(academicIntervalString);
final ExecutionSemester executionSemester = (ExecutionSemester) ExecutionInterval.getExecutionInterval(academicInterval);
return executionSemester;
}
private String findClosestBlueprintNumber(final Space space) {
if (space == null) {
return null;
}
return space.getBlueprintNumber().isPresent() ? space.getBlueprintNumber().get() : findClosestBlueprintNumber(space
.getParent());
}
private Space findSurroundingRoom(final Space space) {
final Space suroundingSpace = space.getParent();
return suroundingSpace == null ? null : SpaceUtils.isRoom(space) ? suroundingSpace : findSurroundingRoom(suroundingSpace);
}
public ActionForward downloadScheduleList(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException {
final ExecutionSemester executionSemester = getExecutionSemester(request);
final Integer semester = executionSemester.getSemester();
final String executionYear = executionSemester.getExecutionYear().getYear();
final Spreadsheet spreadsheet = new Spreadsheet("ScheduleMap");
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionPeriod"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionYear"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionCourse"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionDegree"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.curricular.year"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.shift"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.shift.schedule"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.shift.schedule.hasAllocatedRooms"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.teacher.emails"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.comments"));
for (final ExecutionCourse executionCourse : executionSemester.getAssociatedExecutionCoursesSet()) {
final StringBuilder executionDegreeBuilder = new StringBuilder();
for (final ExecutionDegree executionDegree : executionCourse.getExecutionDegrees()) {
if (executionDegreeBuilder.length() > 0) {
executionDegreeBuilder.append("\n");
}
executionDegreeBuilder.append(executionDegree.getDegree().getSigla());
}
final StringBuilder emailBuilder = new StringBuilder();
for (final Professorship professorship : executionCourse.getProfessorshipsSet()) {
if (emailBuilder.length() > 0) {
emailBuilder.append("\n");
}
emailBuilder.append(professorship.getPerson().getEmailForSendingEmails());
}
for (final CourseLoad courseLoad : executionCourse.getCourseLoadsSet()) {
for (final Shift shift : courseLoad.getShiftsSet()) {
final Set<Integer> curricularYears = new TreeSet<Integer>();
for (final SchoolClass schoolClass : shift.getAssociatedClassesSet()) {
curricularYears.add(schoolClass.getAnoCurricular());
}
final StringBuilder curricularYearBuilder = new StringBuilder();
for (final Integer curricularYear : curricularYears) {
if (curricularYearBuilder.length() > 0) {
curricularYearBuilder.append(", ");
}
curricularYearBuilder.append(curricularYear);
}
final Row row = spreadsheet.addRow();
row.setCell(semester);
row.setCell(executionYear);
row.setCell(executionCourse.getName());
row.setCell(executionDegreeBuilder.toString());
row.setCell(curricularYearBuilder.toString());
row.setCell(shift.getNome());
row.setCell(shift.getLessonPresentationString().replace(';', '\n'));
row.setCell(hasRoomsAttributed(shift));
row.setCell(emailBuilder.toString());
row.setCell(shift.getComment() == null ? "" : shift.getComment());
}
}
}
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=scheduleMap" + executionYear.replace('/', '_') + "_"
+ executionSemester.getSemester() + ".xls");
final ServletOutputStream writer = response.getOutputStream();
spreadsheet.exportToXLSSheet(writer);
writer.flush();
response.flushBuffer();
return null;
}
public ActionForward downloadShiftAttendence(ActionMapping mapping, ActionForm form, HttpServletRequest request,
HttpServletResponse response) throws IOException {
final ExecutionSemester executionSemester = getExecutionSemester(request);
final String executionYear = executionSemester.getExecutionYear().getYear();
final Spreadsheet spreadsheet = new Spreadsheet("ShiftAttendenceMap");
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.shift"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionCourse"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.executionDegree"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.shift.schedule"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.lesson.room"));
spreadsheet.setHeader(BundleUtil.getString(Bundle.APPLICATION, "label.number.students.enrolled"));
for (final ExecutionCourse executionCourse : executionSemester.getAssociatedExecutionCoursesSet()) {
final StringBuilder executionDegreeBuilder = new StringBuilder();
for (final ExecutionDegree executionDegree : executionCourse.getExecutionDegrees()) {
if (executionDegreeBuilder.length() > 0) {
executionDegreeBuilder.append("\n");
}
executionDegreeBuilder.append(executionDegree.getDegree().getSigla());
}
final StringBuilder emailBuilder = new StringBuilder();
for (final Professorship professorship : executionCourse.getProfessorshipsSet()) {
if (emailBuilder.length() > 0) {
emailBuilder.append("\n");
}
emailBuilder.append(professorship.getPerson().getEmailForSendingEmails());
}
for (final CourseLoad courseLoad : executionCourse.getCourseLoadsSet()) {
for (final Shift shift : courseLoad.getShiftsSet()) {
final Row row = spreadsheet.addRow();
final StringBuilder roomBuilder = new StringBuilder();
final StringBuilder scheduleBuilder = new StringBuilder();
if (!shift.getAssociatedLessonsSet().isEmpty()) {
for (Iterator<Lesson> iterator = shift.getAssociatedLessonsSet().iterator(); iterator.hasNext();) {
Lesson lesson = iterator.next();
scheduleBuilder.append(WeekDay.getWeekDay(lesson.getDiaSemana()).getLabelShort());
scheduleBuilder.append(" ");
scheduleBuilder.append(lesson.getBeginHourMinuteSecond().toString("HH:mm"));
scheduleBuilder.append(" - ");
scheduleBuilder.append(lesson.getEndHourMinuteSecond().toString("HH:mm"));
if (lesson.hasSala()) {
roomBuilder.append(lesson.getSala().getName());
}
if (iterator.hasNext()) {
scheduleBuilder.append(" ; ");
roomBuilder.append(" ; ");
}
}
}
row.setCell(shift.getNome());
row.setCell(executionCourse.getName());
row.setCell(executionDegreeBuilder.toString());
row.setCell(scheduleBuilder.toString().replace(';', '\n'));
row.setCell(roomBuilder.toString().replace(';', '\n'));
row.setCell(shift.getStudentsSet().size());
}
}
}
response.setContentType("application/vnd.ms-excel");
response.setHeader("Content-disposition", "attachment; filename=shiftAttendenceMap" + executionYear.replace('/', '_')
+ "_" + executionSemester.getSemester() + ".xls");
final ServletOutputStream writer = response.getOutputStream();
spreadsheet.exportToXLSSheet(writer);
writer.flush();
response.flushBuffer();
return null;
}
private String hasRoomsAttributed(final Shift shift) {
if (shift.getAssociatedLessonsSet().isEmpty()) {
return BundleUtil.getString(Bundle.APPLICATION, "label.no");
}
for (final Lesson lesson : shift.getAssociatedLessonsSet()) {
if (!hasRoomsAttributed(lesson)) {
return BundleUtil.getString(Bundle.APPLICATION, "label.no");
}
}
return BundleUtil.getString(Bundle.APPLICATION, "label.yes");
}
private boolean hasRoomsAttributed(final Lesson lesson) {
return lesson.getSala() != null;
}
}