/** * 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.report.academicAdministrativeOffice; import java.math.BigDecimal; import java.text.Collator; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Map.Entry; import java.util.ResourceBundle; import java.util.stream.Collectors; import org.apache.commons.lang.StringUtils; import org.fenixedu.academic.domain.Degree; import org.fenixedu.academic.domain.DegreeOfficialPublication; import org.fenixedu.academic.domain.DegreeSpecializationArea; import org.fenixedu.academic.domain.IEnrolment; import org.fenixedu.academic.domain.Person; import org.fenixedu.academic.domain.degreeStructure.CycleType; import org.fenixedu.academic.domain.degreeStructure.EctsGraduationGradeConversionTable; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.domain.organizationalStructure.AcademicalInstitutionType; import org.fenixedu.academic.domain.organizationalStructure.Unit; import org.fenixedu.academic.domain.organizationalStructure.UniversityUnit; import org.fenixedu.academic.domain.phd.serviceRequests.documentRequests.PhdDiplomaSupplementRequest; import org.fenixedu.academic.domain.serviceRequests.IDiplomaSupplementRequest; import org.fenixedu.academic.domain.serviceRequests.documentRequests.DiplomaSupplementRequest; import org.fenixedu.academic.domain.serviceRequests.documentRequests.IDocumentRequest; import org.fenixedu.academic.domain.student.Registration; import org.fenixedu.academic.domain.student.Student; import org.fenixedu.academic.domain.student.curriculum.ExtraCurricularActivity; import org.fenixedu.academic.domain.student.curriculum.ExtraCurricularActivityType; import org.fenixedu.academic.domain.student.curriculum.ICurriculumEntry; import org.fenixedu.academic.domain.studentCurriculum.CurriculumGroup; import org.fenixedu.academic.domain.studentCurriculum.Dismissal; import org.fenixedu.academic.domain.studentCurriculum.ExternalEnrolment; import org.fenixedu.academic.util.Bundle; import org.fenixedu.academic.util.StringFormatter; import org.fenixedu.bennu.core.domain.Bennu; import org.fenixedu.bennu.core.i18n.BundleUtil; import org.joda.time.DateTime; import org.joda.time.YearMonthDay; import com.google.common.base.Joiner; import com.google.common.base.Strings; public class DiplomaSupplement extends AdministrativeOfficeDocument { protected DiplomaSupplement(final IDocumentRequest documentRequest, final Locale locale) { super(documentRequest, locale); } @Override public String getReportTemplateKey() { return getClass().getName(); } @Override protected IDiplomaSupplementRequest getDocumentRequest() { return (IDiplomaSupplementRequest) documentRequestDomainReference; } @Override protected void fillReport() { addParameter("bundle", ResourceBundle.getBundle(getBundle(), getLocale())); addParameter("name", StringFormatter.prettyPrint(getDocumentRequest().getPerson().getName().trim())); // Group 1 fillGroup1(); // Group 2 fillGroup2(); // Group 3 fillGroup3(); // Group 4 fillGroup4(); // Group 5 fillGroup5(); // Group 6 fillGroup6(); // Group 7 fillGroup7(); // Group 8 fillGroup8(); } protected void fillGroup8() { addParameter("langSuffix", getLanguage().getLanguage()); } protected void fillGroup7() { final UniversityUnit institutionsUniversityUnit = UniversityUnit.getInstitutionsUniversityUnit(); addParameter("day", new YearMonthDay().toString(DD_SLASH_MM_SLASH_YYYY, getLocale())); addParameter("universityPrincipal", institutionsUniversityUnit.getCurrentPrincipal()); } protected void fillGroup6() { addExtraCurricularActivities(); } protected void fillGroup5() { final StringBuilder access = new StringBuilder(); if (getDocumentRequest().getRequestedCycle() == CycleType.THIRD_CYCLE) { access.append(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.five.one.three")); } else { String degreeDesignation = getDegreeDesignation(getLocale()); access.append(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.five.one.one")).append( SINGLE_SPACE); access.append(degreeDesignation).append(SINGLE_SPACE); access.append(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.five.one.two")); } addParameter("accessToHigherLevelOfEducation", access.toString()); addProfessionalStatus(); } protected void fillGroup4() { String degreeDesignation = getDegreeDesignation(getLocale()); addProgrammeRequirements(degreeDesignation); addEntriesParameters(); addParameter("classificationSystem", BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.four.four.one")); addParameter("finalAverage", getDocumentRequest().getFinalAverage()); String finalAverageQualified = getDocumentRequest().getFinalAverageQualified(getLocale()); if (finalAverageQualified != null) { addParameter("finalAverageQualified", BundleUtil.getString(Bundle.ACADEMIC, getLocale(), finalAverageQualified)); } EctsGraduationGradeConversionTable table = getDocumentRequest().getGraduationConversionTable(); if (getDocumentRequest().isRequestForPhd()) { addParameter("thesisFinalGrade", ((PhdDiplomaSupplementRequest) getDocumentRequest()).getThesisFinalGrade(getLocale())); } addParameter("ectsGradeConversionTable", table.getEctsTable()); addParameter("ectsGradePercentagesTable", table.getPercentages()); } protected void fillGroup3() { addParameter( "qualificationLevel", BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.qualification." + getDocumentRequest().getRequestedCycle())); addParameter("years", getDocumentRequest().getNumberOfCurricularYears()); addParameter("semesters", getDocumentRequest().getNumberOfCurricularSemesters()); addParameter( "weeksOfStudyPerYear", BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.weeksOfStudyPerYear." + getDocumentRequest().getRequestedCycle())); double ectsCreditsValue = getDocumentRequest().getEctsCredits(); String ectsCredits = (long) ectsCreditsValue == ectsCreditsValue ? "" + Long.toString((long) ectsCreditsValue) : Double .toString(ectsCreditsValue); addParameter("ectsCredits", ectsCredits); } protected String fillGroup2() { Locale locale = Locale.getDefault(); final UniversityUnit institutionsUniversityUnit = getUniversity(getDocumentRequest().getRequestDate()); String degreeDesignation = getDegreeDesignation(locale); String graduateTitleNative = getDocumentRequest().getGraduateTitle(locale).split(" ")[0]; addParameter("graduateTitle", degreeDesignation + "\n" + graduateTitleNative); addParameter("universityName", institutionsUniversityUnit.getName()); addParameter( "universityStatus", BundleUtil.getString(Bundle.ENUMERATION, locale, AcademicalInstitutionType.class.getSimpleName() + "." + institutionsUniversityUnit.getInstitutionType().getName())); addParameter("institutionName", Bennu.getInstance().getInstitutionUnit().getName()); addParameter("institutionStatus", BundleUtil.getString(Bundle.ENUMERATION, locale, Bennu.getInstance().getInstitutionUnit().getType().getName()) + SINGLE_SPACE + BundleUtil.getString(Bundle.ACADEMIC, locale, "diploma.supplement.of") + SINGLE_SPACE + institutionsUniversityUnit.getName()); addParameter("prevailingScientificArea", getDocumentRequest().getPrevailingScientificArea(getLocale())); if (CycleType.FIRST_CYCLE.equals(getDocumentRequest().getRequestedCycle())) { addParameter("languages", BundleUtil.getString(Bundle.ENUMERATION, getLocale(), "pt")); } else { addParameter( "languages", BundleUtil.getString(Bundle.ENUMERATION, getLocale(), "pt") + SINGLE_SPACE + BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "label.and").toLowerCase() + SINGLE_SPACE + BundleUtil.getString(Bundle.ENUMERATION, getLocale(), "en")); } return degreeDesignation; } protected void fillGroup1() { /* * Oddly a subreport is only rendered if the specified data source is * not empty. All reports have "entries" parameter as data source. * "entries" may be empty in case of phd diploma supplement so add a * dummy data source */ addParameter("dummyDataSource", Arrays.asList(Boolean.TRUE)); Person person = getDocumentRequest().getPerson(); addParameter("familyName", person.getFamilyNames()); addParameter("givenName", person.getGivenNames()); addParameter("birthDay", person.getDateOfBirthYearMonthDay().toString(DD_SLASH_MM_SLASH_YYYY, getLocale())); addParameter("nationality", StringFormatter.prettyPrint(person.getCountry().getCountryNationality().getContent(getLocale()))); addParameter( "documentIdType", applyMessageArguments(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.one.five.one"), BundleUtil.getString(Bundle.ENUMERATION, getLocale(), person.getIdDocumentType().getName()))); addParameter("documentIdNumber", person.getDocumentIdNumber()); addParameter("registrationNumber", getDocumentRequest().getRegistrationNumber()); addParameter("isExemptedFromStudy", getDocumentRequest().isExemptedFromStudy()); addParameter("isForPhd", getDocumentRequest().isRequestForPhd()); addParameter("isForRegistration", getDocumentRequest().isRequestForRegistration()); } private String getDegreeDesignation(Locale locale) { IDiplomaSupplementRequest request = getDocumentRequest(); ArrayList<String> result = new ArrayList<>(); final String graduationLevel = request.getProgramConclusion().getGraduationLevel().getContent(locale); if (!Strings.isNullOrEmpty(graduationLevel)) { result.add(graduationLevel); result.add(BundleUtil.getString(Bundle.ACADEMIC, locale, "label.in")); } //TODO: phd-refactor registration should always be present in phd Degree degree; if (request.hasRegistration()) { degree = request.getRegistration().getDegree(); } else { degree = ((PhdDiplomaSupplementRequest) request).getPhdIndividualProgramProcess().getPhdProgram().getDegree(); } if (request.isRequestForPhd()) { result.add(degree.getNameI18N(request.getConclusionYear()).getContent(locale)); } else { result.add(request.getProgramConclusion().groupFor(request.getRegistration()).map(CurriculumGroup::getDegreeModule) .map(dm -> dm.getDegreeNameWithTitleSuffix(request.getConclusionYear(), locale)) .orElse(degree.getNameI18N(request.getConclusionYear()).getContent(locale))); } return Joiner.on(" ").join(result); } private void addProgrammeRequirements(String graduateDegree) { String labelThe = CycleType.FIRST_CYCLE.equals(getDocumentRequest().getRequestedCycle()) ? BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "label.the.female") : BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "label.the.male"); double ectsCreditsValue = getDocumentRequest().getEctsCredits(); String ectsCredits = (long) ectsCreditsValue == ectsCreditsValue ? "" + Long.toString((long) ectsCreditsValue) : Double .toString(ectsCreditsValue); DegreeOfficialPublication dr = getDocumentRequest().getDegreeOfficialPublication(); if (dr == null) { throw new DomainException("error.DiplomaSupplement.degreeOfficialPublicationNotFound"); } String officialPublication = dr.getOfficialReference(); String programmeRequirements; if (getDocumentRequest().isRequestForPhd()) { programmeRequirements = applyMessageArguments(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.four.two.programmerequirements.template.noareas.with.official.publication"), labelThe, graduateDegree, ectsCredits, officialPublication); } else if (getDocumentRequest().getRequestedCycle().equals(CycleType.FIRST_CYCLE) || dr.getSpecializationAreaSet().size() == 0) { programmeRequirements = applyMessageArguments(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.four.two.programmerequirements.template.noareas"), labelThe, graduateDegree, ectsCredits); } else { List<String> areas = new ArrayList<String>(); for (DegreeSpecializationArea area : dr.getSpecializationAreaSet()) { areas.add(area.getName().getContent(getLanguage())); } programmeRequirements = applyMessageArguments(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.four.two.programmerequirements.template.withareas"), labelThe, graduateDegree, ectsCredits, Integer.toString(areas.size()), StringUtils.join(areas, "; "), officialPublication); } programmeRequirements = programmeRequirements.substring(0, 1).toUpperCase() + programmeRequirements.substring(1); addParameter("programmeRequirements", programmeRequirements); } protected void addProfessionalStatus() { String professionalStatus; if (!CycleType.SECOND_CYCLE.equals(getDocumentRequest().getRequestedCycle())) { professionalStatus = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.professionalstatus.notapplicable"); } else { DiplomaSupplementRequest request = (DiplomaSupplementRequest) getDocumentRequest(); String degreeSigla = request.getRegistration().getDegree().getSigla(); if (degreeSigla.equals("MA")) { professionalStatus = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.professionalstatus.credited.arquitect.withintership"); } else if (degreeSigla.equals("MMA") || degreeSigla.equals("MQ") || degreeSigla.equals("MUOT") || degreeSigla.equals("MPOT") || degreeSigla.equals("MFarm")) { professionalStatus = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.professionalstatus.notapplicable"); } else { professionalStatus = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.professionalstatus.credited.engineer"); } } addParameter("professionalStatus", professionalStatus); } private void addExtraCurricularActivities() { Student student = getDocumentRequest().getStudent(); if (!student.getExtraCurricularActivitySet().isEmpty()) { List<String> activities = new ArrayList<String>(); Map<ExtraCurricularActivityType, List<ExtraCurricularActivity>> activityMap = new HashMap<ExtraCurricularActivityType, List<ExtraCurricularActivity>>(); for (ExtraCurricularActivity activity : student.getExtraCurricularActivitySet()) { if (!activityMap.containsKey(activity.getType())) { activityMap.put(activity.getType(), new ArrayList<ExtraCurricularActivity>()); } activityMap.get(activity.getType()).add(activity); } for (Entry<ExtraCurricularActivityType, List<ExtraCurricularActivity>> entry : activityMap.entrySet()) { StringBuilder activityText = new StringBuilder(); activityText.append(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.six.one.extracurricularactivity.heading")); activityText.append(SINGLE_SPACE); activityText.append(entry.getKey().getName().getContent(getLanguage())); activityText.append(SINGLE_SPACE); List<String> activityTimings = new ArrayList<String>(); for (ExtraCurricularActivity activity : entry.getValue()) { activityTimings.add(BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.six.one.extracurricularactivity.time.heading") + SINGLE_SPACE + activity.getStart().toString("MM-yyyy") + SINGLE_SPACE + BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.six.one.extracurricularactivity.time.separator") + SINGLE_SPACE + activity.getEnd().toString("MM-yyyy")); } activityText.append(StringUtils.join(activityTimings, ", ")); activities.add(activityText.toString()); } addParameter("extraCurricularActivities", StringUtils.join(activities, '\n') + "."); } else { addParameter("extraCurricularActivities", BundleUtil.getString(Bundle.ACADEMIC, getLocale(), "diploma.supplement.six.one.extracurricularactivity.none")); } } private String applyMessageArguments(String message, String... args) { for (int i = 0; i < args.length; i++) { message = message.replaceAll("\\{" + i + "\\}", args[i]); } return message; } private void addEntriesParameters() { final List<AcademicUnitEntry> identifiers = new ArrayList<AcademicUnitEntry>(); if (getDocumentRequest().hasRegistration()) { Registration registration = getDocumentRequest().getRegistration(); final List<DiplomaSupplementEntry> entries = new ArrayList<DiplomaSupplementEntry>(); final Map<Unit, String> academicUnitIdentifiers = new HashMap<Unit, String>(); for (ICurriculumEntry entry : registration.getCurriculum(getDocumentRequest().getRequestedCycle()) .getCurriculumEntries()) { entries.add(new DiplomaSupplementEntry(entry, academicUnitIdentifiers)); } Collections.sort(entries); addParameter("entries", entries); for (final Entry<Unit, String> entry2 : academicUnitIdentifiers.entrySet()) { identifiers.add(new AcademicUnitEntry(entry2)); } } else { addParameter("entries", new ArrayList<DiplomaSupplementEntry>()); } addParameter("academicUnitIdentifiers", identifiers); } static final public Comparator<DiplomaSupplementEntry> COMPARATOR = new Comparator<DiplomaSupplementEntry>() { @Override public int compare(DiplomaSupplementEntry o1, DiplomaSupplementEntry o2) { final int c = o1.getExecutionYear().compareTo(o2.getExecutionYear()); return c == 0 ? Collator.getInstance().compare(o1.getName(), o2.getName()) : c; } }; public class DiplomaSupplementEntry implements Comparable<DiplomaSupplementEntry> { private final ICurriculumEntry entry; private final String executionYear; private final String name; private final String type; private final String duration; private final BigDecimal ectsCreditsForCurriculum; private final String gradeValue; private final String ectsScale; private final String academicUnitId; public DiplomaSupplementEntry(final ICurriculumEntry entry, final Map<Unit, String> academicUnitIdentifiers) { this.entry = entry; this.executionYear = entry.getExecutionYear().getYear(); this.name = getMLSTextContent(entry.getPresentationName()); DateTime processingDate = computeProcessingDateToLockECTSTableUse(); if (entry instanceof IEnrolment) { IEnrolment enrolment = (IEnrolment) entry; this.type = BundleUtil.getString(Bundle.ENUMERATION, getLocale(), enrolment.getEnrolmentTypeName()); this.duration = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), enrolment.isAnual() ? "diploma.supplement.annual" : "diploma.supplement.semestral"); this.ectsScale = enrolment.getEctsGrade(getDocumentRequest().getRegistration().getLastStudentCurricularPlan(), processingDate).getValue(); } else if (entry instanceof Dismissal && ((Dismissal) entry).getCredits().isEquivalence()) { Dismissal dismissal = (Dismissal) entry; this.type = BundleUtil.getString(Bundle.ENUMERATION, getLocale(), dismissal.getEnrolmentTypeName()); this.duration = BundleUtil.getString(Bundle.ACADEMIC, getLocale(), dismissal.isAnual() ? "diploma.supplement.annual" : "diploma.supplement.semestral"); this.ectsScale = dismissal.getEctsGrade(processingDate).getValue(); } else { throw new Error("The roof is on fire"); } this.ectsCreditsForCurriculum = entry.getEctsCreditsForCurriculum(); this.academicUnitId = obtainAcademicUnitIdentifier(academicUnitIdentifiers); this.gradeValue = entry.getGrade().getValue(); } private DateTime computeProcessingDateToLockECTSTableUse() { DateTime date = documentRequestDomainReference.getProcessingDate(); return date != null ? date : new DateTime(); } public ICurriculumEntry getEntry() { return entry; } public String getExecutionYear() { return executionYear; } public String getName() { return name; } public String getType() { return type; } public String getDuration() { return duration; } public BigDecimal getEctsCreditsForCurriculum() { return ectsCreditsForCurriculum; } public String getGradeValue() { return gradeValue; } public String getEctsScale() { return ectsScale; } public String getAcademicUnitId() { return academicUnitId; } private String obtainAcademicUnitIdentifier(final Map<Unit, String> academicUnitIdentifiers) { final Unit unit = entry instanceof ExternalEnrolment ? ((ExternalEnrolment) entry).getAcademicUnit() : Bennu.getInstance() .getInstitutionUnit(); return getAcademicUnitIdentifier(academicUnitIdentifiers, unit); } @Override public int compareTo(final DiplomaSupplementEntry o) { return COMPARATOR.compare(this, o); } } public class AcademicUnitEntry { private final String identifier; private final String name; public AcademicUnitEntry(final Entry<Unit, String> entry) { this.identifier = entry.getValue(); Unit unit = entry.getKey(); String name = getMLSTextContent(unit.getNameI18n()); List<Unit> univs = unit.getParentUnits().stream().filter(u -> u.isUniversityUnit()).collect(Collectors.toList()); if (!univs.isEmpty()) { final UniversityUnit institutionsUniversityUnit = getUniversity(getDocumentRequest().getRequestDate()); if (univs.contains(institutionsUniversityUnit)) { name = getMLSTextContent(institutionsUniversityUnit.getNameI18n()) + ", " + name; } else { name = getMLSTextContent(univs.iterator().next().getNameI18n()) + ", " + name; } } this.name = name; } public String getIdentifier() { return identifier; } public String getName() { return name; } } }