/**
* 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.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.collections.CollectionUtils;
import org.fenixedu.academic.domain.Country;
import org.fenixedu.academic.domain.Degree;
import org.fenixedu.academic.domain.ExecutionDegree;
import org.fenixedu.academic.domain.Person;
import org.fenixedu.academic.domain.StudentCurricularPlan;
import org.fenixedu.academic.domain.candidacyProcess.mobility.MobilityAgreement;
import org.fenixedu.academic.domain.degreeStructure.CycleType;
import org.fenixedu.academic.domain.exceptions.DomainException;
import org.fenixedu.academic.domain.mobility.outbound.OutboundMobilityCandidacySubmission.Calculator;
import org.fenixedu.academic.domain.organizationalStructure.UniversityUnit;
import org.fenixedu.academic.domain.student.Registration;
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.fenixedu.commons.spreadsheet.Spreadsheet;
import org.fenixedu.commons.spreadsheet.Spreadsheet.Row;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pt.ist.fenixframework.Atomic;
public class OutboundMobilityCandidacyContestGroup extends OutboundMobilityCandidacyContestGroup_Base implements
Comparable<OutboundMobilityCandidacyContestGroup> {
private static final Logger logger = LoggerFactory.getLogger(OutboundMobilityCandidacyContestGroup.class);
public OutboundMobilityCandidacyContestGroup(final OutboundMobilityCandidacyContest contest,
final Set<ExecutionDegree> executionDegrees) {
if (contest == null) {
throw new NullPointerException("error.OutboundMobilityCandidacyContestGroup.must.have.contest");
}
if (executionDegrees.isEmpty()) {
throw new NullPointerException("error.OutboundMobilityCandidacyContestGroup.must.have.execution.degree");
}
setRootDomainObject(Bennu.getInstance());
addOutboundMobilityCandidacyContest(contest);
getExecutionDegreeSet().addAll(executionDegrees);
// 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.
getOutboundMobilityCandidacyContestSet().size();
getExecutionDegreeSet().size();
}
public OutboundMobilityCandidacyContestGroup(final Set<ExecutionDegree> executionDegrees) {
if (executionDegrees.isEmpty()) {
throw new NullPointerException("error.OutboundMobilityCandidacyContestGroup.must.have.execution.degree");
}
setRootDomainObject(Bennu.getInstance());
getExecutionDegreeSet().addAll(executionDegrees);
// 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.
getOutboundMobilityCandidacyContestSet().size();
getExecutionDegreeSet().size();
}
@Override
public int compareTo(final OutboundMobilityCandidacyContestGroup o) {
final int dc = getDescription().compareTo(o.getDescription());
return dc == 0 ? getExternalId().compareTo(o.getExternalId()) : dc;
}
public String getDescription() {
final StringBuilder builder = new StringBuilder();
for (final ExecutionDegree executionDegree : getSortedExecutionDegrees()) {
if (builder.length() > 0) {
builder.append(", ");
}
builder.append(executionDegree.getDegree().getSigla());
}
return builder.toString();
}
public SortedSet<ExecutionDegree> getSortedExecutionDegrees() {
final SortedSet<ExecutionDegree> result = new TreeSet<ExecutionDegree>(ExecutionDegree.COMPARATOR_BY_DEGREE_CODE);
result.addAll(getExecutionDegreeSet());
return result;
}
@Atomic
public void addExecutionDegreeService(final ExecutionDegree executionDegree) {
addExecutionDegree(executionDegree);
}
@Atomic
public void removeExecutionDegreeService(final ExecutionDegree executionDegree) {
removeExecutionDegree(executionDegree);
}
@Atomic
public void addMobilityCoordinatorService(final Person person) {
addMobilityCoordinator(person);
}
@Atomic
public void removeMobilityCoordinatorService(final Person person) {
removeMobilityCoordinator(person);
}
public static OutboundMobilityCandidacyContestGroup findOrCreateGroup(final ExecutionDegree executionDegree) {
for (final OutboundMobilityCandidacyContestGroup mobilityGroup : executionDegree
.getOutboundMobilityCandidacyContestGroupSet()) {
if (mobilityGroup.getExecutionDegreeSet().size() == 1) {
return mobilityGroup;
}
}
return new OutboundMobilityCandidacyContestGroup(Collections.singleton(executionDegree));
}
public void delete() {
getExecutionDegreeSet().clear();
getMobilityCoordinatorSet().clear();
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
contest.delete();
}
setRootDomainObject(null);
deleteDomainObject();
}
public boolean handles(final Degree degree) {
for (final ExecutionDegree executionDegree : getExecutionDegreeSet()) {
if (executionDegree.getDegree() == degree) {
return true;
}
}
return false;
}
public Spreadsheet getCandidatesInformationSpreadSheet(final OutboundMobilityCandidacyPeriod period) {
final String filename = BundleUtil.getString(Bundle.ACADEMIC, "label.mobility.candidates.information.filename");
final Spreadsheet spreadsheetCurricularInfo = new Spreadsheet(filename);
final Spreadsheet spreadsheetOptions =
spreadsheetCurricularInfo.addSpreadsheet(BundleUtil.getString(Bundle.ACADEMIC,
"label.mobility.candidates.options"));
final Spreadsheet spreadsheetContactInformation =
spreadsheetOptions.addSpreadsheet(BundleUtil.getString(Bundle.ACADEMIC, "label.contact.information"));
final Spreadsheet spreadsheetOtherCurricularInfo =
spreadsheetOptions.addSpreadsheet(BundleUtil.getString(Bundle.ACADEMIC, "label.other.curricular.info"));
final Set<Registration> processed = new HashSet<Registration>();
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacySubmission submission = candidacy.getOutboundMobilityCandidacySubmission();
final Registration registration = submission.getRegistration();
final Calculator calculator = new Calculator(registration.getStudent());
if (!processed.contains(registration)) {
final Person person = registration.getPerson();
final Row row = spreadsheetCurricularInfo.addRow();
final BigDecimal candidacyGrade = submission.getGrade(this);
// final ICurriculum curriculum = registration.getCurriculum();
row.setCell(getString("label.username"), person.getUsername());
row.setCell(getString("label.name"), person.getName());
row.setCell(getString("label.degree"), registration.getDegree().getSigla());
row.setCell(getString("label.candidate.classification"),
candidacyGrade == null ? "" : candidacyGrade.toString());
row.setCell(getString("label.ects.first.cycle"), calculator.completedECTSCycle1.toString());
row.setCell(getString("label.ects.average"), calculator.getEctsAverage().toString());
row.setCell(getString("label.ects.average.first.and.second.cycle"), calculator
.getEctsEverateFirstAndSecondCycle().toString());
row.setCell(getString("label.ects.completed"), calculator.completedECTS.toString());
//row.setCell(getString("label.ects.pending"), calculator.getPendingEcts().toString());
row.setCell(getString("label.ects.enrolled"), calculator.enrolledECTS.toString());
for (final Registration otherRegistration : registration.getStudent().getRegistrationsSet()) {
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.getRawGrade().getValue());
fillCycleDetails(rowOCI, CycleType.FIRST_CYCLE, otherRegistration,
getString("label.ects.completed.cycle.first"), getString("label.average.cycle.first"));
fillCycleDetails(rowOCI, CycleType.SECOND_CYCLE, otherRegistration,
getString("label.ects.completed.cycle.second"), getString("label.average.cycle.second"));
}
for (final OutboundMobilityCandidacy c : submission.getSortedOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacyContest contestFromCandidacy = c.getOutboundMobilityCandidacyContest();
final MobilityAgreement mobilityAgreement = contestFromCandidacy.getMobilityAgreement();
final UniversityUnit unit = mobilityAgreement.getUniversityUnit();
final Country country = unit.getCountry();
final Row row2 = spreadsheetOptions.addRow();
row2.setCell(getString("label.username"), person.getUsername());
row2.setCell(getString("label.preference.order"), c.getPreferenceOrder());
row2.setCell(getString("label.degrees"), contestFromCandidacy.getOutboundMobilityCandidacyContestGroup()
.getDescription());
row2.setCell(getString("label.mobility.program"), mobilityAgreement.getMobilityProgram()
.getRegistrationProtocol().getDescription().getContent());
row2.setCell(getString("label.country"), country == null ? "" : country.getName());
row2.setCell(getString("label.university"), unit.getPresentationName());
}
final Row contactRow = spreadsheetContactInformation.addRow();
contactRow.setCell(getString("label.username"), person.getUsername());
contactRow.setCell(getString("label.name"), person.getName());
contactRow.setCell(getString("label.email"), person.getEmailForSendingEmails());
contactRow.setCell(getString("label.phone"), person.getDefaultPhoneNumber());
contactRow.setCell(getString("label.mobile"), person.getDefaultMobilePhoneNumber());
processed.add(registration);
}
}
}
return spreadsheetCurricularInfo;
}
public void fillCandidateOptions(Spreadsheet spreadsheet, OutboundMobilityCandidacyPeriod period) {
}
void fillCycleDetails(final Row row, final CycleType cycleType, final Registration registration, final String header1,
final String header2) {
if (isForCycle(cycleType, registration)) {
ICurriculum curriculum = registration.getCurriculum(cycleType);
row.setCell(header1, curriculum.getSumEctsCredits().toString());
row.setCell(header2, curriculum.getRawGrade().getValue());
} else {
row.setCell(header1, "");
row.setCell(header2, "");
}
}
private boolean isForCycle(final CycleType cycleType, final Registration registration) {
for (final StudentCurricularPlan studentCurricularPlan : registration.getStudentCurricularPlansSet()) {
if (studentCurricularPlan.getCycle(cycleType) != null) {
return true;
}
}
return false;
}
private String getString(final String key) {
return BundleUtil.getString(Bundle.ACADEMIC, key);
}
@Atomic
public void setGrades(final OutboundMobilityCandidacyPeriod candidacyPeriod, final String contents) {
final StringBuilder problems = new StringBuilder();
int l = 0;
for (final String line : contents.split("\r?\n")) {
l++;
final String[] parts = line.split("\t");
final Person person = Person.findByUsername(parts[0]);
if (person == null) {
// truncate to avoid long lines while printing code for username from an invalid file (xls)
problems.append(getMessage("error.mobility.outbound.username.not.valid.on.line", truncateDots(parts[0], 12),
Integer.toString(l)));
problems.append("; ");
continue;
}
try {
final BigDecimal grade = new BigDecimal(parts[1]);
final OutboundMobilityCandidacySubmission submission = candidacyPeriod.findSubmissionFor(person);
submission.setGrade(this, grade);
} catch (final NumberFormatException ex) {
// truncate to avoid long lines while printing code for grade from an invalid file (xls)
problems.append(getMessage("error.mobility.outbound.invalid.grade.on.one", truncateDots(parts[1], 4),
Integer.toString(l)));
problems.append("; ");
} catch (final ArrayIndexOutOfBoundsException ex) {
problems.append(getMessage("error.mobility.outbound.invalid.format.on.line", Integer.toString(l)));
problems.append(".");
break;
}
}
if (problems.length() > 0) {
throw new DomainException("error.mobility.outbound.unable.to.set.grades", problems.toString());
}
}
private String getMessage(final String key, final String... args) {
return BundleUtil.getString(Bundle.ACADEMIC, key, args);
}
// truncate string and add (...) if it is longer than length
private String truncateDots(String stringToTruncate, int length) {
return (stringToTruncate.length() > length) ? (stringToTruncate.substring(0, length) + "(...)") : stringToTruncate;
}
public boolean areAllStudentsGraded(final OutboundMobilityCandidacyPeriod period) {
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacySubmission submission = candidacy.getOutboundMobilityCandidacySubmission();
if (submission.getOutboundMobilityCandidacyPeriod() == period && !hasGrade(submission)) {
return false;
}
}
}
return true;
}
private boolean hasGrade(final OutboundMobilityCandidacySubmission submission) {
for (final OutboundMobilityCandidacySubmissionGrade grade : submission.getOutboundMobilityCandidacySubmissionGradeSet()) {
if (grade.getOutboundMobilityCandidacyContestGroup() == this) {
return true;
}
}
return false;
}
public boolean isCandidacySelectionConcluded(final OutboundMobilityCandidacyPeriod period) {
return getConcludedCandidateSelectionForPeriodSet().contains(period);
}
public boolean isCandidateNotificationConcluded(final OutboundMobilityCandidacyPeriod period) {
return getCandidatesNotifiedOfSelectionResultsForPeriodSet().contains(period);
}
public boolean areCandidatesNotofiedOfSelectionResults(final OutboundMobilityCandidacyPeriod period) {
return getCandidatesNotifiedOfSelectionResultsForPeriodSet().contains(period);
}
public boolean haveAllCandidatesConfirmed(final OutboundMobilityCandidacyPeriod period) {
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacySubmission submission = candidacy.getSubmissionFromSelectedCandidacy();
if (submission != null) {
if (!submission.hasConfirmedPlacement()) {
return false;
}
}
}
}
return true;
}
@Atomic
public void selectCandidates(final OutboundMobilityCandidacyPeriod period) {
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
if (contest.getOutboundMobilityCandidacyPeriod() == period) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacySubmission submission = candidacy.getSubmissionFromSelectedCandidacy();
if (submission != null) {
candidacy.unselect();
}
}
}
}
final SortedSet<OutboundMobilityCandidacySubmissionGrade> grades =
new TreeSet<OutboundMobilityCandidacySubmissionGrade>();
collectGradesForGroup(grades, period);
for (final OutboundMobilityCandidacyContestGroup otherGroup : getRootDomainObject()
.getOutboundMobilityCandidacyContestGroupSet()) {
if (otherGroup != this && intersect(otherGroup)) {
otherGroup.collectGradesForGroup(grades, period);
}
}
for (final OutboundMobilityCandidacySubmissionGrade submissionGrade : grades) {
logger.info("Selecting for: "
+ submissionGrade.getOutboundMobilityCandidacySubmission().getRegistration().getPerson().getUsername());
final BigDecimal grade = submissionGrade.getGrade();
if (grade.signum() == 1) {
submissionGrade.getOutboundMobilityCandidacySubmission().select();
}
}
}
private void unselectCandidates(final OutboundMobilityCandidacyPeriod period) {
for (final OutboundMobilityCandidacyContest contest : getOutboundMobilityCandidacyContestSet()) {
if (contest.getOutboundMobilityCandidacyPeriod() == period) {
for (final OutboundMobilityCandidacy candidacy : contest.getOutboundMobilityCandidacySet()) {
final OutboundMobilityCandidacySubmission submission = candidacy.getSubmissionFromSelectedCandidacy();
if (submission != null) {
candidacy.unselect();
}
}
}
}
}
private boolean intersect(final OutboundMobilityCandidacyContestGroup otherGroup) {
return CollectionUtils.containsAny(getExecutionDegreeSet(), otherGroup.getExecutionDegreeSet());
}
private void collectGradesForGroup(final SortedSet<OutboundMobilityCandidacySubmissionGrade> grades,
final OutboundMobilityCandidacyPeriod period) {
for (final OutboundMobilityCandidacySubmissionGrade submissionGrade : getOutboundMobilityCandidacySubmissionGradeSet()) {
final OutboundMobilityCandidacySubmission submission = submissionGrade.getOutboundMobilityCandidacySubmission();
if (submission.getOutboundMobilityCandidacyPeriod() == period) {
grades.add(submissionGrade);
}
}
}
@Atomic
public void concludeCandidateSelection(final OutboundMobilityCandidacyPeriod period) {
addConcludedCandidateSelectionForPeriod(period);
}
@Atomic
public void revertConcludeCandidateSelection(final OutboundMobilityCandidacyPeriod period) {
removeConcludedCandidateSelectionForPeriod(period);
}
@Atomic
public void concludeCandidateNotification(final OutboundMobilityCandidacyPeriod period) {
addCandidatesNotifiedOfSelectionResultsForPeriod(period);
}
@Atomic
public void revertConcludeCandidateNotification(final OutboundMobilityCandidacyPeriod period) {
removeCandidatesNotifiedOfSelectionResultsForPeriod(period);
}
}