/** * Copyright © ${project.inceptionYear} Instituto Superior Técnico * * This file is part of Fenix IST. * * Fenix IST 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. * * Fenix IST 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 Fenix IST. If not, see <http://www.gnu.org/licenses/>. */ package pt.ist.fenix.domain.student.importation; import java.io.BufferedOutputStream; import java.io.ByteArrayOutputStream; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.collections.Predicate; import org.fenixedu.academic.FenixEduAcademicConfiguration; import org.fenixedu.academic.domain.Degree; import org.fenixedu.academic.domain.DegreeCurricularPlan; import org.fenixedu.academic.domain.EntryPhase; import org.fenixedu.academic.domain.ExecutionDegree; import org.fenixedu.academic.domain.ExecutionYear; import org.fenixedu.academic.domain.Person; import org.fenixedu.academic.domain.QueueJob; import org.fenixedu.academic.domain.QueueJobResult; import org.fenixedu.academic.domain.accounting.EntryType; import org.fenixedu.academic.domain.accounting.EventType; import org.fenixedu.academic.domain.accounting.Installment; import org.fenixedu.academic.domain.accounting.PaymentCodeType; import org.fenixedu.academic.domain.accounting.paymentCodes.AccountingEventPaymentCode; import org.fenixedu.academic.domain.accounting.paymentCodes.InstallmentPaymentCode; import org.fenixedu.academic.domain.accounting.paymentPlans.GratuityPaymentPlan; import org.fenixedu.academic.domain.accounting.postingRules.AdministrativeOfficeFeeAndInsurancePR; import org.fenixedu.academic.domain.accounting.postingRules.AdministrativeOfficeFeePR; import org.fenixedu.academic.domain.administrativeOffice.AdministrativeOffice; import org.fenixedu.academic.domain.candidacy.Candidacy; import org.fenixedu.academic.domain.candidacy.DegreeCandidacy; import org.fenixedu.academic.domain.candidacy.IMDCandidacy; import org.fenixedu.academic.domain.candidacy.StandByCandidacySituation; import org.fenixedu.academic.domain.candidacy.StudentCandidacy; import org.fenixedu.academic.domain.degree.DegreeType; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.domain.organizationalStructure.UnitUtils; import org.fenixedu.academic.domain.person.RoleType; import org.fenixedu.academic.domain.student.PrecedentDegreeInformation; import org.fenixedu.academic.domain.student.Student; import org.fenixedu.academic.dto.accounting.EntryDTO; import org.fenixedu.academic.dto.accounting.EntryWithInstallmentDTO; import org.fenixedu.academic.util.Money; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.core.domain.UserLoginPeriod; import org.fenixedu.spaces.domain.Space; import org.joda.time.DateTime; import org.joda.time.YearMonthDay; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import pt.ist.fenixedu.contracts.domain.Employee; import pt.ist.fenixedu.contracts.domain.accessControl.ActiveEmployees; import pt.ist.fenixedu.tutorship.domain.TutorshipIntention; import pt.utl.ist.fenix.tools.resources.LabelFormatter; public class DgesStudentImportationProcess extends DgesStudentImportationProcess_Base { private static final Logger logger = LoggerFactory.getLogger(DgesStudentImportationProcess.class); protected DgesStudentImportationProcess() { super(); } protected DgesStudentImportationProcess(final ExecutionYear executionYear, final Space campus, final EntryPhase entryPhase, DgesStudentImportationFile dgesStudentImportationFile) { this(); init(executionYear, campus, entryPhase, dgesStudentImportationFile); } protected void init(final ExecutionYear executionYear, final Space campus, final EntryPhase entryPhase, DgesStudentImportationFile dgesStudentImportationFile) { super.init(executionYear, entryPhase); String[] args = new String[0]; if (campus == null) { throw new DomainException("error.DgesStudentImportationProcess.campus.is.null", args); } String[] args1 = {}; if (dgesStudentImportationFile == null) { throw new DomainException("error.DgesStudentImportationProcess.importation.file.is.null", args1); } setDgesStudentImportationForCampus(campus); setDgesStudentImportationFile(dgesStudentImportationFile); } @Override public QueueJobResult execute() throws Exception { ByteArrayOutputStream stream = null; PrintWriter LOG_WRITER = null; try { stream = new ByteArrayOutputStream(); LOG_WRITER = new PrintWriter(new BufferedOutputStream(stream)); importCandidates(LOG_WRITER); } catch (Throwable a) { logger.error(a.getMessage(), a); throw new RuntimeException(a); } finally { if (LOG_WRITER != null) { LOG_WRITER.close(); } stream.close(); } final QueueJobResult queueJobResult = new QueueJobResult(); queueJobResult.setContentType("text/plain"); queueJobResult.setContent(stream.toByteArray()); stream.close(); return queueJobResult; } @Override public String getFilename() { return "DgesStudentImportationProcess_result_" + getExecutionYear().getName().replaceAll("/", "-") + ".txt"; } public void importCandidates(final PrintWriter LOG_WRITER) { final List<DegreeCandidateDTO> degreeCandidateDTOs = parseDgesFile(getDgesStudentImportationFile().getContents(), getUniversityAcronym(), getEntryPhase()); final Employee employee = Employee.readByNumber(4581); LOG_WRITER.println(String.format("DGES Entries for %s : %s", getDgesStudentImportationForCampus().getName(), degreeCandidateDTOs.size())); createDegreeCandidacies(LOG_WRITER, employee, degreeCandidateDTOs); distributeTutorshipIntentions(degreeCandidateDTOs); } private void distributeTutorshipIntentions(List<DegreeCandidateDTO> degreeCandidateDTOs) { if (getEntryPhase().equals(EntryPhase.FIRST_PHASE)) { HashMap<ExecutionDegree, Integer> studentsPerExecution = new HashMap<ExecutionDegree, Integer>(); for (final DegreeCandidateDTO degreeCandidateDTO : degreeCandidateDTOs) { final ExecutionDegree executionDegree = degreeCandidateDTO.getExecutionDegree(getExecutionYear(), getDgesStudentImportationForCampus()); Integer numberOfStudents = studentsPerExecution.get(executionDegree); if (numberOfStudents != null) { numberOfStudents++; } else { numberOfStudents = 1; } studentsPerExecution.put(executionDegree, numberOfStudents); } for (ExecutionDegree executionDegree : studentsPerExecution.keySet()) { int numberOfStudents = studentsPerExecution.get(executionDegree); int numberOfTutors = TutorshipIntention.getTutorshipIntentions(executionDegree).size(); if (numberOfTutors > 0) { int exceedingStudents = numberOfStudents % numberOfTutors; int studentPerTutor = numberOfStudents / numberOfTutors; for (TutorshipIntention tutorshipIntention : TutorshipIntention.getTutorshipIntentions(executionDegree)) { tutorshipIntention.setMaxStudentsToTutor(studentPerTutor); if (exceedingStudents > 0) { tutorshipIntention.setMaxStudentsToTutor(tutorshipIntention.getMaxStudentsToTutor() + 1); exceedingStudents--; } } } } } } private void createDegreeCandidacies(final PrintWriter LOG_WRITER, final Employee employee, final List<DegreeCandidateDTO> degreeCandidateDTOs) { int processed = 0; int personsCreated = 0; String prefix = FenixEduAcademicConfiguration.getConfiguration().dgesUsernamePrefix(); for (final DegreeCandidateDTO degreeCandidateDTO : degreeCandidateDTOs) { if (++processed % 150 == 0) { logger.info("Processed :" + processed); } int studentNumber = Student.generateStudentNumber(); logCandidate(LOG_WRITER, degreeCandidateDTO); Person person = null; try { person = degreeCandidateDTO.getMatchingPerson(); // Person may not yet have a user, so we will create it if (person.getUser() == null) { person.setUser(new User(prefix + studentNumber)); } } catch (DegreeCandidateDTO.NotFoundPersonException e) { person = degreeCandidateDTO.createPerson(prefix + studentNumber); logCreatedPerson(LOG_WRITER, person); personsCreated++; } catch (DegreeCandidateDTO.TooManyMatchedPersonsException e) { logTooManyMatchsForCandidate(LOG_WRITER, degreeCandidateDTO); continue; } catch (DegreeCandidateDTO.MatchingPersonException e) { throw new RuntimeException(e); } if (person.getStudent() != null && !person.getStudent().getRegistrationsSet().isEmpty()) { logCandidateIsStudentWithRegistrationAlreadyExists(LOG_WRITER, degreeCandidateDTO, person); continue; } if (person.getTeacher() != null || RoleType.TEACHER.isMember(person.getUser())) { logCandidateIsTeacher(LOG_WRITER, degreeCandidateDTO, person); continue; } if (new ActiveEmployees().isMember(person.getUser()) || person.getEmployee() != null) { logCandidateIsEmployee(LOG_WRITER, degreeCandidateDTO, person); } RoleType.grant(RoleType.CANDIDATE, person.getUser()); UserLoginPeriod.createOpenPeriod(person.getUser()); if (person.getStudent() == null) { // Ensure that the same student number is created new Student(person, studentNumber); logCreatedStudent(LOG_WRITER, person.getStudent()); } voidPreviousCandidacies(person, degreeCandidateDTO.getExecutionDegree(getExecutionYear(), getDgesStudentImportationForCampus())); final StudentCandidacy studentCandidacy = createCandidacy(employee, degreeCandidateDTO, person); new StandByCandidacySituation(studentCandidacy, employee.getPerson()); createAvailableAccountingEventsPaymentCodes(person, studentCandidacy); createAdministrativeOfficeFeePaymentCode(person, studentCandidacy); } } private void createAdministrativeOfficeFeePaymentCode(Person person, StudentCandidacy studentCandidacy) { AdministrativeOffice office = getAdministrativeOffice(studentCandidacy.getDegreeCurricularPlan()); AdministrativeOfficeFeeAndInsurancePR administrativeOfficePostingRule = findAdministrativeOfficeFeeAndInsurancePostingRule(office); studentCandidacy.addAvailablePaymentCodes(AccountingEventPaymentCode.create( PaymentCodeType.ADMINISTRATIVE_OFFICE_FEE_AND_INSURANCE, new YearMonthDay(), administrativeOfficePostingRule .getAdministrativeOfficeFeePaymentLimitDate(getExecutionYear().getBeginDateYearMonthDay() .toDateTimeAtMidnight(), getExecutionYear().getEndDateYearMonthDay().toDateTimeAtMidnight()), null, calculateAdministrativeOfficeFeeAndInsuranceAmount(office), calculateAdministrativeOfficeFeeAndInsuranceAmount(office), person)); } private Money calculateAdministrativeOfficeFeeAndInsuranceAmount(final AdministrativeOffice office) { return calculateAdministrativeOfficeFeeAmount(office).add(calculateInsuranceAmount(office)); } private Money calculateAdministrativeOfficeFeeAmount(AdministrativeOffice office) { return findAdministrativeOfficeFeePostingRule(office).getFixedAmount(); } private AdministrativeOfficeFeeAndInsurancePR findAdministrativeOfficeFeeAndInsurancePostingRule(AdministrativeOffice office) { return (AdministrativeOfficeFeeAndInsurancePR) office.getServiceAgreementTemplate().findPostingRuleByEventType( EventType.ADMINISTRATIVE_OFFICE_FEE_INSURANCE); } private AdministrativeOfficeFeePR findAdministrativeOfficeFeePostingRule(AdministrativeOffice office) { return (AdministrativeOfficeFeePR) office.getServiceAgreementTemplate().findPostingRuleByEventType( EventType.ADMINISTRATIVE_OFFICE_FEE); } private Money calculateInsuranceAmount(AdministrativeOffice office) { AdministrativeOfficeFeeAndInsurancePR feeAndInsurancePostingRule = findAdministrativeOfficeFeeAndInsurancePostingRule(office); return feeAndInsurancePostingRule.getInsuranceAmount( getExecutionYear().getBeginDateYearMonthDay().toDateTimeAtMidnight(), getExecutionYear().getEndDateYearMonthDay() .toDateTimeAtMidnight()); } private AdministrativeOffice getAdministrativeOffice(final DegreeCurricularPlan degreeCurricularPlan) { return degreeCurricularPlan.getDegree().getAdministrativeOffice(); } private void createAvailableAccountingEventsPaymentCodes(final Person person, final StudentCandidacy studentCandidacy) { ExecutionDegree executionDegree = studentCandidacy.getExecutionDegree(); GratuityPaymentPlan paymentPlan = getGratuityPaymentPlanForFirstTimeStudents(executionDegree); Money totalAmount = Money.ZERO; LabelFormatter descriptionForEntryType = getDescriptionForEntryType(executionDegree.getDegree(), EntryType.GRATUITY_FEE); for (Installment installment : paymentPlan.getInstallmentsSortedByEndDate()) { EntryWithInstallmentDTO entryDTO = new EntryWithInstallmentDTO(EntryType.GRATUITY_FEE, null, installment.getAmount(), descriptionForEntryType, installment); studentCandidacy.addAvailablePaymentCodes(createInstallmentPaymentCode(entryDTO, person.getStudent())); totalAmount = totalAmount.add(installment.getAmount()); } EntryDTO fullPaymentEntryDTO = new EntryDTO(EntryType.GRATUITY_FEE, null, totalAmount, Money.ZERO, totalAmount, descriptionForEntryType, totalAmount); studentCandidacy.addAvailablePaymentCodes(createAccountingEventPaymentCode(fullPaymentEntryDTO, person.getStudent(), paymentPlan)); } private GratuityPaymentPlan getGratuityPaymentPlanForFirstTimeStudents(final ExecutionDegree executionDegree) { List<GratuityPaymentPlan> paymentPlanList = executionDegree.getDegreeCurricularPlan().getServiceAgreementTemplate() .getGratuityPaymentPlansFor(getExecutionYear()); for (GratuityPaymentPlan paymentPlan : paymentPlanList) { if (paymentPlan.isForFirstTimeInstitutionStudents()) { return paymentPlan; } } return null; } private void logCreatedStudent(final PrintWriter LOG_WRITER, final Student student) { LOG_WRITER.println("Created student"); } private void logCreatedPerson(final PrintWriter LOG_WRITER, final Person person) { LOG_WRITER.println("Created person"); } private void logCandidate(final PrintWriter LOG_WRITER, DegreeCandidateDTO degreeCandidateDTO) { LOG_WRITER.println("-------------------------------------------------------------------"); LOG_WRITER.println("Processing: " + degreeCandidateDTO.toString()); } private StudentCandidacy createCandidacy(final Employee employee, final DegreeCandidateDTO degreeCandidateDTO, final Person person) { final ExecutionDegree executionDegree = degreeCandidateDTO.getExecutionDegree(getExecutionYear(), getDgesStudentImportationForCampus()); StudentCandidacy candidacy = null; if (executionDegree.getDegree().getDegreeType() == DegreeType.BOLONHA_DEGREE) { candidacy = new DegreeCandidacy(person, executionDegree, employee.getPerson(), degreeCandidateDTO.getEntryGrade(), degreeCandidateDTO.getContigent(), degreeCandidateDTO.getIngression(), degreeCandidateDTO.getEntryPhase(), degreeCandidateDTO.getPlacingOption()); } else if (executionDegree.getDegree().getDegreeType() == DegreeType.BOLONHA_INTEGRATED_MASTER_DEGREE) { candidacy = new IMDCandidacy(person, executionDegree, employee.getPerson(), degreeCandidateDTO.getEntryGrade(), degreeCandidateDTO.getContigent(), degreeCandidateDTO.getIngression(), degreeCandidateDTO.getEntryPhase(), degreeCandidateDTO.getPlacingOption()); } else { throw new RuntimeException("Unexpected degree type from DGES file"); } candidacy.setHighSchoolType(degreeCandidateDTO.getHighSchoolType()); candidacy.setFirstTimeCandidacy(true); createPrecedentDegreeInformation(candidacy, degreeCandidateDTO); candidacy.setDgesStudentImportationProcess(this); return candidacy; } private void createPrecedentDegreeInformation(final StudentCandidacy studentCandidacy, final DegreeCandidateDTO degreeCandidateDTO) { final PrecedentDegreeInformation precedentDegreeInformation = studentCandidacy.getPrecedentDegreeInformation(); precedentDegreeInformation.setStudentCandidacy(studentCandidacy); precedentDegreeInformation.setConclusionGrade(degreeCandidateDTO.getHighSchoolFinalGrade()); precedentDegreeInformation.setDegreeDesignation(degreeCandidateDTO.getHighSchoolDegreeDesignation()); precedentDegreeInformation.setInstitution(UnitUtils.readExternalInstitutionUnitByName(degreeCandidateDTO .getHighSchoolName())); } private void voidPreviousCandidacies(Person person, ExecutionDegree executionDegree) { for (Candidacy candidacy : person.getCandidaciesSet()) { if (candidacy instanceof StudentCandidacy) { StudentCandidacy studentCandidacy = (StudentCandidacy) candidacy; if (studentCandidacy.getExecutionDegree().getExecutionYear() == executionDegree.getExecutionYear() && !studentCandidacy.isConcluded()) { studentCandidacy.cancelCandidacy(); } } } } private void logCandidateIsEmployee(final PrintWriter LOG_WRITER, DegreeCandidateDTO degreeCandidateDTO, Person person) { LOG_WRITER.println(String.format("CANDIDATE WITH ID %s IS EMPLOYEE WITH NUMBER %s", degreeCandidateDTO.getDocumentIdNumber(), person.getEmployee().getEmployeeNumber())); } private void logCandidateIsTeacher(final PrintWriter LOG_WRITER, DegreeCandidateDTO degreeCandidateDTO, Person person) { LOG_WRITER.println(String.format("CANDIDATE WITH ID %s IS TEACHER WITH USERNAME %s", degreeCandidateDTO.getDocumentIdNumber(), person.getUsername())); } private void logCandidateIsStudentWithRegistrationAlreadyExists(final PrintWriter LOG_WRITER, DegreeCandidateDTO degreeCandidateDTO, Person person) { LOG_WRITER.println(String.format("CANDIDATE WITH ID %s IS THE STUDENT %s WITH REGISTRATIONS", degreeCandidateDTO.getDocumentIdNumber(), person.getStudent().getStudentNumber())); } private void logTooManyMatchsForCandidate(final PrintWriter LOG_WRITER, DegreeCandidateDTO degreeCandidateDTO) { LOG_WRITER.println(String.format("CANDIDATE WITH ID %s HAS MANY PERSONS", degreeCandidateDTO.getDocumentIdNumber())); } String getUniversityAcronym() { return "ALAMEDA".equals(getDgesStudentImportationForCampus().getName()) ? ALAMEDA_UNIVERSITY : TAGUS_UNIVERSITY; } public static boolean canRequestJob() { return QueueJob.getUndoneJobsForClass(DgesStudentImportationProcess.class).isEmpty(); } public static List<DgesStudentImportationProcess> readAllJobs(final ExecutionYear executionYear) { List<DgesStudentImportationProcess> jobList = new ArrayList<DgesStudentImportationProcess>(); CollectionUtils.select(executionYear.getDgesBaseProcessSet(), new Predicate() { @Override public boolean evaluate(Object arg0) { return (arg0 instanceof DgesStudentImportationProcess); } }, jobList); return jobList; } public static List<DgesStudentImportationProcess> readDoneJobs(final ExecutionYear executionYear) { List<DgesStudentImportationProcess> jobList = new ArrayList<DgesStudentImportationProcess>(); CollectionUtils.select(executionYear.getDgesBaseProcessSet(), new Predicate() { @Override public boolean evaluate(Object arg0) { return (arg0 instanceof DgesStudentImportationProcess) && ((QueueJob) arg0).getDone(); } }, jobList); return jobList; } public static List<DgesStudentImportationProcess> readUndoneJobs(final ExecutionYear executionYear) { return new ArrayList(CollectionUtils.subtract(readAllJobs(executionYear), readDoneJobs(executionYear))); } public static List<DgesStudentImportationProcess> readPendingJobs(final ExecutionYear executionYear) { List<DgesStudentImportationProcess> jobList = new ArrayList<DgesStudentImportationProcess>(); CollectionUtils.select(executionYear.getDgesBaseProcessSet(), new Predicate() { @Override public boolean evaluate(Object arg0) { return (arg0 instanceof DgesStudentImportationProcess) && ((QueueJob) arg0).getIsNotDoneAndNotCancelled(); } }, jobList); return jobList; } public LabelFormatter getDescriptionForEntryType(final Degree degree, EntryType entryType) { final LabelFormatter labelFormatter = new LabelFormatter(); labelFormatter.appendLabel(entryType.name(), "enum").appendLabel(" (").appendLabel(degree.getDegreeType().name(), "enum") .appendLabel(" - ").appendLabel(degree.getNameFor(getExecutionYear()).getContent()).appendLabel(" - ") .appendLabel(getExecutionYear().getYear()).appendLabel(")"); return labelFormatter; } private AccountingEventPaymentCode createAccountingEventPaymentCode(final EntryDTO entryDTO, final Student student, final GratuityPaymentPlan paymentPlan) { final YearMonthDay installmentEndDate = new DateTime().plusDays(18).toYearMonthDay(); return AccountingEventPaymentCode.create(PaymentCodeType.GRATUITY_FIRST_INSTALLMENT, new YearMonthDay(), installmentEndDate, null, entryDTO.getAmountToPay(), entryDTO.getAmountToPay(), student.getPerson()); } private InstallmentPaymentCode createInstallmentPaymentCode(final EntryWithInstallmentDTO entry, final Student student) { final YearMonthDay installmentEndDate = new DateTime().plusDays(18).toYearMonthDay(); if (entry.getInstallment().getOrder() == 1) { return InstallmentPaymentCode.create(PaymentCodeType.GRATUITY_FIRST_INSTALLMENT, new YearMonthDay(), installmentEndDate, null, entry.getInstallment(), entry.getAmountToPay(), entry.getAmountToPay(), student); } return InstallmentPaymentCode.create(PaymentCodeType.GRATUITY_FIRST_INSTALLMENT, new YearMonthDay(), entry .getInstallment().getEndDate(), null, entry.getInstallment(), entry.getAmountToPay(), entry.getAmountToPay(), student); } }