3122 Stöd för manuell hantering av slutsem för handledare
This commit is contained in:
parent
6b6c535e97
commit
9628c6a28f
core/src
main/java/se/su/dsv/scipro
finalseminar
ActiveParticipationRegistrationStatus.javaFinalSeminarOppositionRepo.javaFinalSeminarOppositionRepoImpl.javaFinalSeminarService.javaFinalSeminarServiceImpl.javaOpposeError.javaOppositionRegistrationErrorStatus.javaParticipateError.java
project
test/java/se/su/dsv/scipro/finalseminar
view/src
main/java/se/su/dsv/scipro/finalseminar
OpposeColumnPanel.javaParticipateColumnPanel.javaProjectOppositionPage.javaProjectOppositionPage.propertiesSeminarCRUDPanel.javaSeminarCRUDPanel.properties
test/java/se/su/dsv/scipro/finalseminar
76
core/src/main/java/se/su/dsv/scipro/finalseminar/ActiveParticipationRegistrationStatus.java
Normal file
76
core/src/main/java/se/su/dsv/scipro/finalseminar/ActiveParticipationRegistrationStatus.java
Normal file
@ -0,0 +1,76 @@
|
||||
package se.su.dsv.scipro.finalseminar;
|
||||
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class ActiveParticipationRegistrationStatus {
|
||||
ActiveParticipationRegistrationStatus() {
|
||||
}
|
||||
|
||||
public abstract <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g);
|
||||
}
|
||||
|
||||
final class TooManyParticipants extends ActiveParticipationRegistrationStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g) {
|
||||
return a.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class ManualParticipants extends ActiveParticipationRegistrationStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g) {
|
||||
return b.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class ParticipationAlreadyParticipating extends ActiveParticipationRegistrationStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g) {
|
||||
return e.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class ParticipationAlreadyHappened extends ActiveParticipationRegistrationStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g) {
|
||||
return f.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class ParticipationFinalSeminarCancelled extends ActiveParticipationRegistrationStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
Function<TooManyParticipants, A> a,
|
||||
Function<ManualParticipants, A> b,
|
||||
Function<ParticipationAlreadyParticipating, A> e,
|
||||
Function<ParticipationAlreadyHappened, A> f,
|
||||
Function<ParticipationFinalSeminarCancelled, A> g) {
|
||||
return g.apply(this);
|
||||
}
|
||||
}
|
||||
|
@ -11,5 +11,5 @@ import java.util.*;
|
||||
|
||||
@Transactional
|
||||
public interface FinalSeminarOppositionRepo extends JpaRepository<FinalSeminarOpposition, Long>, QueryDslPredicateExecutor<FinalSeminarOpposition> {
|
||||
List<FinalSeminarOpposition> findByOpposingUserAndLevel(User user, ProjectType projectType);
|
||||
List<FinalSeminarOpposition> findByOpposingUserAndType(User user, ProjectType projectType);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ public class FinalSeminarOppositionRepoImpl extends GenericRepo<FinalSeminarOppo
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<FinalSeminarOpposition> findByOpposingUserAndLevel(User user, ProjectType projectType) {
|
||||
public List<FinalSeminarOpposition> findByOpposingUserAndType(User user, ProjectType projectType) {
|
||||
return createQuery()
|
||||
.where(QFinalSeminarOpposition.finalSeminarOpposition.user.eq(user))
|
||||
.where(QFinalSeminarOpposition.finalSeminarOpposition.project.projectType.eq(projectType))
|
||||
|
@ -15,28 +15,37 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
public interface FinalSeminarService extends GenericService<FinalSeminar, Long>, FilteredService<FinalSeminar, Long, FinalSeminarService.Filter>, FinalSeminarScheduling {
|
||||
se.su.dsv.scipro.util.Either<OppositionRegistrationStatus, FinalSeminarOpposition> studentOppose(User student, FinalSeminar finalSeminar, Project project);
|
||||
Either<OpposeError, FinalSeminarOpposition> oppose(User student, FinalSeminar finalSeminar, Project project);
|
||||
void participate(User student, FinalSeminar finalSeminar, Project project);
|
||||
se.su.dsv.scipro.util.Either<OppositionRegistrationStatus, Void> canOppose(User user, FinalSeminar finalSeminar, Project project);
|
||||
boolean canParticipate(User user, FinalSeminar finalSeminar);
|
||||
Either<OppositionRegistrationErrorStatus, FinalSeminarOpposition> attemptAddOpposition(User student, FinalSeminar finalSeminar, Project project);
|
||||
|
||||
Either<ActiveParticipationRegistrationStatus, FinalSeminarActiveParticipation> attemptAddActiveParticipation(User student, FinalSeminar finalSeminar, Project project);
|
||||
|
||||
Either<OpposeError, FinalSeminarOpposition> SupervisorAttemptAddOpposition(User student, FinalSeminar finalSeminar, Project project);
|
||||
|
||||
Either<ParticipateError, FinalSeminarActiveParticipation> SupervisorAttemptAddActiveParticipation(User student, FinalSeminar finalSeminar, Project project);
|
||||
|
||||
Either<OppositionRegistrationErrorStatus, Void> canOppose(User Student, FinalSeminar finalSeminar, Project project);
|
||||
|
||||
Either<ActiveParticipationRegistrationStatus, Void> canActiveParticipate(User student, FinalSeminar finalSeminar);
|
||||
|
||||
Iterable<FinalSeminar> findAll(Filter params);
|
||||
|
||||
Date thesisUploadDeadline(FinalSeminar finalSeminar);
|
||||
|
||||
boolean hasDeadlinePassed(FinalSeminar finalSeminar);
|
||||
boolean hasThesis(FinalSeminar finalSeminar);
|
||||
FinalSeminar findByProject(Project project);
|
||||
Iterable<FinalSeminar> findByStartDateAfter(Date date);
|
||||
|
||||
boolean hasThesis(FinalSeminar finalSeminar);
|
||||
|
||||
FinalSeminar findByProject(Project project);
|
||||
|
||||
Iterable<FinalSeminar> findByStartDateAfter(Date date);
|
||||
|
||||
List<FinalSeminar> findProjectOpposing(Project project);
|
||||
|
||||
FinalSeminar deleteThesis(FinalSeminar seminar);
|
||||
|
||||
@Override
|
||||
void delete(FinalSeminar finalSeminar);
|
||||
|
||||
boolean isActiveParticipant(FinalSeminar seminar, User activeParticipant);
|
||||
|
||||
boolean hasHadFinalSeminar(Project project);
|
||||
|
||||
List<FinalSeminar> findUnfinishedSeminars(Date after, Date before, Pageable pageable);
|
||||
|
@ -23,11 +23,12 @@ import se.su.dsv.scipro.util.Either;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Provider;
|
||||
import java.time.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static com.querydsl.core.types.dsl.Expressions.allOf;
|
||||
import static com.querydsl.core.types.dsl.Expressions.*;
|
||||
|
||||
public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, Long> implements FinalSeminarService {
|
||||
|
||||
@ -178,16 +179,6 @@ public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, L
|
||||
super.delete(seminar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void participate(User student, FinalSeminar finalSeminar, Project project) {
|
||||
FinalSeminarActiveParticipation participation = new FinalSeminarActiveParticipation();
|
||||
participation.setUser(student);
|
||||
participation.setFinalSeminar(finalSeminar);
|
||||
participation.setProject(project);
|
||||
finalSeminar.addActiveParticipant(participation);
|
||||
finalSeminarRepository.save(finalSeminar);
|
||||
}
|
||||
|
||||
private boolean alreadyOpponent(User user, FinalSeminar finalSeminar) {
|
||||
for (FinalSeminarOpposition fso : finalSeminar.getOppositions()) {
|
||||
if (fso.getUser().equals(user)) {
|
||||
@ -197,7 +188,7 @@ public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, L
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean alreadyParticipant(User user, FinalSeminar finalSeminar) {
|
||||
private boolean alreadyActiveParticipant(User user, FinalSeminar finalSeminar) {
|
||||
for (FinalSeminarActiveParticipation fsap : finalSeminar.getActiveParticipations()) {
|
||||
if (fsap.getUser().equals(user)) {
|
||||
return true;
|
||||
@ -216,80 +207,122 @@ public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, L
|
||||
}
|
||||
|
||||
@Override
|
||||
public se.su.dsv.scipro.util.Either<OppositionRegistrationStatus, Void> canOppose(User user, FinalSeminar finalSeminar, Project project) {
|
||||
public Either<OppositionRegistrationErrorStatus, Void> canOppose(User Student, FinalSeminar finalSeminar, Project project) {
|
||||
if (finalSeminar.isCancelled()) {
|
||||
return se.su.dsv.scipro.util.Either.left(new FinalSeminarCancelled());
|
||||
return Either.left(new FinalSeminarCancelled());
|
||||
}
|
||||
if (!notAlreadyInSeminar(user, finalSeminar)) {
|
||||
return se.su.dsv.scipro.util.Either.left(new AlreadyParticipating());
|
||||
if (alreadyParticipatingInSeminar(Student, finalSeminar)) {
|
||||
return Either.left(new AlreadyParticipating());
|
||||
}
|
||||
if (finalSeminar.getStartDate().before(new Date())) {
|
||||
return se.su.dsv.scipro.util.Either.left(new AlreadyHappened());
|
||||
return Either.left(new AlreadyHappened());
|
||||
}
|
||||
if (finalSeminar.getManualParticipants()) {
|
||||
return Either.left(new ManualOpponents());
|
||||
}
|
||||
if (finalSeminar.getOppositions().size() >= finalSeminar.getMaxOpponents()) {
|
||||
return se.su.dsv.scipro.util.Either.left(new TooManyOpponents());
|
||||
return Either.left(new TooManyOpponents());
|
||||
}
|
||||
|
||||
for (FinalSeminarOpposition opposition : finalSeminarOppositionRepository.findByOpposingUserAndLevel(user, project.getProjectType())) {
|
||||
for (FinalSeminarOpposition opposition : finalSeminarOppositionRepository.findByOpposingUserAndType(Student, project.getProjectType())) {
|
||||
if (opposition.getGrade() == null) {
|
||||
return se.su.dsv.scipro.util.Either.left(new UngradedOpposition());
|
||||
}
|
||||
else if (opposition.getGrade() == FinalSeminarGrade.APPROVED) {
|
||||
return se.su.dsv.scipro.util.Either.left(new AlreadyOpposed());
|
||||
return Either.left(new UngradedOpposition());
|
||||
} else if (opposition.getGrade() == FinalSeminarGrade.APPROVED) {
|
||||
return Either.left(new AlreadyOpposed());
|
||||
}
|
||||
}
|
||||
|
||||
int oppositionPriorityDays = finalSeminarSettingsService.getInstance().getOppositionPriorityDays();
|
||||
final Instant seminarDate = finalSeminar.getDateCreated().toInstant();
|
||||
final Instant available = seminarDate.plus(Period.ofDays(oppositionPriorityDays));
|
||||
if (available.isAfter(Instant.now()) && findByProject(project) == null) {
|
||||
return se.su.dsv.scipro.util.Either.left(new PriorityForSeminarAuthors(oppositionPriorityDays, available));
|
||||
return Either.left(new PriorityForSeminarAuthors(oppositionPriorityDays, available));
|
||||
}
|
||||
return se.su.dsv.scipro.util.Either.right(null);
|
||||
return Either.right(null);
|
||||
}
|
||||
|
||||
private boolean notAlreadyInSeminar(User user, FinalSeminar finalSeminar) {
|
||||
return !alreadyOpponent(user, finalSeminar) && !isAuthor(user, finalSeminar) && !alreadyParticipant(user, finalSeminar);
|
||||
@Override
|
||||
public Either<ActiveParticipationRegistrationStatus, Void> canActiveParticipate(User student, FinalSeminar finalSeminar) {
|
||||
if (finalSeminar.getManualParticipants()) {
|
||||
return Either.left(new ManualParticipants());
|
||||
}
|
||||
if (alreadyParticipatingInSeminar(student, finalSeminar)) {
|
||||
return Either.left(new ParticipationAlreadyParticipating());
|
||||
}
|
||||
if (finalSeminar.getActiveParticipations().size() >= finalSeminar.getMaxParticipants()) {
|
||||
return Either.left(new TooManyParticipants());
|
||||
}
|
||||
if (finalSeminar.getStartDate().before(new Date())) {
|
||||
return Either.left(new ParticipationAlreadyHappened());
|
||||
}
|
||||
if (finalSeminar.isCancelled()) {
|
||||
return Either.left(new ParticipationFinalSeminarCancelled());
|
||||
}
|
||||
return Either.right(null);
|
||||
}
|
||||
|
||||
private boolean alreadyParticipatingInSeminar(User user, FinalSeminar finalSeminar) {
|
||||
return alreadyOpponent(user, finalSeminar) || isAuthor(user, finalSeminar) || alreadyActiveParticipant(user, finalSeminar);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public se.su.dsv.scipro.util.Either<OppositionRegistrationStatus, FinalSeminarOpposition> studentOppose(final User student, final FinalSeminar finalSeminar, final Project project) {
|
||||
public Either<OppositionRegistrationErrorStatus, FinalSeminarOpposition> attemptAddOpposition(final User student, final FinalSeminar finalSeminar, final Project project) {
|
||||
return canOppose(student, finalSeminar, project)
|
||||
.map(allowed -> {
|
||||
final FinalSeminarOpposition opposition = new FinalSeminarOpposition();
|
||||
opposition.setUser(student);
|
||||
opposition.setFinalSeminar(finalSeminar);
|
||||
opposition.setProject(project);
|
||||
finalSeminar.addOpposition(opposition);
|
||||
finalSeminarRepository.save(finalSeminar);
|
||||
return opposition;
|
||||
});
|
||||
.map(allowed -> createAndSaveOpposition(student, finalSeminar, project));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public se.su.dsv.scipro.util.Either<OpposeError, FinalSeminarOpposition> oppose(User student, FinalSeminar finalSeminar, Project project) {
|
||||
if (!project.isParticipant(student)) return se.su.dsv.scipro.util.Either.left(OpposeError.NotAuthor);
|
||||
else if (finalSeminar.getActiveParticipants().contains(student)) return se.su.dsv.scipro.util.Either.left(OpposeError.AlreadyParticipant);
|
||||
else if (finalSeminar.getOpponents().contains(student)) return se.su.dsv.scipro.util.Either.left(OpposeError.AlreadyOpponent);
|
||||
else {
|
||||
FinalSeminarOpposition opposition = new FinalSeminarOpposition();
|
||||
opposition.setUser(student);
|
||||
opposition.setFinalSeminar(finalSeminar);
|
||||
opposition.setProject(project);
|
||||
finalSeminar.addOpposition(opposition);
|
||||
finalSeminarRepository.save(finalSeminar);
|
||||
return se.su.dsv.scipro.util.Either.right(opposition);
|
||||
public Either<OpposeError, FinalSeminarOpposition> SupervisorAttemptAddOpposition(User student, FinalSeminar finalSeminar, Project project) {
|
||||
if (!project.isParticipant(student)) {
|
||||
return Either.left(OpposeError.NotAuthorOfSameProjectType);
|
||||
} else if (finalSeminar.getActiveParticipants().contains(student)) {
|
||||
return Either.left(OpposeError.AlreadyParticipant);
|
||||
} else if (finalSeminar.getOpponents().contains(student)) {
|
||||
return Either.left(OpposeError.AlreadyOpponent);
|
||||
} else {
|
||||
return Either.right(createAndSaveOpposition(student, finalSeminar, project));
|
||||
}
|
||||
}
|
||||
|
||||
private FinalSeminarOpposition createAndSaveOpposition(User student, FinalSeminar finalSeminar, Project project) {
|
||||
FinalSeminarOpposition opposition = new FinalSeminarOpposition();
|
||||
opposition.setUser(student);
|
||||
opposition.setFinalSeminar(finalSeminar);
|
||||
opposition.setProject(project);
|
||||
finalSeminar.addOpposition(opposition);
|
||||
finalSeminarRepository.save(finalSeminar);
|
||||
return opposition;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canParticipate(User user, FinalSeminar finalSeminar) {
|
||||
return finalSeminar.getActiveParticipations().size() < finalSeminar.getMaxParticipants() &&
|
||||
finalSeminar.getStartDate().after(new Date()) &&
|
||||
notAlreadyInSeminar(user, finalSeminar) &&
|
||||
!finalSeminar.isDeleted();
|
||||
@Transactional
|
||||
public Either<ActiveParticipationRegistrationStatus, FinalSeminarActiveParticipation> attemptAddActiveParticipation(final User student, final FinalSeminar finalSeminar, final Project project) {
|
||||
return canActiveParticipate(student, finalSeminar)
|
||||
.map(allowed -> createAndSaveActiveParticipation(student, finalSeminar, project));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public Either<ParticipateError, FinalSeminarActiveParticipation> SupervisorAttemptAddActiveParticipation(User student, FinalSeminar finalSeminar, Project project) {
|
||||
if (!project.isParticipant(student)) {
|
||||
return Either.left(ParticipateError.NotAuthorOfSameProjectType);
|
||||
} else if (finalSeminar.getActiveParticipants().contains(student)) {
|
||||
return Either.left(ParticipateError.AlreadyParticipant);
|
||||
} else if (finalSeminar.getOpponents().contains(student)) {
|
||||
return Either.left(ParticipateError.AlreadyOpponent);
|
||||
} else {
|
||||
return Either.right(createAndSaveActiveParticipation(student, finalSeminar, project));
|
||||
}
|
||||
}
|
||||
|
||||
private FinalSeminarActiveParticipation createAndSaveActiveParticipation(User student, FinalSeminar finalSeminar, Project project) {
|
||||
FinalSeminarActiveParticipation activeParticipation = new FinalSeminarActiveParticipation();
|
||||
activeParticipation.setUser(student);
|
||||
activeParticipation.setFinalSeminar(finalSeminar);
|
||||
activeParticipation.setProject(project);
|
||||
finalSeminar.addActiveParticipant(activeParticipation);
|
||||
finalSeminarRepository.save(finalSeminar);
|
||||
return activeParticipation;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -395,11 +428,6 @@ public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, L
|
||||
return findAll(projectOpposing(project));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActiveParticipant(FinalSeminar seminar, User activeParticipant) {
|
||||
return alreadyParticipant(activeParticipant, seminar);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHadFinalSeminar(Project project) {
|
||||
FinalSeminar finalSeminar = findByProject(project);
|
||||
@ -427,7 +455,7 @@ public class FinalSeminarServiceImpl extends AbstractServiceImpl<FinalSeminar, L
|
||||
|
||||
@Override
|
||||
public List<FinalSeminarOpposition> findUserOpposing(Project project, User user) {
|
||||
return finalSeminarOppositionRepository.findByOpposingUserAndLevel(user, project.getProjectType());
|
||||
return finalSeminarOppositionRepository.findByOpposingUserAndType(user, project.getProjectType());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -1,5 +1,5 @@
|
||||
package se.su.dsv.scipro.finalseminar;
|
||||
|
||||
public enum OpposeError {
|
||||
AlreadyOpponent, AlreadyParticipant, NotAuthor
|
||||
AlreadyOpponent, AlreadyParticipant, NotAuthorOfSameProjectType
|
||||
}
|
@ -3,8 +3,9 @@ package se.su.dsv.scipro.finalseminar;
|
||||
import java.time.Instant;
|
||||
import java.util.function.Function;
|
||||
|
||||
public abstract class OppositionRegistrationStatus {
|
||||
OppositionRegistrationStatus() { }
|
||||
public abstract class OppositionRegistrationErrorStatus {
|
||||
OppositionRegistrationErrorStatus() {
|
||||
}
|
||||
|
||||
public abstract <A> A fold(
|
||||
Function<UngradedOpposition, A> a,
|
||||
@ -13,10 +14,11 @@ public abstract class OppositionRegistrationStatus {
|
||||
Function<AlreadyHappened, A> d,
|
||||
Function<TooManyOpponents, A> e,
|
||||
Function<PriorityForSeminarAuthors, A> f,
|
||||
Function<AlreadyOpposed, A> g);
|
||||
Function<AlreadyOpposed, A> g,
|
||||
Function<ManualOpponents, A> h);
|
||||
}
|
||||
|
||||
final class UngradedOpposition extends OppositionRegistrationStatus {
|
||||
final class UngradedOpposition extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -25,13 +27,13 @@ final class UngradedOpposition extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return a.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class AlreadyOpposed extends OppositionRegistrationStatus {
|
||||
final class AlreadyOpposed extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -40,13 +42,13 @@ final class AlreadyOpposed extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return g.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class FinalSeminarCancelled extends OppositionRegistrationStatus {
|
||||
final class FinalSeminarCancelled extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -55,13 +57,13 @@ final class FinalSeminarCancelled extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return b.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class AlreadyParticipating extends OppositionRegistrationStatus {
|
||||
final class AlreadyParticipating extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -70,13 +72,13 @@ final class AlreadyParticipating extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return c.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class AlreadyHappened extends OppositionRegistrationStatus {
|
||||
final class AlreadyHappened extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -85,13 +87,13 @@ final class AlreadyHappened extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return d.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class TooManyOpponents extends OppositionRegistrationStatus {
|
||||
final class TooManyOpponents extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
@ -100,13 +102,28 @@ final class TooManyOpponents extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return e.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class PriorityForSeminarAuthors extends OppositionRegistrationStatus {
|
||||
final class ManualOpponents extends OppositionRegistrationErrorStatus {
|
||||
@Override
|
||||
public <A> A fold(
|
||||
final Function<UngradedOpposition, A> a,
|
||||
final Function<FinalSeminarCancelled, A> b,
|
||||
final Function<AlreadyParticipating, A> c,
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return h.apply(this);
|
||||
}
|
||||
}
|
||||
|
||||
final class PriorityForSeminarAuthors extends OppositionRegistrationErrorStatus {
|
||||
private final int priorityDays;
|
||||
private final Instant priorityEnd;
|
||||
|
||||
@ -131,8 +148,8 @@ final class PriorityForSeminarAuthors extends OppositionRegistrationStatus {
|
||||
final Function<AlreadyHappened, A> d,
|
||||
final Function<TooManyOpponents, A> e,
|
||||
final Function<PriorityForSeminarAuthors, A> f,
|
||||
final Function<AlreadyOpposed, A> g)
|
||||
{
|
||||
final Function<AlreadyOpposed, A> g,
|
||||
final Function<ManualOpponents, A> h) {
|
||||
return f.apply(this);
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
package se.su.dsv.scipro.finalseminar;
|
||||
|
||||
public enum ParticipateError {
|
||||
AlreadyOpponent, AlreadyParticipant, NotAuthorOfSameProjectType
|
||||
}
|
@ -242,16 +242,6 @@ public class Project extends DomainObject {
|
||||
return getReviewer() != null ? getReviewer().getFullName() : NO_REVIEWER;
|
||||
}
|
||||
|
||||
public String getCoSupervisorName() {
|
||||
if (!getCoSupervisors().isEmpty()) {
|
||||
return getCoSupervisors()
|
||||
.stream()
|
||||
.map(User::getFullName)
|
||||
.collect(Collectors.joining(", "));
|
||||
}
|
||||
return NO_CO_SUPERVISOR;
|
||||
}
|
||||
|
||||
public DegreeType getProjectTypeDegreeType() {
|
||||
return getProjectType().getDegreeType();
|
||||
}
|
||||
|
@ -111,30 +111,30 @@ public class FinalSeminarServiceImplIntegrationTest extends IntegrationTest {
|
||||
@Test
|
||||
public void can_not_oppose_if_already_opponent() {
|
||||
otherProject.addProjectParticipant(user);
|
||||
finalSeminarService.oppose(user, futureFinalSeminar, otherProject);
|
||||
finalSeminarService.SupervisorAttemptAddOpposition(user, futureFinalSeminar, otherProject);
|
||||
assertThat(finalSeminarService.canOppose(user, futureFinalSeminar, otherProject), isLeft(instanceOf(AlreadyParticipating.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_oppose_if_participant() {
|
||||
finalSeminarService.participate(user, futureFinalSeminar, otherProject);
|
||||
finalSeminarService.SupervisorAttemptAddActiveParticipation(user, futureFinalSeminar, otherProject);
|
||||
assertThat(finalSeminarService.canOppose(user, futureFinalSeminar, otherProject), isLeft(instanceOf(AlreadyParticipating.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_participate_on_future_seminar() {
|
||||
assertTrue(finalSeminarService.canParticipate(user, futureFinalSeminar));
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, futureFinalSeminar), isRight(anything()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_participate_on_past_seminar() {
|
||||
assertFalse(finalSeminarService.canParticipate(user, pastFinalSeminar));
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, pastFinalSeminar), isLeft(instanceOf(ParticipationAlreadyHappened.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_not_participate_twice() {
|
||||
finalSeminarService.participate(user, futureFinalSeminar, otherProject);
|
||||
assertFalse(finalSeminarService.canParticipate(user, futureFinalSeminar));
|
||||
finalSeminarService.SupervisorAttemptAddActiveParticipation(user, futureFinalSeminar, otherProject);
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, pastFinalSeminar), isLeft(instanceOf(ParticipationAlreadyParticipating.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -146,7 +146,7 @@ public class FinalSeminarServiceImplIntegrationTest extends IntegrationTest {
|
||||
@Test
|
||||
public void can_not_participate_if_max_is_reached() {
|
||||
futureFinalSeminar.setMaxParticipants(0);
|
||||
assertFalse(finalSeminarService.canParticipate(user, futureFinalSeminar));
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, pastFinalSeminar), isLeft(instanceOf(ManualParticipants.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -158,7 +158,7 @@ public class FinalSeminarServiceImplIntegrationTest extends IntegrationTest {
|
||||
@Test
|
||||
public void can_not_participate_if_is_author() {
|
||||
futureFinalSeminar.getProject().addProjectParticipant(user);
|
||||
assertFalse(finalSeminarService.canParticipate(user, futureFinalSeminar));
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, pastFinalSeminar), isLeft(instanceOf(ParticipationAlreadyParticipating.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -170,13 +170,13 @@ public class FinalSeminarServiceImplIntegrationTest extends IntegrationTest {
|
||||
@Test
|
||||
public void can_not_participate_on_deleted_seminar() {
|
||||
futureFinalSeminar.setDeleted(true);
|
||||
assertFalse(finalSeminarService.canParticipate(user, futureFinalSeminar));
|
||||
assertThat(finalSeminarService.canActiveParticipate(user, pastFinalSeminar), isLeft(instanceOf(ParticipationFinalSeminarCancelled.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_participate_if_previous_oppositions_are_failed() {
|
||||
FinalSeminar secondSeminar = createFinalSeminar(createProject(), 10);
|
||||
finalSeminarService.oppose(user, futureFinalSeminar, otherProject);
|
||||
finalSeminarService.SupervisorAttemptAddOpposition(user, futureFinalSeminar, otherProject);
|
||||
for (FinalSeminarOpposition finalSeminarOpposition : futureFinalSeminar.getOppositions()) {
|
||||
finalSeminarOpposition.setGrade(FinalSeminarGrade.NOT_APPROVED);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public abstract class OpposeColumnPanel extends Panel {
|
||||
public OpposeColumnPanel(String id, final IModel<FinalSeminar> finalSeminarModel, final IModel<Project> projectModel) {
|
||||
super(id, finalSeminarModel);
|
||||
|
||||
final Either<OppositionRegistrationStatus, Void> canOppose =
|
||||
final Either<OppositionRegistrationErrorStatus, Void> canOppose =
|
||||
finalSeminarService.canOppose(SciProSession.get().getUser(), finalSeminarModel.getObject(), projectModel.getObject());
|
||||
final Component opposeLink = canOppose.fold(
|
||||
this::showError,
|
||||
@ -39,7 +39,7 @@ public abstract class OpposeColumnPanel extends Panel {
|
||||
add(opposeLink);
|
||||
}
|
||||
|
||||
private Component showError(OppositionRegistrationStatus notAllowed) {
|
||||
private Component showError(OppositionRegistrationErrorStatus notAllowed) {
|
||||
return notAllowed.fold(
|
||||
ungradedOpposition -> getLabel("You have ungraded oppositions"),
|
||||
finalSeminarCancelled -> getLabel("The final seminar has been cancelled"),
|
||||
@ -49,7 +49,8 @@ public abstract class OpposeColumnPanel extends Panel {
|
||||
priorityForSeminarAuthors -> getLabel("Authors with their own final seminar have " +
|
||||
"priority to register for " + priorityForSeminarAuthors.getPriorityDays() +
|
||||
" day(s). This priority will end at " + asDateTime(Date.from(priorityForSeminarAuthors.getPriorityEnd()))),
|
||||
alreadyOpposed -> getLabel("You have already completed an opposition and can not perform a second one."));
|
||||
alreadyOpposed -> getLabel("You have already completed an opposition and can not perform a second one."),
|
||||
manualOpponents -> getLabel("Opponents for this seminar are handled by the supervisor."));
|
||||
}
|
||||
|
||||
public String asDateTime(Date date) {
|
||||
|
@ -1,11 +1,14 @@
|
||||
package se.su.dsv.scipro.finalseminar;
|
||||
|
||||
import org.apache.wicket.ajax.AjaxRequestTarget;
|
||||
import org.apache.wicket.ajax.markup.html.AjaxLink;
|
||||
import org.apache.wicket.Component;
|
||||
import org.apache.wicket.markup.html.link.Link;
|
||||
import org.apache.wicket.markup.html.link.StatelessLink;
|
||||
import org.apache.wicket.markup.html.panel.Panel;
|
||||
import org.apache.wicket.model.IModel;
|
||||
import org.apache.wicket.model.Model;
|
||||
import se.su.dsv.scipro.session.SciProSession;
|
||||
import se.su.dsv.scipro.util.AjaxConfirmationLink;
|
||||
import se.su.dsv.scipro.util.Either;
|
||||
import se.su.dsv.scipro.util.JavascriptEventConfirmation;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@ -17,32 +20,55 @@ public abstract class ParticipateColumnPanel extends Panel {
|
||||
@Inject
|
||||
private FinalSeminarService finalSeminarService;
|
||||
|
||||
private AjaxLink participateLink;
|
||||
private ParticipateColumnPanel participateColumnPanel;
|
||||
|
||||
public abstract void onClick(IModel<FinalSeminar> clickedModel);
|
||||
|
||||
public ParticipateColumnPanel(String id, final IModel<FinalSeminar> model) {
|
||||
super(id, model);
|
||||
public ParticipateColumnPanel(String id, final IModel<FinalSeminar> finalSeminarModel) {
|
||||
super(id, finalSeminarModel);
|
||||
|
||||
participateColumnPanel = this;
|
||||
final Either<ActiveParticipationRegistrationStatus, Void> canActiveParticipate =
|
||||
finalSeminarService.canActiveParticipate(SciProSession.get().getUser(), finalSeminarModel.getObject());
|
||||
final Component participateLink = canActiveParticipate.fold(
|
||||
this::showError,
|
||||
allowed -> new ParticipateColumnPanel.ParticipateLink(LINK, finalSeminarModel)
|
||||
);
|
||||
|
||||
participateLink = new AjaxConfirmationLink<Void>(LINK, PARTICIPATE_CONFIRMATION) {
|
||||
@Override
|
||||
public void onClick(AjaxRequestTarget target) {
|
||||
participateColumnPanel.onClick(model);
|
||||
participateLink.setEnabled(false);
|
||||
target.add(participateLink);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onConfigure() {
|
||||
super.onConfigure();
|
||||
setEnabled(finalSeminarService.canParticipate(SciProSession.get().getUser(), model.getObject()));
|
||||
}
|
||||
};
|
||||
|
||||
participateLink.setOutputMarkupId(true);
|
||||
add(participateLink);
|
||||
}
|
||||
}
|
||||
|
||||
private Component showError(ActiveParticipationRegistrationStatus notAllowed) {
|
||||
return notAllowed.fold(
|
||||
tooManyParticipants -> getLabel("The seminar is already has the maximum participants"),
|
||||
ManualParticipants -> getLabel("Participants for this seminar are handled by the supervisor."),
|
||||
ParticipationAlreadyParticipating -> getLabel("You are already participating in the final seminar"),
|
||||
ParticipationAlreadyHappened -> getLabel("The seminar has already happened"),
|
||||
ParticipationFinalSeminarCancelled -> getLabel("The final seminar has been cancelled"));
|
||||
|
||||
}
|
||||
|
||||
private Component getLabel(final String label) {
|
||||
// Dumb workaround to get the <a> tag transformed into a <span>
|
||||
final StatelessLink<Void> components = new StatelessLink<>(LINK) {
|
||||
@Override
|
||||
public void onClick() {
|
||||
}
|
||||
};
|
||||
return components
|
||||
.setBody(Model.of(label))
|
||||
.setEnabled(false);
|
||||
}
|
||||
|
||||
private class ParticipateLink extends Link<FinalSeminar> {
|
||||
|
||||
public ParticipateLink(final String id, final IModel<FinalSeminar> finalSeminarModel) {
|
||||
super(id, finalSeminarModel);
|
||||
add(new JavascriptEventConfirmation("click", ParticipateColumnPanel.PARTICIPATE_CONFIRMATION));
|
||||
setOutputMarkupId(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick() {
|
||||
ParticipateColumnPanel.this.onClick(getModel());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -94,8 +94,8 @@ public class ProjectOppositionPage extends AbstractProjectDetailsPage implements
|
||||
add(new ExportableDataPanel<>("select", createColumns(), provider));
|
||||
}
|
||||
|
||||
public boolean oppose(FinalSeminar finalSeminar) {
|
||||
return finalSeminarService.studentOppose(SciProSession.get().getUser(), finalSeminar, getActiveProject())
|
||||
public boolean submitOpposition(FinalSeminar finalSeminar) {
|
||||
return finalSeminarService.attemptAddOpposition(SciProSession.get().getUser(), finalSeminar, getActiveProject())
|
||||
.fold(
|
||||
notAllowed -> {
|
||||
final String errorMessage = notAllowed.fold(
|
||||
@ -108,7 +108,8 @@ public class ProjectOppositionPage extends AbstractProjectDetailsPage implements
|
||||
"Authors with their own final seminar have " +
|
||||
"priority to register for " + priorityForSeminarAuthors.getPriorityDays() +
|
||||
" day(s). This priority will end at " + asDateTime(Date.from(priorityForSeminarAuthors.getPriorityEnd())),
|
||||
alreadyOpposed -> "You have already completed an opposition and can not perform a second one."
|
||||
alreadyOpposed -> "You have already completed an opposition and can't perform a second one.",
|
||||
manualOpponents -> "Opponents for this seminar are handled by the supervisor."
|
||||
);
|
||||
error(errorMessage);
|
||||
return Boolean.FALSE;
|
||||
@ -125,16 +126,30 @@ public class ProjectOppositionPage extends AbstractProjectDetailsPage implements
|
||||
return dateFormat.format(date);
|
||||
}
|
||||
|
||||
public void participate(FinalSeminar finalSeminar) {
|
||||
finalSeminarService.participate(SciProSession.get().getUser(), finalSeminar, getActiveProject());
|
||||
notificationController.notifySeminar(finalSeminar, SeminarEvent.Event.PARTICIPATION_CHANGED, new NotificationSource());
|
||||
public boolean submitParticipation(FinalSeminar finalSeminar) {
|
||||
return finalSeminarService.attemptAddActiveParticipation(SciProSession.get().getUser(), finalSeminar, getActiveProject())
|
||||
.fold(
|
||||
notAllowed -> {
|
||||
final String errorMessage = notAllowed.fold(
|
||||
TooManyParticipants -> "The seminar is already full on participants",
|
||||
ManualParticipants -> "Participants for this seminar are handled by the supervisor.",
|
||||
ParticipationAlreadyParticipating -> "You are already participating in the final seminar",
|
||||
ParticipationAlreadyHappened -> "The seminar has already happened",
|
||||
ParticipationFinalSeminarCancelled -> "The final seminar has been cancelled");
|
||||
error(errorMessage);
|
||||
return Boolean.FALSE;
|
||||
},
|
||||
opposed -> {
|
||||
notificationController.notifySeminar(finalSeminar, SeminarEvent.Event.PARTICIPATION_CHANGED, new NotificationSource());
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private List<IColumn<FinalSeminar, String>> createColumns() {
|
||||
List<IColumn<FinalSeminar, String>> columns = new ArrayList<>();
|
||||
columns.add(new DateColumn<>(Model.of("Date"), FinalSeminar::getStartDate, "startDate", DateStyle.DATETIME));
|
||||
columns.add(new LambdaColumn<>(Model.of("Type"), "project.projectType.name", fs -> fs.getProjectType().getName()));
|
||||
|
||||
columns.add(new AbstractColumn<>(Model.of("Title"), "project.title") {
|
||||
@Override
|
||||
public void populateItem(Item<ICellPopulator<FinalSeminar>> cellItem, String componentId, final IModel<FinalSeminar> rowModel) {
|
||||
@ -154,8 +169,8 @@ public class ProjectOppositionPage extends AbstractProjectDetailsPage implements
|
||||
item.add(new OpposeColumnPanel(s, iModel, projectModel) {
|
||||
@Override
|
||||
public void onClick(IModel<FinalSeminar> clickedModel) {
|
||||
if (oppose(clickedModel.getObject())) {
|
||||
ProjectOppositionPage.this.success(getString("oppose", clickedModel));
|
||||
if (submitOpposition(clickedModel.getObject())) {
|
||||
ProjectOppositionPage.this.success(getString("oppositionAdded", clickedModel));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -167,9 +182,9 @@ public class ProjectOppositionPage extends AbstractProjectDetailsPage implements
|
||||
item.add(new ParticipateColumnPanel(s, iModel) {
|
||||
@Override
|
||||
public void onClick(IModel<FinalSeminar> clickedModel) {
|
||||
participate(clickedModel.getObject());
|
||||
getSession().success(getString("participate", clickedModel));
|
||||
setResponsePage(ProjectOppositionPage.class);
|
||||
if (submitParticipation(clickedModel.getObject())) {
|
||||
ProjectOppositionPage.this.success(getString("participationAdded", clickedModel));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
info.text= If the Oppose and Participate links are black and not clickable that means the \
|
||||
seminar is full. Wait for another seminar to oppose on or participate in.
|
||||
oppose= You are now registered as opponent on the final seminar for project: ${projectTitle}
|
||||
participate= You are now registered as an active participant on the final seminar for project: ${projectTitle}
|
||||
oppositionAdded= You are now registered as an opponent for the final seminar for project: ${projectTitle}
|
||||
participationAdded= You are now registered as an active participant for the final seminar for project: ${projectTitle}
|
@ -45,7 +45,7 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
@Inject private NonWorkDayPeriodService nonWorkDays;
|
||||
|
||||
private DefaultSelect2MultiChoice<User> opponents;
|
||||
private DefaultSelect2MultiChoice<User> participants;
|
||||
private DefaultSelect2MultiChoice<User> activeParticipants;
|
||||
private Set<SeminarEvent.Event> events = new HashSet<>();
|
||||
|
||||
public SeminarCRUDPanel(String id, IModel<FinalSeminar> seminar) {
|
||||
@ -104,8 +104,8 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
final AutoCompleteRoleProvider authorChoices = new AutoCompleteRoleProvider(userSearchService, Set.of(Roles.AUTHOR));
|
||||
opponents = new DefaultSelect2MultiChoice<>("opponents", new DetachableServiceModelCollection<>(userService), authorChoices);
|
||||
add(opponents);
|
||||
participants = new DefaultSelect2MultiChoice<>("participants", new DetachableServiceModelCollection<>(userService), authorChoices);
|
||||
add(participants);
|
||||
activeParticipants = new DefaultSelect2MultiChoice<>("participants", new DetachableServiceModelCollection<>(userService), authorChoices);
|
||||
add(activeParticipants);
|
||||
|
||||
add(new ConfirmationLink<>(DELETE, seminar, new StringResourceModel("confirm", this)) {
|
||||
@Override
|
||||
@ -143,11 +143,15 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
if (seminar.getActiveParticipants().contains(opponent)) {
|
||||
error("Selected opponent is already active participant");
|
||||
}
|
||||
if (participants.getConvertedInput().contains(opponent)) {
|
||||
if (activeParticipants.getConvertedInput().contains(opponent)) {
|
||||
error("Can't add same user as both opponent and active participant");
|
||||
}
|
||||
if (hasError()) {
|
||||
opponents.clearInput();
|
||||
opponents.getModel().setObject(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
for (User participant : participants.getConvertedInput()) {
|
||||
for (User participant : activeParticipants.getConvertedInput()) {
|
||||
if (seminar.getProject().getHeadSupervisor().equals(participant)) {
|
||||
error("Head supervisor can't be active participant");
|
||||
}
|
||||
@ -166,6 +170,10 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
if (opponents.getConvertedInput().contains(participant)) {
|
||||
error("Can't add same user as both opponent and active participant");
|
||||
}
|
||||
if (hasError()) {
|
||||
activeParticipants.clearInput();
|
||||
activeParticipants.getModel().setObject(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -193,24 +201,35 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
}
|
||||
}
|
||||
|
||||
private void addActiveParticipants(FinalSeminar seminar) {
|
||||
for (User participant : participants.getModelObject()) {
|
||||
if (!seminarService.isActiveParticipant(seminar, participant)) {
|
||||
List<Project> participantProjects = projectService.getActiveProjects(participant, seminar.getProjectType());
|
||||
if (participantProjects.isEmpty()) {
|
||||
error(getString("opponent.no.project", Model.of(participant)));
|
||||
} else if (participantProjects.size() > 1) {
|
||||
error(getString("opponent.too.many.projects", Model.of(participant)));
|
||||
} else {
|
||||
FinalSeminarActiveParticipation participation = new FinalSeminarActiveParticipation();
|
||||
participation.setUser(participant);
|
||||
participation.setFinalSeminar(seminar);
|
||||
participation.setProject(participantProjects.get(0));
|
||||
seminar.addActiveParticipant(participation);
|
||||
events.add(SeminarEvent.Event.PARTICIPATION_CHANGED);
|
||||
}
|
||||
private void addActiveParticipants(FinalSeminar finalSeminar) {
|
||||
for (User potentialParticipant : activeParticipants.getModelObject()) {
|
||||
Optional<Project> maybeProject = getPotentialParticipantProject(potentialParticipant, finalSeminar.getProjectType());
|
||||
if (maybeProject.isEmpty()) {
|
||||
error(getString("opponent.no.project", Model.of(potentialParticipant)));
|
||||
} else {
|
||||
final Project project = maybeProject.get();
|
||||
Either<ParticipateError, FinalSeminarActiveParticipation> result = seminarService.SupervisorAttemptAddActiveParticipation(potentialParticipant, finalSeminar, project);
|
||||
result.fold(
|
||||
error -> {
|
||||
switch (error) {
|
||||
case AlreadyOpponent ->
|
||||
error("Selected opponent " + potentialParticipant.getFullName() + " is already opponent");
|
||||
case AlreadyParticipant ->
|
||||
error("Selected opponent " + potentialParticipant.getFullName() + " is already active participant");
|
||||
case NotAuthorOfSameProjectType ->
|
||||
error("Failed to add " + potentialParticipant.getFullName() + ", not an author of a project of same type");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
opponent -> {
|
||||
success("Added " + potentialParticipant.getFullName() + " as an opponent");
|
||||
events.add(SeminarEvent.Event.PARTICIPATION_CHANGED);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
}
|
||||
activeParticipants.clearInput();
|
||||
activeParticipants.getModel().setObject(Collections.emptyList());
|
||||
}
|
||||
|
||||
private void addOpponentsTo(FinalSeminar finalSeminar) {
|
||||
@ -221,7 +240,7 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
error(getString("opponent.no.project", Model.of(potentialOpponent)));
|
||||
} else {
|
||||
final Project project = maybeProject.get();
|
||||
Either<OpposeError, FinalSeminarOpposition> result = seminarService.oppose(potentialOpponent, finalSeminar, project);
|
||||
Either<OpposeError, FinalSeminarOpposition> result = seminarService.SupervisorAttemptAddOpposition(potentialOpponent, finalSeminar, project);
|
||||
result.fold(
|
||||
error -> {
|
||||
switch (error) {
|
||||
@ -229,8 +248,8 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
error("Selected opponent " + potentialOpponent.getFullName() + " is already opponent");
|
||||
case AlreadyParticipant ->
|
||||
error("Selected opponent " + potentialOpponent.getFullName() + " is already active participant");
|
||||
case NotAuthor ->
|
||||
error("Failed to add " + potentialOpponent.getFullName() + " as an opponent");
|
||||
case NotAuthorOfSameProjectType ->
|
||||
error("Failed to add " + potentialOpponent.getFullName() + ", not an author of a project of same type");
|
||||
}
|
||||
return false;
|
||||
},
|
||||
@ -240,6 +259,8 @@ public class SeminarCRUDPanel extends GenericPanel<FinalSeminar> {
|
||||
return true;
|
||||
});
|
||||
}
|
||||
opponents.clearInput();
|
||||
opponents.getModel().setObject(Collections.emptyList());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,8 +4,8 @@ maxOpponents.RangeValidator.minimum=The selected number of max oppositions may n
|
||||
FinalSeminarLanguage.SWEDISH=Swedish
|
||||
# suppress inspection "UnusedProperty"
|
||||
FinalSeminarLanguage.ENGLISH=English
|
||||
opponent.no.project=${fullName} does not have an active project on this projects type.
|
||||
opponent.too.many.projects=${fullName} has more than one active project on this projects type - unable to add opponent manually.
|
||||
opponent.no.project=${fullName} does not have an active project of this projects type.
|
||||
opponent.too.many.projects=${fullName} has more than one active project of this projects type - unable to add opponent manually.
|
||||
final.seminar.updated=Final seminar saved
|
||||
create= Create
|
||||
update= Update
|
||||
|
@ -64,19 +64,19 @@ public class ProjectOppositionPageTest extends PageTest {
|
||||
Mockito.when(finalSeminarService.findAll(ArgumentMatchers.any(FinalSeminarService.Filter.class), ArgumentMatchers.any(Pageable.class))).thenReturn(Collections.singletonList(finalSeminar));
|
||||
Mockito.when(finalSeminarService.count(ArgumentMatchers.any(FinalSeminarService.Filter.class))).thenReturn(1L);
|
||||
Mockito.when(finalSeminarService.canOppose(user, finalSeminar, project)).thenReturn(Either.right(null));
|
||||
Mockito.when(finalSeminarService.canParticipate(user, finalSeminar)).thenReturn(true);
|
||||
Mockito.when(finalSeminarService.canActiveParticipate(user, finalSeminar)).thenReturn(Either.right(null));
|
||||
tester.startPage(ProjectOppositionPage.class, pageParameters);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clickingOpposeUpdatesCorrectProject() {
|
||||
Mockito.when(finalSeminarService.studentOppose(user, finalSeminar, project)).thenReturn(Either.right(null));
|
||||
Mockito.when(finalSeminarService.attemptAddOpposition(user, finalSeminar, project)).thenReturn(Either.right(null));
|
||||
final Page page = tester.getLastRenderedPage();
|
||||
page.visitChildren(OpposeColumnPanel.class, new IVisitor<Component, Object>() {
|
||||
@Override
|
||||
public void component(Component component, IVisit<Object> visit) {
|
||||
tester.clickLink(path(component.getPageRelativePath(), OpposeColumnPanel.LINK));
|
||||
Mockito.verify(finalSeminarService).studentOppose(user, finalSeminar, project);
|
||||
Mockito.verify(finalSeminarService).attemptAddOpposition(user, finalSeminar, project);
|
||||
visit.stop();
|
||||
}
|
||||
});
|
||||
@ -84,7 +84,7 @@ public class ProjectOppositionPageTest extends PageTest {
|
||||
|
||||
@Test
|
||||
public void clicking_oppose_generates_feedback() {
|
||||
Mockito.when(finalSeminarService.studentOppose(user, finalSeminar, project)).thenReturn(Either.right(null));
|
||||
Mockito.when(finalSeminarService.attemptAddOpposition(user, finalSeminar, project)).thenReturn(Either.right(null));
|
||||
final Page page = tester.getLastRenderedPage();
|
||||
page.visitChildren(OpposeColumnPanel.class, new IVisitor<Component, Object>() {
|
||||
@Override
|
||||
@ -92,7 +92,7 @@ public class ProjectOppositionPageTest extends PageTest {
|
||||
tester.clickLink(path(component.getPageRelativePath(), OpposeColumnPanel.LINK));
|
||||
|
||||
List<Serializable> messages = tester.getMessages(FeedbackMessage.SUCCESS);
|
||||
MatcherAssert.assertThat(messages, hasItem(StringContains.containsString(page.getString("oppose", Model.of(finalSeminar)))));
|
||||
MatcherAssert.assertThat(messages, hasItem(StringContains.containsString(page.getString("oppositionAdded", Model.of(finalSeminar)))));
|
||||
visit.stop();
|
||||
}
|
||||
});
|
||||
@ -105,7 +105,7 @@ public class ProjectOppositionPageTest extends PageTest {
|
||||
@Override
|
||||
public void component(Component component, IVisit<Object> visit) {
|
||||
tester.clickLink(path(component.getPageRelativePath(), ParticipateColumnPanel.LINK));
|
||||
Mockito.verify(finalSeminarService).participate(user, finalSeminar, project);
|
||||
Mockito.verify(finalSeminarService).SupervisorAttemptAddActiveParticipation(user, finalSeminar, project);
|
||||
visit.stop();
|
||||
}
|
||||
});
|
||||
@ -120,7 +120,7 @@ public class ProjectOppositionPageTest extends PageTest {
|
||||
tester.clickLink(path(component.getPageRelativePath(), ParticipateColumnPanel.LINK));
|
||||
|
||||
List<Serializable> messages = tester.getMessages(FeedbackMessage.SUCCESS);
|
||||
MatcherAssert.assertThat(messages, hasItem(StringContains.containsString(page.getString("participate", Model.of(finalSeminar)))));
|
||||
MatcherAssert.assertThat(messages, hasItem(StringContains.containsString(page.getString("participationAdded", Model.of(finalSeminar)))));
|
||||
visit.stop();
|
||||
}
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user