SEBSERV-260 new ARCHIVED state for exam configurations

This commit is contained in:
anhefti 2022-01-26 12:05:38 +01:00
parent 3afed86efa
commit f9eb0b2535
9 changed files with 68 additions and 17 deletions

View file

@ -39,7 +39,8 @@ public final class ConfigurationNode implements GrantEntity {
public enum ConfigurationStatus {
CONSTRUCTION,
READY_TO_USE,
IN_USE
IN_USE,
ARCHIVED
}
@JsonProperty(CONFIGURATION_NODE.ATTR_ID)

View file

@ -95,7 +95,7 @@ public class SEBExamConfigList implements TemplateComposer {
this.statusFilter = new TableFilterAttribute(
CriteriaType.SINGLE_SELECTION,
ConfigurationNode.FILTER_ATTR_STATUS,
this.resourceService::examConfigStatusResources);
this.resourceService::examConfigStatusFilterResources);
}
@Override

View file

@ -450,8 +450,17 @@ public class ResourceService {
.collect(Collectors.toList());
}
public List<Tuple<String>> examConfigStatusResources() {
return examConfigStatusResources(false);
public List<Tuple<String>> examConfigStatusFilterResources() {
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) {

View file

@ -181,7 +181,7 @@ public final class PageAction {
if (confirmMessage != null) {
this.pageContext.applyConfirmDialog(confirmMessage,
confirm -> callback.accept((confirm)
? exec()
? exec().onError(error -> this.pageContext.notifyUnexpectedError(error))
: Result.ofRuntimeError("Confirm denied")));
} else {
callback.accept(exec());

View file

@ -67,6 +67,13 @@ public interface ExamConfigurationMapDAO extends
* @return Result referencing the List of exam identifiers (PK) for a given configuration identifier */
Result<Collection<Long>> getExamIdsForConfigId(Long configurationId);
Result<Boolean> checkForDeletion(Long configurationNodeId);
/** Use this to check if a specified exam configuration don't have any relations
* to an currently active exam, meaning in upcoming or running state.
* Exams in finished state are not active and will not go into account here.
*
* @param configurationNodeId the identifier of exam configuration to check
* @return Result refer to true if config has no relations to active exams, or false of it has or refer to an error
* if happened */
Result<Boolean> checkNoActiveExamReferences(Long configurationNodeId);
}

View file

@ -19,6 +19,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import org.apache.commons.lang3.StringUtils;
import org.mybatis.dynamic.sql.SqlBuilder;
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
@ -123,17 +124,15 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO {
.on(InstitutionRecordDynamicSqlSupport.id,
SqlBuilder.equalTo(ConfigurationNodeRecordDynamicSqlSupport.institutionId))
.where(
ConfigurationNodeRecordDynamicSqlSupport.status,
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeStatus()))
ConfigurationNodeRecordDynamicSqlSupport.institutionId,
SqlBuilder.isEqualToWhenPresent(filterMap.getInstitutionId()))
: this.configurationNodeRecordMapper
.selectByExample()
.where(
ConfigurationNodeRecordDynamicSqlSupport.status,
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeStatus()));
ConfigurationNodeRecordDynamicSqlSupport.institutionId,
SqlBuilder.isEqualToWhenPresent(filterMap.getInstitutionId()));
return whereClause.and(
ConfigurationNodeRecordDynamicSqlSupport.institutionId,
SqlBuilder.isEqualToWhenPresent(filterMap.getInstitutionId()))
whereClause
.and(
ConfigurationNodeRecordDynamicSqlSupport.name,
SqlBuilder.isLikeWhenPresent(filterMap.getName()))
@ -145,7 +144,20 @@ public class ConfigurationNodeDAOImpl implements ConfigurationNodeDAO {
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeType()))
.and(
ConfigurationNodeRecordDynamicSqlSupport.templateId,
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeTemplateId()))
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeTemplateId()));
final String status = filterMap.getConfigNodeStatus();
if (StringUtils.isBlank(status)) {
whereClause.and(
ConfigurationNodeRecordDynamicSqlSupport.status,
SqlBuilder.isNotEqualToWhenPresent(ConfigurationStatus.ARCHIVED.name()));
} else {
whereClause.and(
ConfigurationNodeRecordDynamicSqlSupport.status,
SqlBuilder.isEqualToWhenPresent(filterMap.getConfigNodeStatus()));
}
return whereClause
.build()
.execute()
.stream()

View file

@ -375,7 +375,7 @@ public class ExamConfigurationMapDAOImpl implements ExamConfigurationMapDAO {
@Override
@Transactional(readOnly = true)
public Result<Boolean> checkForDeletion(final Long configurationNodeId) {
public Result<Boolean> checkNoActiveExamReferences(final Long configurationNodeId) {
return Result.tryCatch(() -> !this.examConfigurationMapRecordMapper.selectByExample()
.where(
ExamConfigurationMapRecordDynamicSqlSupport.configurationNodeId,

View file

@ -511,13 +511,34 @@ public class ConfigurationNodeController extends EntityController<ConfigurationN
"The Type of ConfigurationNode cannot change after creation");
}
return e;
});
})
.map(this::checkChangeToArchived);
}
private ConfigurationNode checkChangeToArchived(final ConfigurationNode entity) {
if (entity.status == ConfigurationStatus.ARCHIVED) {
// check if we have a change to archived
final ConfigurationNode persistentNode = this.configurationNodeDAO
.byPK(entity.id)
.getOrThrow();
// yes we have
if (persistentNode.status != ConfigurationStatus.ARCHIVED) {
// check if this is possible (no upcoming or running exams involved)
if (!this.examConfigurationMapDAO.checkNoActiveExamReferences(entity.id).getOr(false)) {
throw new APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
.of("Exam configuration has references to at least one upcoming or running exam."));
}
}
}
return entity;
}
@Override
protected Result<ConfigurationNode> validForDelete(final ConfigurationNode entity) {
return Result.tryCatch(() -> {
if (!this.examConfigurationMapDAO.checkForDeletion(entity.id).getOr(false)) {
if (!this.examConfigurationMapDAO.checkNoActiveExamReferences(entity.id).getOr(false)) {
throw new APIMessageException(
APIMessage.ErrorMessage.INTEGRITY_VALIDATION
.of("Exam configuration has references to at least one upcoming or running exam."));

View file

@ -863,6 +863,7 @@ sebserver.examconfig.form.attached-to.tooltip=This SEB exam configuration is cur
sebserver.examconfig.status.CONSTRUCTION=Under Construction
sebserver.examconfig.status.READY_TO_USE=Ready To Use
sebserver.examconfig.status.IN_USE=In Use
sebserver.examconfig.status.ARCHIVED=Archived
sebserver.examconfig.props.from.unpublished.message=Note: There are unpublished changes to this Settings. Use 'Save/Publish Settings' to make sure the settings are active.
sebserver.examconfig.props.from.title=SEB Settings ({0})