SEBSERV-160 fixed "In Use" for selection

This commit is contained in:
anhefti 2022-05-02 13:35:35 +02:00
parent 8b9eebfe5b
commit 6396afa53b
7 changed files with 65 additions and 9 deletions

View file

@ -26,12 +26,15 @@ import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM;
import ch.ethz.seb.sebserver.gbl.model.Domain.EXAM_CONFIGURATION_MAP;
import ch.ethz.seb.sebserver.gbl.model.Entity;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamType;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigurationNode.ConfigurationStatus;
@JsonIgnoreProperties(ignoreUnknown = true)
public final class ExamConfigurationMap implements GrantEntity {
private static final String ARR_EXAM_STATUS = "examStatus";
public static final String ATTR_CONFIRM_ENCRYPT_SECRET = "confirm_encrypt_secret";
public static final String FILTER_ATTR_EXAM_ID = "examId";
@ -60,6 +63,9 @@ public final class ExamConfigurationMap implements GrantEntity {
@JsonProperty(EXAM.ATTR_TYPE)
public final ExamType examType;
@JsonProperty(ARR_EXAM_STATUS)
public final ExamStatus examStatus;
@NotNull(message = "examConfigurationMap:configurationNodeId:notNull")
@JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID)
public final Long configurationNodeId;
@ -91,6 +97,7 @@ public final class ExamConfigurationMap implements GrantEntity {
@JsonProperty(QuizData.QUIZ_ATTR_DESCRIPTION) final String examDescription,
@JsonProperty(QuizData.QUIZ_ATTR_START_TIME) final DateTime examStartTime,
@JsonProperty(EXAM.ATTR_TYPE) final ExamType examType,
@JsonProperty(ARR_EXAM_STATUS) final ExamStatus examStatus,
@JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID) final Long configurationNodeId,
@JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES) final String userNames,
@JsonProperty(EXAM_CONFIGURATION_MAP.ATTR_ENCRYPT_SECRET) final CharSequence encryptSecret,
@ -106,6 +113,7 @@ public final class ExamConfigurationMap implements GrantEntity {
this.examDescription = examDescription;
this.examStartTime = examStartTime;
this.examType = examType;
this.examStatus = examStatus;
this.configurationNodeId = configurationNodeId;
this.userNames = userNames;
this.encryptSecret = encryptSecret;
@ -125,6 +133,7 @@ public final class ExamConfigurationMap implements GrantEntity {
this.examDescription = postParams.getString(QuizData.QUIZ_ATTR_DESCRIPTION);
this.examStartTime = postParams.getDateTime(QuizData.QUIZ_ATTR_START_TIME);
this.examType = postParams.getEnum(EXAM.ATTR_TYPE, ExamType.class);
this.examStatus = postParams.getEnum(ARR_EXAM_STATUS, ExamStatus.class);
this.configurationNodeId = postParams.getLong(Domain.EXAM_CONFIGURATION_MAP.ATTR_CONFIGURATION_NODE_ID);
this.userNames = postParams.getString(Domain.EXAM_CONFIGURATION_MAP.ATTR_USER_NAMES);
@ -149,6 +158,7 @@ public final class ExamConfigurationMap implements GrantEntity {
this.examDescription = null;
this.examStartTime = null;
this.examType = null;
this.examStatus = null;
this.configurationNodeId = configurationNodeId;
this.userNames = userNames;
this.encryptSecret = null;
@ -205,6 +215,10 @@ public final class ExamConfigurationMap implements GrantEntity {
return this.examType;
}
public ExamStatus getExamStatus() {
return this.examStatus;
}
public Long getConfigurationNodeId() {
return this.configurationNodeId;
}
@ -250,6 +264,7 @@ public final class ExamConfigurationMap implements GrantEntity {
this.examDescription,
this.examStartTime,
this.examType,
this.examStatus,
this.configurationNodeId,
this.userNames,
Constants.EMPTY_NOTE,
@ -296,7 +311,7 @@ public final class ExamConfigurationMap implements GrantEntity {
public static ExamConfigurationMap createNew(final Exam exam) {
return new ExamConfigurationMap(
null, exam.institutionId, exam.id, exam.name, exam.description, exam.startTime, exam.type,
null, exam.institutionId, exam.id, exam.name, exam.description, exam.startTime, exam.type, exam.status,
null, null, null, null, null, null, null);
}

View file

@ -87,7 +87,7 @@ public class SEBExamConfigBatchStateChangePopup extends AbstractBatchActionWizar
FORM_STATUS_TEXT_KEY,
targetStateName,
() -> this.pageService.getResourceService()
.examConfigStatusResources(false))
.examConfigStatusResourcesAll())
.readonly(readonly));
}

View file

@ -27,6 +27,7 @@ import ch.ethz.seb.sebserver.gbl.api.EntityType;
import ch.ethz.seb.sebserver.gbl.model.Domain;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamConfigurationMap;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.sebconfig.ConfigKey;
@ -173,6 +174,16 @@ public class SEBExamConfigForm implements TemplateComposer {
.call()
.map(names -> names != null && !names.isEmpty())
.getOr(Boolean.FALSE);
final boolean hasRunningExam = isAttachedToExam && this.restService
.getBuilder(GetExamConfigMappingsPage.class)
.withQueryParam(ExamConfigurationMap.FILTER_ATTR_CONFIG_ID, examConfig.getModelId())
.call()
.map(res -> res.content
.stream()
.filter(map -> map.examStatus == ExamStatus.RUNNING)
.findAny()
.isPresent())
.getOr(false);
// new PageContext with actual EntityKey
final PageContext formContext = pageContext.withEntityKey(examConfig.getEntityKey());
@ -223,7 +234,7 @@ public class SEBExamConfigForm implements TemplateComposer {
Domain.CONFIGURATION_NODE.ATTR_STATUS,
FORM_STATUS_TEXT_KEY,
examConfig.status.name(),
() -> resourceService.examConfigStatusResources(isAttachedToExam))
() -> resourceService.examConfigStatusResources(isAttachedToExam, hasRunningExam))
.withEmptyCellSeparation(!isReadonly))
.buildFor((isNew)
? this.restService.getRestCall(NewExamConfig.class)
@ -297,7 +308,7 @@ public class SEBExamConfigForm implements TemplateComposer {
.withEntityKey(entityKey)
.withExec(formHandle::processFormSave)
.ignoreMoveAwayFromEdit()
.withConfirm(() -> stateChangeConfirm(isAttachedToExam, formHandle))
.withConfirm(() -> stateChangeConfirm(hasRunningExam, formHandle))
.publishIf(() -> !isReadonly)
.newAction(ActionDefinition.SEB_EXAM_CONFIG_PROP_CANCEL_MODIFY)
@ -436,17 +447,17 @@ public class SEBExamConfigForm implements TemplateComposer {
}
private LocTextKey stateChangeConfirm(
final boolean isAttachedToExam,
final boolean hasRunningExam,
final FormHandle<ConfigurationNode> formHandle) {
if (isAttachedToExam) {
if (hasRunningExam) {
final String fieldValue = formHandle
.getForm()
.getFieldValue(Domain.CONFIGURATION_NODE.ATTR_STATUS);
if (fieldValue != null) {
final ConfigurationStatus state = ConfigurationStatus.valueOf(fieldValue);
if (state != ConfigurationStatus.IN_USE) {
if (state != ConfigurationStatus.IN_USE && state != ConfigurationStatus.ARCHIVED) {
return SAVE_CONFIRM_STATE_CHANGE_WHILE_ATTACHED;
}
}

View file

@ -471,7 +471,20 @@ public class ResourceService {
.collect(Collectors.toList());
}
public List<Tuple<String>> examConfigStatusResources(final boolean isAttachedToExam) {
public List<Tuple<String>> examConfigStatusResourcesAll() {
return Arrays.stream(ConfigurationStatus.values())
.map(type -> new Tuple3<>(
type.name(),
this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()),
Utils.formatLineBreaks(this.i18nSupport.getText(
this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name())
+ Constants.TOOLTIP_TEXT_KEY_SUFFIX,
StringUtils.EMPTY))))
.sorted(RESOURCE_COMPARATOR)
.collect(Collectors.toList());
}
public List<Tuple<String>> examConfigStatusResources(final boolean isAttachedToExam, final boolean hasRunningExam) {
return Arrays.stream(ConfigurationStatus.values())
.filter(status -> {
if (isAttachedToExam) {
@ -480,6 +493,7 @@ public class ResourceService {
return status != ConfigurationStatus.IN_USE;
}
})
.filter(status -> !hasRunningExam || status != ConfigurationStatus.ARCHIVED)
.map(type -> new Tuple3<>(
type.name(),
this.i18nSupport.getText(EXAMCONFIG_STATUS_PREFIX + type.name()),

View file

@ -442,6 +442,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
(exam != null) ? exam.description : null,
(exam != null) ? exam.startTime : null,
(exam != null) ? exam.type : ExamType.UNDEFINED,
(exam != null) ? exam.status : null,
record.getConfigurationNodeId(),
record.getUserNames(),
record.getEncryptSecret(),

View file

@ -136,6 +136,7 @@ public class ExamConfigServiceImpl implements ExamConfigService {
}
}
@Override
public Result<Long> getFollowupConfigurationId(final Long examConfigNodeId) {
return this.configurationDAO.getFollowupConfigurationId(examConfigNodeId);
}
@ -443,6 +444,20 @@ public class ExamConfigServiceImpl implements ExamConfigService {
}
}
// if changing to "In Use" check config is mapped for at least one exam
if (configurationNode.status == ConfigurationStatus.IN_USE &&
existingNode.status != ConfigurationStatus.IN_USE) {
if (this.examConfigurationMapDAO
.getExamIdsForConfigNodeId(configurationNode.id)
.getOr(Collections.emptyList())
.isEmpty()) {
throw new APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
.of("Exam configuration has no reference to any exam."));
}
}
return configurationNode;
});

View file

@ -209,7 +209,7 @@ public class ModelObjectJSONGenerator {
System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject));
domainObject = new ExamConfigurationMap(
1L, 1L, 1L, "examName", "examDescription", DateTime.now(), ExamType.BYOD,
1L, 1L, 1L, "examName", "examDescription", DateTime.now(), ExamType.BYOD, ExamStatus.RUNNING,
1L, "userNames", "encryptSecret", "confirmEncryptSecret", "configName", "configDescription",
ConfigurationStatus.IN_USE);
System.out.println(domainObject.getClass().getSimpleName() + ":");