/** * 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.phd.thesis; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.TreeSet; import org.fenixedu.academic.domain.Person; import org.fenixedu.academic.domain.caseHandling.Activity; import org.fenixedu.academic.domain.caseHandling.StartActivity; import org.fenixedu.academic.domain.exceptions.DomainException; import org.fenixedu.academic.domain.organizationalStructure.Unit; import org.fenixedu.academic.domain.phd.InternalPhdParticipant; import org.fenixedu.academic.domain.phd.PhdIndividualProgramDocumentType; import org.fenixedu.academic.domain.phd.PhdIndividualProgramProcess; import org.fenixedu.academic.domain.phd.PhdParticipant; import org.fenixedu.academic.domain.phd.PhdProgramDocumentUploadBean; import org.fenixedu.academic.domain.phd.PhdProgramProcess; import org.fenixedu.academic.domain.phd.PhdProgramProcessDocument; import org.fenixedu.academic.domain.phd.debts.PhdThesisRequestFee; import org.fenixedu.academic.domain.phd.migration.activities.SkipThesisJuryActivities; import org.fenixedu.academic.domain.phd.thesis.activities.AddJuryElement; import org.fenixedu.academic.domain.phd.thesis.activities.AddPresidentJuryElement; import org.fenixedu.academic.domain.phd.thesis.activities.AddState; import org.fenixedu.academic.domain.phd.thesis.activities.ConcludePhdProcess; import org.fenixedu.academic.domain.phd.thesis.activities.DeleteDocument; import org.fenixedu.academic.domain.phd.thesis.activities.DeleteJuryElement; import org.fenixedu.academic.domain.phd.thesis.activities.DownloadFinalThesisDocument; import org.fenixedu.academic.domain.phd.thesis.activities.DownloadProvisionalThesisDocument; import org.fenixedu.academic.domain.phd.thesis.activities.DownloadThesisRequirement; import org.fenixedu.academic.domain.phd.thesis.activities.EditJuryElement; import org.fenixedu.academic.domain.phd.thesis.activities.EditPhdThesisProcessInformation; import org.fenixedu.academic.domain.phd.thesis.activities.JuryDocumentsDownload; import org.fenixedu.academic.domain.phd.thesis.activities.JuryReporterFeedbackExternalUpload; import org.fenixedu.academic.domain.phd.thesis.activities.JuryReporterFeedbackUpload; import org.fenixedu.academic.domain.phd.thesis.activities.JuryReviewDocumentsDownload; import org.fenixedu.academic.domain.phd.thesis.activities.MoveJuryElementOrder; import org.fenixedu.academic.domain.phd.thesis.activities.PhdThesisActivity; import org.fenixedu.academic.domain.phd.thesis.activities.PrintJuryElementsDocument; import org.fenixedu.academic.domain.phd.thesis.activities.RatifyFinalThesis; import org.fenixedu.academic.domain.phd.thesis.activities.RejectJuryElements; import org.fenixedu.academic.domain.phd.thesis.activities.RejectJuryElementsDocuments; import org.fenixedu.academic.domain.phd.thesis.activities.RemindJuryReviewToReporters; import org.fenixedu.academic.domain.phd.thesis.activities.RemoveLastState; import org.fenixedu.academic.domain.phd.thesis.activities.ReplaceDocument; import org.fenixedu.academic.domain.phd.thesis.activities.RequestJuryElements; import org.fenixedu.academic.domain.phd.thesis.activities.RequestJuryReviews; import org.fenixedu.academic.domain.phd.thesis.activities.ScheduleThesisDiscussion; import org.fenixedu.academic.domain.phd.thesis.activities.SetFinalGrade; import org.fenixedu.academic.domain.phd.thesis.activities.SetPhdJuryElementRatificationEntity; import org.fenixedu.academic.domain.phd.thesis.activities.SkipScheduleThesisDiscussion; import org.fenixedu.academic.domain.phd.thesis.activities.SubmitJuryElementsDocuments; import org.fenixedu.academic.domain.phd.thesis.activities.SubmitThesis; import org.fenixedu.academic.domain.phd.thesis.activities.SwapJuryElementsOrder; import org.fenixedu.academic.domain.phd.thesis.activities.UploadDocuments; import org.fenixedu.academic.domain.phd.thesis.activities.ValidateJury; import org.fenixedu.academic.domain.phd.thesis.meeting.PhdMeetingSchedulingProcessStateType; import org.fenixedu.academic.util.Bundle; import org.fenixedu.academic.util.MultiLanguageString; import org.fenixedu.bennu.core.domain.User; import org.fenixedu.bennu.core.i18n.BundleUtil; import org.joda.time.DateTime; import org.joda.time.LocalDate; import pt.ist.fenixframework.Atomic; public class PhdThesisProcess extends PhdThesisProcess_Base { @StartActivity static public class RequestThesis extends PhdThesisActivity { @Override public void activityPreConditions(PhdThesisProcess process, User userView) { // Activity on main process ensures access control } @Override protected PhdThesisProcess executeActivity(PhdThesisProcess process, User userView, Object object) { final PhdThesisProcessBean bean = (PhdThesisProcessBean) object; LocalDate whenThesisDiscussionRequired = bean.getWhenThesisDiscussionRequired(); final PhdThesisProcess result = new PhdThesisProcess(); result.setIndividualProgramProcess(bean.getProcess()); result.addDocuments(bean.getDocuments(), userView.getPerson()); result.setWhenThesisDiscussionRequired(whenThesisDiscussionRequired); result.setWhenJuryRequired(bean.getWhenThesisDiscussionRequired()); String presidentTitlePt = MessageFormat.format(BundleUtil.getString(Bundle.PHD, new Locale("pt", "PT"), "message.phd.thesis.president.title.default"), Unit.getInstitutionAcronym()); String presidentTitleEn = MessageFormat.format(BundleUtil.getString(Bundle.PHD, new Locale("en", "EN"), "message.phd.thesis.president.title.default"), Unit.getInstitutionAcronym()); result.setPresidentTitle(new MultiLanguageString(MultiLanguageString.pt, presidentTitlePt).with( MultiLanguageString.en, presidentTitleEn)); if (!result.getIndividualProgramProcess().isMigratedProcess()) { new PhdThesisRequestFee(bean.getProcess()); } result.createState(PhdThesisProcessStateType.NEW, userView.getPerson(), bean.getRemarks()); return result; } } static private List<Activity> activities = new ArrayList<Activity>(); static { activities.add(new RequestJuryElements()); activities.add(new SubmitJuryElementsDocuments()); activities.add(new AddJuryElement()); activities.add(new EditJuryElement()); activities.add(new DeleteJuryElement()); activities.add(new SwapJuryElementsOrder()); activities.add(new MoveJuryElementOrder()); activities.add(new AddPresidentJuryElement()); activities.add(new ValidateJury()); activities.add(new PrintJuryElementsDocument()); activities.add(new RejectJuryElements()); activities.add(new SubmitThesis()); activities.add(new DownloadProvisionalThesisDocument()); activities.add(new DownloadFinalThesisDocument()); activities.add(new DownloadThesisRequirement()); activities.add(new RequestJuryReviews()); activities.add(new RemindJuryReviewToReporters()); activities.add(new JuryDocumentsDownload()); activities.add(new JuryReporterFeedbackUpload()); activities.add(new JuryReporterFeedbackExternalUpload()); activities.add(new JuryReviewDocumentsDownload()); activities.add(new ScheduleThesisDiscussion()); activities.add(new RatifyFinalThesis()); activities.add(new SetFinalGrade()); activities.add(new RejectJuryElementsDocuments()); activities.add(new ReplaceDocument()); activities.add(new AddState()); activities.add(new RemoveLastState()); activities.add(new SkipThesisJuryActivities()); activities.add(new SkipScheduleThesisDiscussion()); activities.add(new EditPhdThesisProcessInformation()); activities.add(new ConcludePhdProcess()); activities.add(new SetPhdJuryElementRatificationEntity()); activities.add(new UploadDocuments()); activities.add(new DeleteDocument()); } @Override public boolean isAllowedToManageProcess(User userView) { return this.getIndividualProgramProcess().isAllowedToManageProcess(userView); } private PhdThesisProcess() { super(); } public boolean isJuryValidated() { return getWhenJuryValidated() != null; } public boolean isFinalThesisRatified() { return getWhenFinalThesisRatified() != null; } public void swapJuryElementsOrder(ThesisJuryElement e1, ThesisJuryElement e2) { if (getThesisJuryElementsSet().contains(e1) && getThesisJuryElementsSet().contains(e2)) { final Integer order1 = e1.getElementOrder(); final Integer order2 = e2.getElementOrder(); e1.setElementOrder(order2); e2.setElementOrder(order1); } } public void deleteJuryElement(ThesisJuryElement element) { if (getThesisJuryElementsSet().contains(element)) { final Integer elementOrder = element.getElementOrder(); element.delete(); reorderJuryElements(elementOrder); } } private void reorderJuryElements(Integer removedElementOrder) { for (final ThesisJuryElement element : getOrderedThesisJuryElements()) { if (element.getElementOrder().compareTo(removedElementOrder) > 0) { element.setElementOrder(Integer.valueOf(element.getElementOrder().intValue() - 1)); } } } public TreeSet<ThesisJuryElement> getOrderedThesisJuryElements() { final TreeSet<ThesisJuryElement> result = new TreeSet<ThesisJuryElement>(ThesisJuryElement.COMPARATOR_BY_ELEMENT_ORDER); result.addAll(getThesisJuryElementsSet()); return result; } public void createState(PhdThesisProcessStateType type, Person person, String remarks) { PhdThesisProcessState.createWithInferredStateDate(this, type, person, remarks); } @Override public Person getPerson() { return getIndividualProgramProcess().getPerson(); } @Override public boolean canExecuteActivity(User userView) { return false; } @Override public List<Activity> getActivities() { return activities; } @Override public String getDisplayName() { return BundleUtil.getString(Bundle.PHD, getClass().getSimpleName()); } @Override public PhdThesisProcessState getMostRecentState() { return (PhdThesisProcessState) super.getMostRecentState(); } @Override public PhdThesisProcessStateType getActiveState() { return (PhdThesisProcessStateType) super.getActiveState(); } // TODO: find clean solution to return specific documents // grouped?? public PhdProgramProcessDocument getProvisionalThesisDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.PROVISIONAL_THESIS); } public boolean hasProvisionalThesisDocument() { return getProvisionalThesisDocument() != null; } public PhdProgramProcessDocument getFinalThesisDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.FINAL_THESIS); } public boolean hasFinalThesisDocument() { return getFinalThesisDocument() != null; } public PhdProgramProcessDocument getThesisRequirementDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.THESIS_REQUIREMENT); } public boolean hasThesisRequirementDocument() { return getThesisRequirementDocument() != null; } public boolean isConcluded() { return getActiveState() == PhdThesisProcessStateType.CONCLUDED; } public PhdProgramProcessDocument getJuryElementsDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.JURY_ELEMENTS); } public Boolean hasJuryElementsDocument() { return getJuryElementsDocument() != null; } public PhdProgramProcessDocument getJuryPresidentDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.JURY_PRESIDENT_ELEMENT); } public Boolean hasJuryPresidentDocument() { return getJuryPresidentDocument() != null; } public DateTime getWhenRequestJury() { if (getWhenJuryRequired() == null) { return null; } return getWhenJuryRequired().toDateTimeAtStartOfDay(); } public DateTime getWhenReceivedJury() { return getLastExecutionDateOf(SubmitJuryElementsDocuments.class); } public DateTime getWhenRequestedJuryReviews() { return getLastExecutionDateOf(RequestJuryReviews.class); } public PhdParticipant getParticipant(final Person person) { return getIndividualProgramProcess().getParticipant(person); } public boolean isParticipant(final Person person) { return getIndividualProgramProcess().isParticipant(person); } public ThesisJuryElement getThesisJuryElement(final Person person) { for (final ThesisJuryElement element : getThesisJuryElementsSet()) { if (element.isFor(person)) { return element; } } return null; } public boolean isPresidentJuryElement(final Person person) { return getPresidentJuryElement() != null && getPresidentJuryElement().isFor(person); } public List<PhdProgramProcessDocument> getThesisDocumentsToFeedback() { final List<PhdProgramProcessDocument> documents = new ArrayList<PhdProgramProcessDocument>(3); addDocument(documents, getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.THESIS_REQUIREMENT)); addDocument(documents, getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.THESIS_ABSTRACT)); addDocument(documents, getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.PROVISIONAL_THESIS)); addDocument(documents, getCV()); return documents; } private void addDocument(List<PhdProgramProcessDocument> documents, PhdProgramProcessDocument document) { if (document != null) { documents.add(document); } } private PhdProgramProcessDocument getCV() { final PhdProgramProcessDocument cv = getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.CV); return (cv != null) ? cv : getCandidacyProcess().getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.CV); } private PhdProgramProcess getCandidacyProcess() { return getIndividualProgramProcess().getCandidacyProcess(); } public String getProcessNumber() { return getIndividualProgramProcess().getProcessNumber(); } public Collection<ThesisJuryElement> getReportThesisJuryElements() { final Collection<ThesisJuryElement> result = new ArrayList<ThesisJuryElement>(); for (final ThesisJuryElement element : getThesisJuryElementsSet()) { if (element.getReporter().booleanValue()) { result.add(element); } } return result; } public boolean isWaitingForJuryReporterFeedback() { return getActiveState() == PhdThesisProcessStateType.WAITING_FOR_JURY_REPORTER_FEEDBACK; } public boolean isAnyDocumentToValidate() { for (final ThesisJuryElement element : getThesisJuryElementsSet()) { if (element.getReporter().booleanValue() && !element.isDocumentValidated()) { return true; } } return false; } public Collection<PhdProgramProcessDocument> getReportThesisJuryElementDocuments() { final Collection<PhdProgramProcessDocument> result = new HashSet<PhdProgramProcessDocument>(); for (final ThesisJuryElement element : getThesisJuryElementsSet()) { if (element.getReporter().booleanValue() && element.isDocumentValidated()) { result.add(element.getLastFeedbackDocument()); } } return result; } public PhdProgramProcessDocument getJuryMeetingMinutesDocument() { return getLatestDocumentVersionFor(PhdIndividualProgramDocumentType.JURY_MEETING_MINUTES); } @Override protected void addDocuments(final List<PhdProgramDocumentUploadBean> documents, final Person responsible) { for (final PhdProgramDocumentUploadBean each : documents) { if (each.isRequired()) { if (!each.hasAnyInformation()) { throw new DomainException("error.PhdThesisProcess.document.is.required.and.no.information.given", each .getType().getLocalizedName()); } } if (each.hasAnyInformation()) { addDocument(each, responsible); } } } public void rejectJuryElementsDocuments() { if (hasJuryElementsDocument()) { getJuryElementsDocument().setDocumentAccepted(false); } if (hasJuryPresidentDocument()) { getJuryPresidentDocument().setDocumentAccepted(false); } } public void deleteLastState() { if (getStates().size() <= 1) { throw new DomainException("error.PhdThesisProcess.states.size.is.one"); } getMostRecentState().delete(); } public void checkJuryPresidentNotGuider(final PhdThesisJuryElementBean bean) { final PhdIndividualProgramProcess process = getIndividualProgramProcess(); for (PhdParticipant processParticipant : process.getParticipantsSet()) { if (processParticipant == bean.getParticipant()) { if (process.isGuider(processParticipant) || process.isAssistantGuider(processParticipant)) { throw new DomainException("error.PhdThesisProcess.president.cannot.be.guider.or.assistantguider"); } break; } if (processParticipant.isFor(retrievePersonFromParticipantBean(bean))) { if (process.isGuider(processParticipant) || process.isAssistantGuider(processParticipant)) { throw new DomainException("error.PhdThesisProcess.president.cannot.be.guider.or.assistantguider"); } } } } public void checkJuryReporterNotGuider(final PhdThesisJuryElementBean bean) { if (!bean.isReporter()) { return; } final PhdIndividualProgramProcess process = getIndividualProgramProcess(); for (PhdParticipant processParticipant : process.getParticipantsSet()) { if (processParticipant == bean.getParticipant()) { if (process.isGuider(processParticipant) || process.isAssistantGuider(processParticipant)) { throw new DomainException("error.PhdThesisProcess.reporter.cannot.be.guider.or.assistantguider"); } break; } if (processParticipant.isFor(retrievePersonFromParticipantBean(bean))) { if (process.isGuider(processParticipant) || process.isAssistantGuider(processParticipant)) { throw new DomainException("error.PhdThesisProcess.reporter.cannot.be.guider.or.assistantguider"); } } } } private Person retrievePersonFromParticipantBean(PhdThesisJuryElementBean participantBean) { if (participantBean.getParticipant() == null) { return participantBean.getPerson(); } if (participantBean.getParticipant() != null && participantBean.getParticipant().isInternal()) { return ((InternalPhdParticipant) participantBean.getParticipant()).getPerson(); } return null; } public PhdMeetingSchedulingProcessStateType getPhdMeetingSchedulingActiveState() { if (getMeetingProcess() == null) { return null; } return getMeetingProcess().getActiveState(); } public List<PhdThesisProcessStateType> getPossibleNextStates() { PhdThesisProcessStateType currentState = getActiveState(); if (currentState == null) { return Collections.singletonList(PhdThesisProcessStateType.NEW); } switch (currentState) { case NEW: return Arrays.asList(PhdThesisProcessStateType.WAITING_FOR_JURY_CONSTITUTION, PhdThesisProcessStateType.WAITING_FOR_JURY_REPORTER_FEEDBACK); case WAITING_FOR_JURY_CONSTITUTION: return Arrays.asList(PhdThesisProcessStateType.JURY_WAITING_FOR_VALIDATION, PhdThesisProcessStateType.JURY_VALIDATED, PhdThesisProcessStateType.WAITING_FOR_JURY_REPORTER_FEEDBACK); case JURY_WAITING_FOR_VALIDATION: return Arrays.asList(PhdThesisProcessStateType.JURY_VALIDATED, PhdThesisProcessStateType.WAITING_FOR_JURY_REPORTER_FEEDBACK); case JURY_VALIDATED: return Collections.singletonList(PhdThesisProcessStateType.WAITING_FOR_JURY_REPORTER_FEEDBACK); case WAITING_FOR_JURY_REPORTER_FEEDBACK: return Collections.singletonList(PhdThesisProcessStateType.WAITING_FOR_THESIS_MEETING_SCHEDULING); case WAITING_FOR_THESIS_MEETING_SCHEDULING: return Collections.singletonList(PhdThesisProcessStateType.WAITING_FOR_THESIS_DISCUSSION_DATE_SCHEDULING); case WAITING_FOR_THESIS_DISCUSSION_DATE_SCHEDULING: return Collections.singletonList(PhdThesisProcessStateType.THESIS_DISCUSSION_DATE_SCHECULED); case THESIS_DISCUSSION_DATE_SCHECULED: return Collections.singletonList(PhdThesisProcessStateType.WAITING_FOR_THESIS_RATIFICATION); case WAITING_FOR_THESIS_RATIFICATION: return Collections.singletonList(PhdThesisProcessStateType.WAITING_FOR_FINAL_GRADE); case WAITING_FOR_FINAL_GRADE: return Collections.singletonList(PhdThesisProcessStateType.CONCLUDED); } return Collections.EMPTY_LIST; } public void removeLastState() { if (getStatesSet().size() <= 1) { throw new DomainException("phd.thesis.PhdThesisProcess.cannot.remove.state"); } getMostRecentState().delete(); } public PhdThesisProcess edit(final User userView, final PhdThesisProcessBean bean) { setWhenThesisDiscussionRequired(bean.getWhenThesisDiscussionRequired()); setWhenJuryRequired(bean.getWhenJuryRequested()); setWhenJuryValidated(bean.getWhenJuryValidated()); setWhenJuryDesignated(bean.getWhenJuryDesignated()); setWhenFinalThesisRatified(bean.getWhenFinalThesisRatified()); setConclusionDate(bean.getConclusionDate()); setFinalGrade(bean.getFinalGrade()); return this; } @Override public PhdJuryElementsRatificationEntity getPhdJuryElementsRatificationEntity() { if (super.getPhdJuryElementsRatificationEntity() == null) { return PhdJuryElementsRatificationEntity.BY_COORDINATOR; } return super.getPhdJuryElementsRatificationEntity(); } @Atomic public void createRequestFee() { new PhdThesisRequestFee(getIndividualProgramProcess()); } @Override @Deprecated public java.util.Set<org.fenixedu.academic.domain.phd.thesis.PhdThesisProcessState> getStates() { return getStatesSet(); } @Override public boolean hasAnyStates() { return !getStatesSet().isEmpty(); } }