/**
* 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.mobility.outbound;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.fenixedu.academic.domain.Country;
import org.fenixedu.academic.domain.ExecutionDegree;
import org.fenixedu.academic.domain.ExecutionInterval;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.candidacyProcess.mobility.MobilityAgreement;
import org.fenixedu.academic.domain.candidacyProcess.mobility.MobilityProgram;
import org.fenixedu.academic.domain.degreeStructure.CycleType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.organizationalStructure.UniversityUnit;
import org.fenixedu.academic.domain.period.CandidacyPeriod;
import org.fenixedu.academic.domain.student.Registration;
import org.fenixedu.academic.domain.student.RegistrationProtocol;
import org.fenixedu.academic.domain.student.curriculum.ICurriculum;
import org.fenixedu.academic.util.Bundle;
import org.fenixedu.bennu.core.domain.Bennu;
import org.fenixedu.bennu.core.i18n.BundleUtil;
import org.joda.time.DateTime;
import org.joda.time.Interval;
import org.joda.time.YearMonthDay;
import pt.ist.fenixframework.Atomic;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet;
import pt.utl.ist.fenix.tools.util.excel.Spreadsheet.Row;
public class OutboundMobilityCandidacyPeriod extends OutboundMobilityCandidacyPeriod_Base implements Comparable<CandidacyPeriod> {
public OutboundMobilityCandidacyPeriod(final ExecutionInterval executionInterval, final DateTime start, final DateTime end) {
super();
init(executionInterval, start, end);
final OutboundMobilityCandidacyPeriod previousPeriod = findPreviousPeriod();
for (final OutboundMobilityCandidacyContestGroup group : previousPeriod.getOutboundMobilityCandidacyContestGroupSet()) {
if (!group.getExecutionDegreeSet().isEmpty()) {
final OutboundMobilityCandidacyContestGroup newGroup =
new OutboundMobilityCandidacyContestGroup(group.getExecutionDegreeSet());
for (final OutboundMobilityCandidacyContest contest : group.getOutboundMobilityCandidacyContestSet()) {
final OutboundMobilityCandidacyContest newContest =
new OutboundMobilityCandidacyContest(this, newGroup, contest.getMobilityAgreement(),
contest.getVacancies());
newGroup.getOutboundMobilityCandidacyContestSet().add(newContest);
}
newGroup.getMobilityCoordinatorSet().addAll(group.getMobilityCoordinatorSet());
}
}
for (final OutboundMobilityCandidacyContest contest : previousPeriod.getOutboundMobilityCandidacyContestSet()) {
contest.getExecutionDegreeSet();
contest.getMobilityAgreement();
contest.getOutboundMobilityCandidacyContestGroup();
contest.getVacancies();
}
}
@Atomic
public static OutboundMobilityCandidacyPeriod create(final ExecutionInterval executionInterval, final DateTime start,
final DateTime end) {
return new OutboundMobilityCandidacyPeriod(executionInterval, start, end);
}
@Atomic
public OutboundMobilityCandidacyContest createOutboundMobilityCandidacyContest(final ExecutionDegree executionDegree,
final MobilityProgram mobilityProgram, final UniversityUnit unit, final Integer vacancies) {
final MobilityAgreement mobilityAgreement = findOrCreateMobilityAgreement(mobilityProgram, unit);
final OutboundMobilityCandidacyContestGroup mobilityGroup =
OutboundMobilityCandidacyContestGroup.findOrCreateGroup(executionDegree);
final OutboundMobilityCandidacyContest contest =
new OutboundMobilityCandidacyContest(this, mobilityGroup, mobilityAgreement, vacancies);
// TODO : This is a hack due to a bug in the consistency predicate or fenix-framework code.
// When the relation is initialized but never traversed, the consistency predicate always
// fails. Forcing a traversal will resolve this issue. The bug has already been solved in
// the framework, but the framework has not yet been updated on this project.
mobilityGroup.getOutboundMobilityCandidacyContestSet().size();
return contest;
}
@Atomic
public OutboundMobilityCandidacyContest createOutboundMobilityCandidacyContest(
final OutboundMobilityCandidacyContestGroup mobilityGroup, final MobilityProgram mobilityProgram,
final UniversityUnit unit, final Integer vacancies) {
final MobilityAgreement mobilityAgreement = findOrCreateMobilityAgreement(mobilityProgram, unit);
return new OutboundMobilityCandidacyContest(this, mobilityGroup, mobilityAgreement, vacancies);
}
private MobilityAgreement findOrCreateMobilityAgreement(final MobilityProgram mobilityProgram, final UniversityUnit unit) {
for (final MobilityAgreement mobilityAgreement : mobilityProgram.getMobilityAgreementsSet()) {
if (mobilityAgreement.getUniversityUnit() == unit) {
return mobilityAgreement;
}
}
return new MobilityAgreement(mobilityProgram, unit);
}
@Override
public int compareTo(final CandidacyPeriod cp) {
int i = getStart().compareTo(cp.getStart());
return i == 0 ? getExternalId().compareTo(cp.getExternalId()) : i;
}
public Interval getInterval() {
return new Interval(getStart(), getEnd());
}
public String getIntervalAsString() {
return getStart().toString("yyyy/MM/dd HH:mm") + " - " + getEnd().toString("yyyy/MM/dd HH:mm");
}
public SortedSet<OutboundMobilityCandidacyContest> getSortedOutboundMobilityCandidacyContest() {
return new TreeSet<OutboundMobilityCandidacyContest>(getOutboundMobilityCandidacyContestSet());
}
public boolean isAcceptingCandidacies() {
return isOpen();
}
public SortedSet<OutboundMobilityCandidacyContestGroup> getOutboundMobilityCandidacyContestGroupSet() {
final SortedSet<OutboundMobilityCandidacyContestGroup> result = new TreeSet<OutboundMobilityCandidacyContestGroup>();
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
result.add(contest.getOutboundMobilityCandidacyContestGroup());
}
return result;
}
public OutboundMobilityCandidacySubmission findSubmissionFor(final Person person) {
for (final Registration registration : person.getStudent().getRegistrationsSet()) {
for (final OutboundMobilityCandidacySubmission submission : registration.getOutboundMobilityCandidacySubmissionSet()) {
if (submission.getOutboundMobilityCandidacyPeriod() == this) {
return submission;
}
}
}
return null;
}
@Atomic
public void setOptionIntroductoryDestriptionService(final String optionIntroductoryDestription) {
setOptionIntroductoryDestription(optionIntroductoryDestription);
}
@Atomic
public void addOption(final String optionValue, final Boolean availableForCandidates) {
new OutboundMobilityCandidacyPeriodConfirmationOption(this, optionValue, availableForCandidates);
}
public SortedSet<OutboundMobilityCandidacyPeriodConfirmationOption> getSortedOptions() {
return new TreeSet<OutboundMobilityCandidacyPeriodConfirmationOption>(
getOutboundMobilityCandidacyPeriodConfirmationOptionSet());
}
public SortedSet<OutboundMobilityCandidacySubmission> getSortedSubmissionSet(
final OutboundMobilityCandidacyContestGroup mobilityGroup) {
final SortedSet<OutboundMobilityCandidacySubmission> result =
new TreeSet<OutboundMobilityCandidacySubmission>(new Comparator<OutboundMobilityCandidacySubmission>() {
@Override
public int compare(OutboundMobilityCandidacySubmission o1, OutboundMobilityCandidacySubmission o2) {
final BigDecimal grade1 = o1.getGradeForSerialization(mobilityGroup);
final BigDecimal grade2 = o2.getGradeForSerialization(mobilityGroup);
if ((grade1 == null && grade2 == null) || (grade1 != null && grade1.equals(grade2))) {
return o1.compareTo(o2);
}
return grade1 == null ? 1 : grade2 == null ? -1 : grade2.compareTo(grade1);
}
});
for (final OutboundMobilityCandidacySubmission submission : getOutboundMobilityCandidacySubmissionSet()) {
if (submission.hasContestInGroup(mobilityGroup)) {
result.add(submission);
}
}
return result;
}
public Spreadsheet getSelectedCandidateSpreadSheet(final OutboundMobilityCandidacyPeriod period) {
final String filename =
BundleUtil.getString(Bundle.ACADEMIC, "label.mobility.outbound.period.export.selected.candiadates.filename");
final Spreadsheet spreadsheetCandidates = new Spreadsheet(filename);
final Spreadsheet spreadsheetOtherCurricularInfo =
spreadsheetCandidates.addSpreadsheet(BundleUtil.getString(Bundle.ACADEMIC, "label.other.curricular.info"));
for (final OutboundMobilityCandidacySubmission submission : getOutboundMobilityCandidacySubmissionSet()) {
final OutboundMobilityCandidacy candidacy = submission.getSelectedCandidacy();
if (candidacy != null) {
final Registration registration = submission.getRegistration();
final Person person = registration.getPerson();
final ICurriculum curriculum = registration.getCurriculum();
final OutboundMobilityCandidacyContest contest = candidacy.getOutboundMobilityCandidacyContest();
final OutboundMobilityCandidacyContestGroup group = contest.getOutboundMobilityCandidacyContestGroup();
final MobilityAgreement mobilityAgreement = contest.getMobilityAgreement();
final MobilityProgram mobilityProgram = mobilityAgreement.getMobilityProgram();
final RegistrationProtocol registrationProtocol = mobilityProgram.getRegistrationProtocol();
final UniversityUnit universityUnit = mobilityAgreement.getUniversityUnit();
final Country country = universityUnit.getCountry();
final Row candidacyRow = spreadsheetCandidates.addRow();
candidacyRow.setCell(getString("label.mobility.program"), registrationProtocol.getDescription().getContent());
candidacyRow.setCell(getString("label.country"), country == null ? "" : country.getLocalizedName().toString());
candidacyRow.setCell(getString("label.university"), universityUnit.getPresentationName());
candidacyRow.setCell(getString("label.degrees"), group.getDescription());
candidacyRow.setCell(getString("label.vacancies"), contest.getVacancies());
candidacyRow.setCell(getString("label.username"), person.getUsername());
candidacyRow.setCell(getString("label.name"), person.getName());
candidacyRow.setCell(getString("label.degree"), registration.getDegree().getSigla());
candidacyRow.setCell(getString("label.candidate.classification"), getGrades(submission));
candidacyRow.setCell(getString("label.preference.order"), candidacy.getPreferenceOrder());
candidacyRow.setCell(getString("label.curricular.year"), curriculum.getCurricularYear());
candidacyRow.setCell(getString("label.ects.completed.degree"), curriculum.getSumEctsCredits().toString());
candidacyRow.setCell(getString("label.average.degree"), curriculum.getAverage().toString());
group.fillCycleDetails(candidacyRow, CycleType.FIRST_CYCLE, registration,
getString("label.ects.completed.cycle.first"), getString("label.average.cycle.first"));
group.fillCycleDetails(candidacyRow, CycleType.SECOND_CYCLE, registration,
getString("label.ects.completed.cycle.second"), getString("label.average.cycle.second"));
candidacyRow.setCell(getString("label.email"), person.getEmailForSendingEmails());
candidacyRow.setCell(getString("label.phone"), person.getDefaultPhoneNumber());
candidacyRow.setCell(getString("label.mobile"), person.getDefaultMobilePhoneNumber());
for (final Registration otherRegistration : registration.getStudent().getRegistrationsSet()) {
if (otherRegistration != registration) {
final Row rowOCI = spreadsheetOtherCurricularInfo.addRow();
final ICurriculum curriculumOther = otherRegistration.getCurriculum();
rowOCI.setCell(getString("label.username"), person.getUsername());
rowOCI.setCell(getString("label.name"), person.getName());
rowOCI.setCell(getString("label.degree"), otherRegistration.getDegree().getSigla());
rowOCI.setCell(getString("label.curricular.year"), curriculumOther.getCurricularYear());
rowOCI.setCell(getString("label.ects.completed.degree"), curriculumOther.getSumEctsCredits().toString());
rowOCI.setCell(getString("label.average.degree"), curriculumOther.getAverage().toString());
group.fillCycleDetails(rowOCI, CycleType.FIRST_CYCLE, otherRegistration,
getString("label.ects.completed.cycle.first"), getString("label.average.cycle.first"));
group.fillCycleDetails(rowOCI, CycleType.SECOND_CYCLE, otherRegistration,
getString("label.ects.completed.cycle.second"), getString("label.average.cycle.second"));
}
}
}
}
return spreadsheetCandidates;
}
private String getGrades(final OutboundMobilityCandidacySubmission submission) {
final Set<BigDecimal> grades = new HashSet<BigDecimal>();
final StringBuilder builder = new StringBuilder();
for (final OutboundMobilityCandidacySubmissionGrade grade : submission.getOutboundMobilityCandidacySubmissionGradeSet()) {
final BigDecimal value = grade.getGrade();
if (!grades.contains(value)) {
grades.add(value);
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(value.toString());
}
}
return builder.toString();
}
private String getString(final String key, final String... args) {
return BundleUtil.getString(Bundle.ACADEMIC, key, args);
}
@Atomic
public String selectCandidatesForAllGroups() {
boolean hasSomePlacement = false;
final StringBuilder error = new StringBuilder();
final Map<OutboundMobilityCandidacySubmission, OutboundMobilityCandidacy> selections =
new HashMap<OutboundMobilityCandidacySubmission, OutboundMobilityCandidacy>();
for (final OutboundMobilityCandidacySubmission submission : getOutboundMobilityCandidacySubmissionSet()) {
final OutboundMobilityCandidacy selectedCandidacy = submission.getSelectedCandidacy();
selections.put(submission, selectedCandidacy);
if (selectedCandidacy != null) {
hasSomePlacement = true;
}
BigDecimal gv = null;
for (final OutboundMobilityCandidacySubmissionGrade grade : submission
.getOutboundMobilityCandidacySubmissionGradeSet()) {
if (gv == null) {
gv = grade.getGrade();
} else {
if (!gv.equals(grade.getGrade())) {
final Registration registration = submission.getRegistration();
error.append(getString("label.error.student.has.different.grades.in.different.groups", registration
.getPerson().getUsername(), registration.getDegree().getSigla()));
error.append("\n");
}
}
}
}
if (error.length() > 0) {
throw new DomainException(error.toString());
}
for (final OutboundMobilityCandidacyContestGroup group : getOutboundMobilityCandidacyContestGroupSet()) {
for (final OutboundMobilityCandidacyContest contest : group.getOutboundMobilityCandidacyContestSet()) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
candidacy.unselect();
}
}
}
for (final OutboundMobilityCandidacyContestGroup group : getOutboundMobilityCandidacyContestGroupSet()) {
group.selectCandidates(this);
}
final StringBuilder result = new StringBuilder();
final SortedSet<OutboundMobilityCandidacySubmission> submissions =
new TreeSet<OutboundMobilityCandidacySubmission>(new Comparator<OutboundMobilityCandidacySubmission>() {
@Override
public int compare(OutboundMobilityCandidacySubmission o1, OutboundMobilityCandidacySubmission o2) {
final int cd = o1.getRegistration().getDegree().compareTo(o2.getRegistration().getDegree());
return cd == 0 ? o1.compareTo(o2) : cd;
}
});
submissions.addAll(getOutboundMobilityCandidacySubmissionSet());
int selectedCandidateCount = 0;
for (final OutboundMobilityCandidacySubmission submission : submissions) {
final OutboundMobilityCandidacy selectedCandidacy = submission.getSelectedCandidacy();
if (selectedCandidacy != null) {
selectedCandidateCount++;
}
if (selectedCandidacy != selections.get(submission)) {
final Registration registration = submission.getRegistration();
result.append(getString("label.changed.candidate.selection", registration.getPerson().getUsername(), registration
.getDegree().getSigla(), printPlacement(selections.get(submission)), printPlacement(selectedCandidacy)));
result.append("\n");
}
}
return hasSomePlacement ? result.toString() : getString("label.count.placed.candidates", "" + selectedCandidateCount);
}
private String printPlacement(final OutboundMobilityCandidacy candidacy) {
return candidacy == null ? "-----" : candidacy.getOutboundMobilityCandidacyContest().getMobilityAgreement()
.getUniversityUnit().getPresentationName();
}
private OutboundMobilityCandidacyPeriod findPreviousPeriod() {
OutboundMobilityCandidacyPeriod result = null;
for (final ExecutionInterval interval : Bennu.getInstance().getExecutionIntervalsSet()) {
for (final CandidacyPeriod candidacyPeriod : interval.getCandidacyPeriodsSet()) {
if (candidacyPeriod instanceof OutboundMobilityCandidacyPeriod) {
final OutboundMobilityCandidacyPeriod period = (OutboundMobilityCandidacyPeriod) candidacyPeriod;
final YearMonthDay ymd = period.getExecutionInterval().getBeginDateYearMonthDay();
if (ymd.isBefore(getExecutionInterval().getBeginDateYearMonthDay())
&& (result == null || result.getExecutionInterval().getBeginDateYearMonthDay().isBefore(ymd))) {
result = period;
}
}
}
}
return result;
}
@Atomic
public void delete() {
if (!getOutboundMobilityCandidacyContestSet().isEmpty()) {
throw new DomainException("cannot.delete.because.still.connected.to.contests");
}
for (final OutboundMobilityCandidacyPeriodConfirmationOption option : getOutboundMobilityCandidacyPeriodConfirmationOptionSet()) {
option.delete();
}
setExecutionInterval(null);
setRootDomainObject(null);
deleteDomainObject();
}
}