/** * 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.renderers; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.apache.commons.beanutils.BeanComparator; import org.apache.commons.lang.StringUtils; import org.fenixedu.academic.domain.CurricularCourse; import org.fenixedu.academic.domain.ExecutionSemester; import org.fenixedu.academic.domain.StudentCurricularPlan; import org.fenixedu.academic.domain.degreeStructure.Context; import org.fenixedu.academic.domain.degreeStructure.CourseGroup; import org.fenixedu.academic.domain.degreeStructure.CycleType; import org.fenixedu.academic.domain.degreeStructure.OptionalCurricularCourse; import org.fenixedu.academic.domain.studentCurriculum.CurriculumGroup; import org.fenixedu.academic.domain.studentCurriculum.CycleCurriculumGroup; import org.fenixedu.academic.domain.studentCurriculum.NoCourseGroupCurriculumGroup; import org.fenixedu.academic.dto.administrativeOffice.dismissal.DismissalBean; import org.fenixedu.academic.dto.administrativeOffice.dismissal.DismissalBean.DismissalType; import org.fenixedu.academic.dto.administrativeOffice.dismissal.DismissalBean.SelectedCurricularCourse; import org.fenixedu.academic.dto.administrativeOffice.dismissal.DismissalBean.SelectedOptionalCurricularCourse; import pt.ist.fenixWebFramework.rendererExtensions.controllers.CopyCheckBoxValuesController; import pt.ist.fenixWebFramework.rendererExtensions.converters.DomainObjectKeyConverter; import pt.ist.fenixWebFramework.renderers.InputRenderer; import pt.ist.fenixWebFramework.renderers.components.HtmlBlockContainer; import pt.ist.fenixWebFramework.renderers.components.HtmlCheckBox; import pt.ist.fenixWebFramework.renderers.components.HtmlComponent; import pt.ist.fenixWebFramework.renderers.components.HtmlMultipleHiddenField; import pt.ist.fenixWebFramework.renderers.components.HtmlRadioButton; import pt.ist.fenixWebFramework.renderers.components.HtmlRadioButtonGroup; import pt.ist.fenixWebFramework.renderers.components.HtmlTable; import pt.ist.fenixWebFramework.renderers.components.HtmlTableCell; import pt.ist.fenixWebFramework.renderers.components.HtmlTableRow; import pt.ist.fenixWebFramework.renderers.components.HtmlText; import pt.ist.fenixWebFramework.renderers.components.converters.ConversionException; import pt.ist.fenixWebFramework.renderers.components.converters.Converter; import pt.ist.fenixWebFramework.renderers.layouts.Layout; import pt.ist.fenixWebFramework.renderers.model.MetaObjectFactory; import pt.ist.fenixWebFramework.renderers.schemas.Schema; public class StudentDismissalRenderer extends InputRenderer { private Integer initialWidth = 60; private Integer widthDecreasePerLevel = 3; private String tablesClasses = "showinfo3 mvert0"; private String groupRowClasses = "bgcolor2"; private String curricularCourseRowClasses = ""; private String groupCellClasses = "smalltxt, aright"; private String curricularCourseCellClasses = ", aright"; private String dismissalType; public Integer getInitialWidth() { return initialWidth; } public void setInitialWidth(Integer initialWidth) { this.initialWidth = initialWidth; } public Integer getWidthDecreasePerLevel() { return widthDecreasePerLevel; } public void setWidthDecreasePerLevel(Integer widthDecreasePerLevel) { this.widthDecreasePerLevel = widthDecreasePerLevel; } public String getTablesClasses() { return tablesClasses; } public void setTablesClasses(String tablesClasses) { this.tablesClasses = tablesClasses; } public String getGroupRowClasses() { return groupRowClasses; } public void setGroupRowClasses(String groupRowClasses) { this.groupRowClasses = groupRowClasses; } public String getCurricularCourseRowClasses() { return curricularCourseRowClasses; } public void setCurricularCourseRowClasses(String curricularCourseRowClasses) { this.curricularCourseRowClasses = curricularCourseRowClasses; } private String[] getGroupCellClasses() { return groupCellClasses.split(","); } public void setGroupCellClasses(String groupCellClasses) { this.groupCellClasses = groupCellClasses; } public String getGroupNameClasses() { return getGroupCellClasses()[0]; } public String getGroupRadioClasses() { return getGroupCellClasses()[1]; } private String[] getCurricularCourseCellClasses() { return curricularCourseCellClasses.split(","); } public void setCurricularCourseCellClasses(String curricularCourseCellClasses) { this.curricularCourseCellClasses = curricularCourseCellClasses; } public String getCurricularCourseNameClasses() { return getCurricularCourseCellClasses()[0]; } public String getCurricularCourseCheckBoxClasses() { return getCurricularCourseCellClasses()[1]; } public String getDismissalType() { return dismissalType; } public void setDismissalType(String dismissalType) { this.dismissalType = dismissalType; } public StudentDismissalRenderer() { super(); } @Override protected Layout getLayout(Object object, Class type) { return new StudentDismissalLayout(); } protected class StudentDismissalLayout extends Layout { private final CopyCheckBoxValuesController curricularCoursesController = new CopyCheckBoxValuesController(); private final CopyCheckBoxValuesController optionalCurricularCoursesController = new CopyCheckBoxValuesController(); private HtmlRadioButtonGroup radioButtonGroup = null; private DismissalBean dismissalBean = null; @Override public HtmlComponent createComponent(Object object, Class type) { dismissalBean = (DismissalBean) object; HtmlBlockContainer container = new HtmlBlockContainer(); if (dismissalBean == null || dismissalBean.getExecutionPeriod() == null || dismissalBean.getStudentCurricularPlan().getStartExecutionPeriod() .isAfter(dismissalBean.getExecutionPeriod())) { return new HtmlText(); } DismissalType dismissalTypeValue = getDismissalType() == null ? dismissalBean.getDismissalType() : DismissalType.valueOf(getDismissalType()); if (dismissalTypeValue == DismissalType.CURRICULUM_GROUP_CREDITS) { radioButtonGroup = new HtmlRadioButtonGroup(); // slot refered by name radioButtonGroup.bind(getInputContext().getMetaObject(), "courseGroup"); radioButtonGroup.setConverter(new DomainObjectKeyConverter()); container.addChild(radioButtonGroup); generateCourseGroupCycles(container, dismissalBean.getStudentCurricularPlan(), dismissalBean.getExecutionPeriod()); } else if (dismissalTypeValue == DismissalType.CURRICULAR_COURSE_CREDITS) { final HtmlMultipleHiddenField hiddenCurricularCourses = new HtmlMultipleHiddenField(); // slot refered by name hiddenCurricularCourses.bind(getInputContext().getMetaObject(), "dismissals"); hiddenCurricularCourses.setConverter(new SelectedCurricularCoursesKeyConverter()); hiddenCurricularCourses.setController(curricularCoursesController); container.addChild(hiddenCurricularCourses); final HtmlMultipleHiddenField hiddenOptionalCurricularCourses = new HtmlMultipleHiddenField(); // slot refered by name hiddenOptionalCurricularCourses.bind(getInputContext().getMetaObject(), "optionalDismissals"); hiddenOptionalCurricularCourses.setConverter(new SelectedOptionalCurricularCoursesKeyConverter()); hiddenOptionalCurricularCourses.setController(optionalCurricularCoursesController); container.addChild(hiddenOptionalCurricularCourses); generateCurricularCourses(container, dismissalBean.getStudentCurricularPlan(), dismissalBean.getExecutionPeriod()); } else { radioButtonGroup = new HtmlRadioButtonGroup(); // slot refered by name radioButtonGroup.bind(getInputContext().getMetaObject(), "curriculumGroup"); radioButtonGroup.setConverter(new DomainObjectKeyConverter()); container.addChild(radioButtonGroup); generateNoCourseGroupCurriculumGroups(container, dismissalBean.getStudentCurricularPlan()); } return container; } protected void generateNoCourseGroupCurriculumGroups(HtmlBlockContainer container, StudentCurricularPlan studentCurricularPlan) { for (NoCourseGroupCurriculumGroup noCourseGroupCurriculumGroup : studentCurricularPlan .getNoCourseGroupCurriculumGroups()) { generateNoCourseGroupCurriculumGroup(container, studentCurricularPlan, noCourseGroupCurriculumGroup, 0); } } protected void generateNoCourseGroupCurriculumGroup(HtmlBlockContainer container, StudentCurricularPlan studentCurricularPlan, CurriculumGroup curriculumGroup, int depth) { final HtmlTable groupTable = new HtmlTable(); container.addChild(groupTable); groupTable.setClasses(getTablesClasses()); groupTable.setStyle("width: " + (getInitialWidth() - depth) + "em; margin-left: " + depth + "em;"); final HtmlTableRow htmlTableRow = groupTable.createRow(); htmlTableRow.setClasses(getGroupRowClasses()); final HtmlTableCell nameCell = htmlTableRow.createCell(); nameCell.setBody(new HtmlText(curriculumGroup.getFullPath())); nameCell.setClasses(getGroupNameClasses()); final HtmlTableCell radioButtonCell = htmlTableRow.createCell(); final HtmlRadioButton radioButton = radioButtonGroup.createRadioButton(); radioButton.setUserValue(MetaObjectFactory.createObject(curriculumGroup, new Schema(CurriculumGroup.class)).getKey() .toString()); radioButton.setChecked(curriculumGroup == dismissalBean.getCurriculumGroup()); radioButtonCell.setBody(radioButton); radioButtonCell.setClasses(getGroupRadioClasses()); radioButtonCell.setStyle("width: 2em;"); } protected void generateCourseGroupCycles(final HtmlBlockContainer blockContainer, final StudentCurricularPlan studentCurricularPlan, final ExecutionSemester executionSemester) { if (studentCurricularPlan.isBolonhaDegree()) { for (final CycleType cycleType : studentCurricularPlan.getDegreeType().getSupportedCyclesToEnrol()) { final CourseGroup courseGroup = getCourseGroupWithCycleType(studentCurricularPlan, cycleType); if (courseGroup != null) { generateCourseGroups(blockContainer, studentCurricularPlan, courseGroup, executionSemester, 0); } } } else { generateCourseGroups(blockContainer, studentCurricularPlan, studentCurricularPlan.getRoot().getDegreeModule(), executionSemester, 0); } } protected CourseGroup getCourseGroupWithCycleType(final StudentCurricularPlan studentCurricularPlan, final CycleType cycleType) { final CycleCurriculumGroup curriculumGroup = studentCurricularPlan.getCycle(cycleType); return curriculumGroup != null ? curriculumGroup.getDegreeModule() : studentCurricularPlan.getDegreeCurricularPlan() .getCycleCourseGroup(cycleType); } protected void generateCurricularCourses(final HtmlBlockContainer blockContainer, final StudentCurricularPlan studentCurricularPlan, final ExecutionSemester executionSemester) { final HtmlTable groupTable = new HtmlTable(); blockContainer.addChild(groupTable); groupTable.setClasses(getTablesClasses()); groupTable.setStyle("width: " + getInitialWidth() + "em; margin-left: 0em;"); final List<CurricularCourse> orderedCurricularCourses = new ArrayList<CurricularCourse>(dismissalBean.getAllCurricularCoursesToDismissal()); Collections.sort(orderedCurricularCourses, new BeanComparator("name", Collator.getInstance())); for (final CurricularCourse curricularCourse : orderedCurricularCourses) { final HtmlTableRow htmlTableRow = groupTable.createRow(); htmlTableRow.setClasses(getCurricularCourseRowClasses()); final HtmlTableCell nameCell = htmlTableRow.createCell(); final String code = curricularCourse.getCode(); final String oneFullName = curricularCourse.getOneFullName(executionSemester); final String name = " <span class='bold'>" + curricularCourse.getName(dismissalBean.getExecutionPeriod()) + "</span> (" + oneFullName.substring(0, oneFullName.lastIndexOf(">")) + ")"; final String codeAndname = StringUtils.isEmpty(code) ? name : code + " - " + name; nameCell.setBody(new HtmlText(codeAndname, false)); nameCell.setClasses(getCurricularCourseNameClasses()); final HtmlTableCell checkBoxCell = htmlTableRow.createCell(); checkBoxCell.setClasses(getCurricularCourseCheckBoxClasses()); final HtmlCheckBox checkBox = new HtmlCheckBox(dismissalBean.containsDismissalOrOptionalDismissal(curricularCourse)); checkBox.setName("curricularCourseCheckBox" + curricularCourse.getExternalId()); if (curricularCourse.isOptionalCurricularCourse()) { final OptionalCurricularCourse optionalCurricularCourse = (OptionalCurricularCourse) curricularCourse; checkBox.setUserValue(new DismissalBean.SelectedOptionalCurricularCourse(optionalCurricularCourse, studentCurricularPlan).getKey()); checkBoxCell.setBody(checkBox); optionalCurricularCoursesController.addCheckBox(checkBox); } else { checkBox.setUserValue(new DismissalBean.SelectedCurricularCourse(curricularCourse, studentCurricularPlan) .getKey()); checkBoxCell.setBody(checkBox); curricularCoursesController.addCheckBox(checkBox); } } } protected void generateCourseGroups(final HtmlBlockContainer blockContainer, final StudentCurricularPlan studentCurricularPlan, final CourseGroup courseGroup, final ExecutionSemester executionSemester, int depth) { final HtmlTable groupTable = new HtmlTable(); blockContainer.addChild(groupTable); groupTable.setClasses(getTablesClasses()); groupTable.setStyle("width: " + (getInitialWidth() - depth) + "em; margin-left: " + depth + "em;"); final HtmlTableRow htmlTableRow = groupTable.createRow(); htmlTableRow.setClasses(getGroupRowClasses()); final HtmlTableCell nameCell = htmlTableRow.createCell(); nameCell.setBody(new HtmlText(courseGroup.getName())); nameCell.setClasses(getGroupNameClasses()); final HtmlTableCell currentCreditsCell = htmlTableRow.createCell(); final double ectsCreditsForCourseGroup = studentCurricularPlan.getCreditsConcludedForCourseGroup(courseGroup).doubleValue(); if (ectsCreditsForCourseGroup == 0d) { currentCreditsCell.setBody(new HtmlText("ECTS: -")); } else { currentCreditsCell.setBody(new HtmlText("ECTS: " + ectsCreditsForCourseGroup)); } currentCreditsCell.setClasses("smalltxt"); currentCreditsCell.setStyle("width: 6em;"); final HtmlTableCell creditsMinCell = htmlTableRow.createCell(); creditsMinCell.setBody(new HtmlText("Min: " + courseGroup.getMinEctsCredits(executionSemester))); creditsMinCell.setClasses("smalltxt"); creditsMinCell.setStyle("width: 6em;"); final HtmlTableCell creditsMaxCell = htmlTableRow.createCell(); creditsMaxCell.setBody(new HtmlText("Max: " + courseGroup.getMaxEctsCredits(executionSemester))); creditsMaxCell.setClasses("smalltxt"); creditsMaxCell.setStyle("width: 6em;"); final HtmlTableCell radioButtonCell = htmlTableRow.createCell(); final HtmlRadioButton radioButton = radioButtonGroup.createRadioButton(); radioButton.setUserValue(MetaObjectFactory.createObject(courseGroup, new Schema(CourseGroup.class)).getKey() .toString()); radioButton.setChecked(courseGroup == dismissalBean.getCourseGroup()); radioButtonCell.setBody(radioButton); radioButtonCell.setClasses(getGroupRadioClasses()); radioButtonCell.setStyle("width: 2em;"); for (final Context context : courseGroup.getSortedOpenChildContextsWithCourseGroups(executionSemester)) { generateCourseGroups(blockContainer, studentCurricularPlan, (CourseGroup) context.getChildDegreeModule(), executionSemester, depth + getWidthDecreasePerLevel()); } } } private static class SelectedCurricularCoursesKeyConverter extends Converter { static private final long serialVersionUID = 1L; @Override public Object convert(Class type, Object value) { if (value == null) { return null; } final DomainObjectKeyConverter converter = new DomainObjectKeyConverter(); final List<SelectedCurricularCourse> result = new ArrayList<SelectedCurricularCourse>(); final String[] values = (String[]) value; for (String key : values) { String[] parts = key.split(","); if (parts.length < 3) { throw new ConversionException("invalid key format: " + key); } final CurricularCourse curricularCourse = (CurricularCourse) converter.convert(type, parts[0]); final CurriculumGroup curriculumGroup = (CurriculumGroup) converter.convert(type, parts[1]); final StudentCurricularPlan studentCurricularPlan = (StudentCurricularPlan) converter.convert(type, parts[2]); final SelectedCurricularCourse selectedCurricularCourse = new SelectedCurricularCourse(curricularCourse, studentCurricularPlan); selectedCurricularCourse.setCurriculumGroup(curriculumGroup); result.add(selectedCurricularCourse); } return result; } } private static class SelectedOptionalCurricularCoursesKeyConverter extends Converter { static private final long serialVersionUID = 1L; @Override public Object convert(Class type, Object value) { if (value == null) { return null; } final DomainObjectKeyConverter converter = new DomainObjectKeyConverter(); final List<SelectedOptionalCurricularCourse> result = new ArrayList<SelectedOptionalCurricularCourse>(); final String[] values = (String[]) value; for (String key : values) { String[] parts = key.split(","); if (parts.length < 3) { throw new ConversionException("invalid key format: " + key); } final OptionalCurricularCourse curricularCourse = (OptionalCurricularCourse) converter.convert(type, parts[0]); final CurriculumGroup curriculumGroup = (CurriculumGroup) converter.convert(type, parts[1]); final StudentCurricularPlan studentCurricularPlan = (StudentCurricularPlan) converter.convert(type, parts[2]); final SelectedOptionalCurricularCourse selectedCurricularCourse = new SelectedOptionalCurricularCourse(curricularCourse, studentCurricularPlan); selectedCurricularCourse.setCurriculumGroup(curriculumGroup); result.add(selectedCurricularCourse); } return result; } } }