Merge remote-tracking branch 'origin/rel-1.3.3'
Conflicts: pom.xml
This commit is contained in:
commit
f9957a1266
22 changed files with 424 additions and 152 deletions
2
pom.xml
2
pom.xml
|
@ -18,7 +18,7 @@
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
<properties>
|
||||||
<sebserver-version>1.3.2</sebserver-version>
|
<sebserver-version>1.3.3</sebserver-version>
|
||||||
<build-version>${sebserver-version}</build-version>
|
<build-version>${sebserver-version}</build-version>
|
||||||
<revision>${sebserver-version}</revision>
|
<revision>${sebserver-version}</revision>
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
|
|
|
@ -95,6 +95,17 @@ public class IndicatorTemplate implements Entity {
|
||||||
this.thresholds = postParams.getThresholds();
|
this.thresholds = postParams.getThresholds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IndicatorTemplate(final Long id, final IndicatorTemplate other) {
|
||||||
|
this.id = id;
|
||||||
|
this.examTemplateId = other.examTemplateId;
|
||||||
|
this.name = other.name;
|
||||||
|
this.type = other.type;
|
||||||
|
this.defaultColor = other.defaultColor;
|
||||||
|
this.defaultIcon = other.defaultIcon;
|
||||||
|
this.tags = other.tags;
|
||||||
|
this.thresholds = Utils.immutableListOf(other.thresholds);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getModelId() {
|
public String getModelId() {
|
||||||
return (this.id == null) ? null : String.valueOf(this.id);
|
return (this.id == null) ? null : String.valueOf(this.id);
|
||||||
|
@ -168,7 +179,7 @@ public class IndicatorTemplate implements Entity {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
builder.append("Indicator [id=");
|
builder.append("Indicator [id=");
|
||||||
builder.append(this.id);
|
builder.append(this.id);
|
||||||
builder.append(", examId=");
|
builder.append(", examTemplateId=");
|
||||||
builder.append(this.examTemplateId);
|
builder.append(this.examTemplateId);
|
||||||
builder.append(", name=");
|
builder.append(", name=");
|
||||||
builder.append(this.name);
|
builder.append(this.name);
|
||||||
|
|
|
@ -82,10 +82,14 @@ public final class MultiSelectionCheckbox extends Composite implements Selection
|
||||||
WidgetFactory.setARIALabel(button, tuple._2);
|
WidgetFactory.setARIALabel(button, tuple._2);
|
||||||
this.checkboxes.put(tuple._1, button);
|
this.checkboxes.put(tuple._1, button);
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
try {
|
||||||
final Tuple3<String> tuple3 = tuple.adaptTo(Tuple3.class);
|
@SuppressWarnings("unchecked")
|
||||||
if (tuple3 != null && StringUtils.isNotBlank(tuple3._3)) {
|
final Tuple3<String> tuple3 = tuple.adaptTo(Tuple3.class);
|
||||||
button.setToolTipText(tuple3._3);
|
if (tuple3 != null && StringUtils.isNotBlank(tuple3._3)) {
|
||||||
|
button.setToolTipText(tuple3._3);
|
||||||
|
}
|
||||||
|
} catch (final Exception e) {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ package ch.ethz.seb.sebserver.webservice.datalayer.checks;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -19,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||||
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
import ch.ethz.seb.sebserver.gbl.profile.WebServiceProfile;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck;
|
import ch.ethz.seb.sebserver.webservice.DBIntegrityCheck;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.OrientationRecordMapper;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.OrientationRecord;
|
||||||
|
|
||||||
|
@ -68,10 +71,15 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tryFix) {
|
if (tryFix) {
|
||||||
toDelete
|
final List<Long> checkedToDelete = toDelete
|
||||||
|
.stream()
|
||||||
|
.filter(this::doubleCheck)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
checkedToDelete
|
||||||
.stream()
|
.stream()
|
||||||
.forEach(this.orientationRecordMapper::deleteByPrimaryKey);
|
.forEach(this.orientationRecordMapper::deleteByPrimaryKey);
|
||||||
return "Fixed duplicates by deletion: " + toDelete;
|
return "Fixed duplicates by deletion: " + checkedToDelete + " from findings:" + toDelete;
|
||||||
} else {
|
} else {
|
||||||
return "Found duplicates: " + toDelete;
|
return "Found duplicates: " + toDelete;
|
||||||
}
|
}
|
||||||
|
@ -79,6 +87,24 @@ public class OrientationTableDuplicatesCheck implements DBIntegrityCheck {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean doubleCheck(final Long id) {
|
||||||
|
try {
|
||||||
|
final OrientationRecord selectByPrimaryKey = this.orientationRecordMapper.selectByPrimaryKey(id);
|
||||||
|
final Long count = this.orientationRecordMapper.countByExample()
|
||||||
|
.where(
|
||||||
|
OrientationRecordDynamicSqlSupport.configAttributeId,
|
||||||
|
SqlBuilder.isEqualTo(selectByPrimaryKey.getConfigAttributeId()))
|
||||||
|
.and(
|
||||||
|
OrientationRecordDynamicSqlSupport.templateId,
|
||||||
|
SqlBuilder.isEqualTo(selectByPrimaryKey.getTemplateId()))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
return count != null && count.longValue() > 1;
|
||||||
|
} catch (final Exception e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
final StringBuilder builder = new StringBuilder();
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
|
|
@ -32,6 +32,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionR
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientEventRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ConfigurationNodeRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.LmsSetupRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.SebClientConfigRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.SebClientConfigRecordDynamicSqlSupport;
|
||||||
|
@ -203,11 +204,11 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
if (StringUtils.isNotBlank(sortColumnName)) {
|
if (StringUtils.isNotBlank(sortColumnName)) {
|
||||||
switch (sortOrder) {
|
switch (sortOrder) {
|
||||||
case DESCENDING: {
|
case DESCENDING: {
|
||||||
PageHelper.orderBy(sortColumnName + " DESC");
|
PageHelper.orderBy(sortColumnName + " DESC, id DESC");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default: {
|
default: {
|
||||||
PageHelper.orderBy(sortColumnName);
|
PageHelper.orderBy(sortColumnName + ", id");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -265,6 +266,18 @@ public class PaginationServiceImpl implements PaginationService {
|
||||||
this.sortColumnMapping.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), lmsSetupTableMap);
|
this.sortColumnMapping.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), lmsSetupTableMap);
|
||||||
this.defaultSortColumn.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), Domain.LMS_SETUP.ATTR_ID);
|
this.defaultSortColumn.put(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord.name(), Domain.LMS_SETUP.ATTR_ID);
|
||||||
|
|
||||||
|
// Exam Template Table
|
||||||
|
final Map<String, String> examTemplateTableMap = new HashMap<>();
|
||||||
|
examTemplateTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef);
|
||||||
|
examTemplateTableMap.put(Domain.EXAM_TEMPLATE.ATTR_NAME, ExamTemplateRecordDynamicSqlSupport.name.name());
|
||||||
|
examTemplateTableMap.put(Domain.EXAM_TEMPLATE.ATTR_EXAM_TYPE,
|
||||||
|
ExamTemplateRecordDynamicSqlSupport.examType.name());
|
||||||
|
|
||||||
|
this.sortColumnMapping.put(ExamTemplateRecordDynamicSqlSupport.examTemplateRecord.name(), examTemplateTableMap);
|
||||||
|
this.defaultSortColumn.put(
|
||||||
|
ExamTemplateRecordDynamicSqlSupport.examTemplateRecord.name(),
|
||||||
|
Domain.EXAM_TEMPLATE.ATTR_ID);
|
||||||
|
|
||||||
// Exam Table
|
// Exam Table
|
||||||
final Map<String, String> examTableMap = new HashMap<>();
|
final Map<String, String> examTableMap = new HashMap<>();
|
||||||
examTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef);
|
examTableMap.put(Entity.FILTER_ATTR_INSTITUTION, institutionNameRef);
|
||||||
|
|
|
@ -105,4 +105,10 @@ public interface ConfigurationDAO extends EntityDAO<Configuration, Configuration
|
||||||
* @return the last version of configuration */
|
* @return the last version of configuration */
|
||||||
Result<Configuration> getConfigurationLastStableVersion(Long configNodeId);
|
Result<Configuration> getConfigurationLastStableVersion(Long configNodeId);
|
||||||
|
|
||||||
|
/** Use this to get the follow-up configuration identifer for a specified configuration node.
|
||||||
|
*
|
||||||
|
* @param configurationNode ConfigurationNode to get the current follow-up configuration from
|
||||||
|
* @return the current follow-up configuration identifier */
|
||||||
|
Result<Long> getFollowupConfigurationId(Long configNodeId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,9 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
|
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Result;
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionSupportDAO;
|
||||||
|
|
||||||
|
@ -21,4 +23,22 @@ public interface ExamTemplateDAO extends EntityDAO<ExamTemplate, ExamTemplate>,
|
||||||
* @return Result refer to the ExamTemplate instance or to an error when happened */
|
* @return Result refer to the ExamTemplate instance or to an error when happened */
|
||||||
Result<ExamTemplate> getInstitutionalDefault(Long institutionId);
|
Result<ExamTemplate> getInstitutionalDefault(Long institutionId);
|
||||||
|
|
||||||
|
/** Creates a new indicator template
|
||||||
|
*
|
||||||
|
* @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId)
|
||||||
|
* @return Result refer to the created IndicatorTemplate or to an error when happened */
|
||||||
|
Result<IndicatorTemplate> createNewIndicatorTemplate(IndicatorTemplate indicatorTemplate);
|
||||||
|
|
||||||
|
/** Saves an already existing indicator template
|
||||||
|
*
|
||||||
|
* @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId)
|
||||||
|
* @return Result refer to the saved IndicatorTemplate or to an error when happened */
|
||||||
|
Result<IndicatorTemplate> saveIndicatorTemplate(IndicatorTemplate indicatorTemplate);
|
||||||
|
|
||||||
|
/** Deletes an already existing indicator template
|
||||||
|
*
|
||||||
|
* @param indicatorTemplate The IndicatorTemplate refer also to the exam template (examTemplateId)
|
||||||
|
* @return Result refer to the EntityKey of the deleted IndicatorTemplate or to an error when happened */
|
||||||
|
Result<EntityKey> deleteIndicatorTemplate(String examTemplateId, String indicatorTemplateId);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao;
|
||||||
|
|
||||||
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
import ch.ethz.seb.sebserver.gbl.model.Entity;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
import ch.ethz.seb.sebserver.gbl.model.EntityProcessingReport;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserAccount;
|
||||||
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
import ch.ethz.seb.sebserver.gbl.model.user.UserActivityLog;
|
||||||
|
@ -83,6 +84,12 @@ public interface UserActivityLogDAO extends
|
||||||
* @return Result of the Entity or referring to an Error if happened */
|
* @return Result of the Entity or referring to an Error if happened */
|
||||||
<E extends Entity> Result<E> logDelete(E entity);
|
<E extends Entity> Result<E> logDelete(E entity);
|
||||||
|
|
||||||
|
/** Create a user activity log entry for the current user of activity type DELETE
|
||||||
|
*
|
||||||
|
* @param entityKey the EntityKey of the deleted object
|
||||||
|
* @return Result of the EntityKey or referring to an Error if happened */
|
||||||
|
Result<EntityKey> logDelete(EntityKey entityKey);
|
||||||
|
|
||||||
/** Used to log a successful bulk action and uses the EntityProcessingReport from the
|
/** Used to log a successful bulk action and uses the EntityProcessingReport from the
|
||||||
* bulk action to log all details.
|
* bulk action to log all details.
|
||||||
*
|
*
|
||||||
|
|
|
@ -133,8 +133,24 @@ public class ConfigurationDAOImpl implements ConfigurationDAO {
|
||||||
.build()
|
.build()
|
||||||
.execute()
|
.execute()
|
||||||
.stream()
|
.stream()
|
||||||
.collect(Utils.toSingleton())).flatMap(ConfigurationDAOImpl::toDomainModel);
|
.collect(Utils.toSingleton()))
|
||||||
|
.flatMap(ConfigurationDAOImpl::toDomainModel);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Long> getFollowupConfigurationId(final Long configNodeId) {
|
||||||
|
return Result.tryCatch(() -> this.configurationRecordMapper.selectIdsByExample()
|
||||||
|
.where(
|
||||||
|
ConfigurationRecordDynamicSqlSupport.configurationNodeId,
|
||||||
|
isEqualTo(configNodeId))
|
||||||
|
.and(
|
||||||
|
ConfigurationRecordDynamicSqlSupport.followup,
|
||||||
|
isEqualTo(BooleanUtils.toInteger(true)))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.collect(Utils.toSingleton()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -16,12 +16,16 @@ import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import org.apache.commons.lang3.BooleanUtils;
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
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;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
import org.springframework.transaction.annotation.Transactional;
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
@ -42,6 +46,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordMapper;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamTemplateRecordMapper;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDynamicSqlSupport;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.InstitutionRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamTemplateRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamTemplateRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.impl.BulkAction;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
||||||
|
@ -140,24 +145,38 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
final FilterMap filterMap,
|
final FilterMap filterMap,
|
||||||
final Predicate<ExamTemplate> predicate) {
|
final Predicate<ExamTemplate> predicate) {
|
||||||
|
|
||||||
return Result.tryCatch(() -> this.examTemplateRecordMapper
|
return Result.tryCatch(() -> {
|
||||||
.selectByExample()
|
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<ExamTemplateRecord>>>.QueryExpressionWhereBuilder whereClause =
|
||||||
.where(
|
(filterMap.getBoolean(FilterMap.ATTR_ADD_INSITUTION_JOIN))
|
||||||
ExamTemplateRecordDynamicSqlSupport.institutionId,
|
? this.examTemplateRecordMapper
|
||||||
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
.selectByExample()
|
||||||
.and(
|
.join(InstitutionRecordDynamicSqlSupport.institutionRecord)
|
||||||
ExamTemplateRecordDynamicSqlSupport.name,
|
.on(InstitutionRecordDynamicSqlSupport.id,
|
||||||
isLikeWhenPresent(filterMap.getExamTemplateName()))
|
SqlBuilder.equalTo(ExamTemplateRecordDynamicSqlSupport.institutionId))
|
||||||
.and(
|
.where(
|
||||||
ExamTemplateRecordDynamicSqlSupport.examType,
|
ExamTemplateRecordDynamicSqlSupport.institutionId,
|
||||||
isEqualToWhenPresent(filterMap.getString(ExamTemplate.FILTER_ATTR_EXAM_TYPE)))
|
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
||||||
.build()
|
: this.examTemplateRecordMapper
|
||||||
.execute()
|
.selectByExample()
|
||||||
.stream()
|
.where(
|
||||||
.map(this::toDomainModel)
|
ExamTemplateRecordDynamicSqlSupport.institutionId,
|
||||||
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
isEqualToWhenPresent(filterMap.getInstitutionId()));
|
||||||
.filter(predicate)
|
|
||||||
.collect(Collectors.toList()));
|
return whereClause
|
||||||
|
.and(
|
||||||
|
ExamTemplateRecordDynamicSqlSupport.name,
|
||||||
|
isLikeWhenPresent(filterMap.getExamTemplateName()))
|
||||||
|
.and(
|
||||||
|
ExamTemplateRecordDynamicSqlSupport.examType,
|
||||||
|
isEqualToWhenPresent(filterMap.getString(ExamTemplate.FILTER_ATTR_EXAM_TYPE)))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.map(this::toDomainModel)
|
||||||
|
.flatMap(DAOLoggingSupport::logAndSkipOnError)
|
||||||
|
.filter(predicate)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -202,11 +221,6 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
checkUniqueName(data);
|
checkUniqueName(data);
|
||||||
checkUniqueDefault(data);
|
checkUniqueDefault(data);
|
||||||
|
|
||||||
final Collection<IndicatorTemplate> indicatorTemplates = data.getIndicatorTemplates();
|
|
||||||
final String indicatorsJSON = (indicatorTemplates != null && !indicatorTemplates.isEmpty())
|
|
||||||
? this.jsonMapper.writeValueAsString(indicatorTemplates)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
|
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
|
||||||
data.id,
|
data.id,
|
||||||
null,
|
null,
|
||||||
|
@ -219,7 +233,7 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
(data.supporter != null)
|
(data.supporter != null)
|
||||||
? StringUtils.join(data.supporter, Constants.LIST_SEPARATOR_CHAR)
|
? StringUtils.join(data.supporter, Constants.LIST_SEPARATOR_CHAR)
|
||||||
: null,
|
: null,
|
||||||
indicatorsJSON,
|
null,
|
||||||
BooleanUtils.toInteger(data.institutionalDefault));
|
BooleanUtils.toInteger(data.institutionalDefault));
|
||||||
|
|
||||||
this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord);
|
this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
|
@ -241,6 +255,137 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
.onError(TransactionHandler::rollback);
|
.onError(TransactionHandler::rollback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<IndicatorTemplate> createNewIndicatorTemplate(final IndicatorTemplate indicatorTemplate) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Create new indicator template: {}", indicatorTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Long examTemplatePK = indicatorTemplate.examTemplateId;
|
||||||
|
final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper
|
||||||
|
.selectByPrimaryKey(examTemplatePK);
|
||||||
|
final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates();
|
||||||
|
final Collection<IndicatorTemplate> indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON))
|
||||||
|
? this.jsonMapper.readValue(
|
||||||
|
indicatorTemplatesJSON,
|
||||||
|
new TypeReference<Collection<IndicatorTemplate>>() {
|
||||||
|
})
|
||||||
|
: Collections.emptyList();
|
||||||
|
|
||||||
|
checkUniqueIndicatorName(indicatorTemplate, indicators);
|
||||||
|
|
||||||
|
final IndicatorTemplate newIndicatorTemplate = new IndicatorTemplate(
|
||||||
|
getNextIndicatorId(indicators),
|
||||||
|
indicatorTemplate);
|
||||||
|
|
||||||
|
final List<IndicatorTemplate> newIndicators = new ArrayList<>(indicators);
|
||||||
|
newIndicators.add(newIndicatorTemplate);
|
||||||
|
|
||||||
|
final String newIndicatorTemplatesJSON = newIndicators.isEmpty()
|
||||||
|
? StringUtils.EMPTY
|
||||||
|
: this.jsonMapper.writeValueAsString(newIndicators);
|
||||||
|
|
||||||
|
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
|
||||||
|
examTemplatePK, null, null, null, null, null, null,
|
||||||
|
newIndicatorTemplatesJSON, null);
|
||||||
|
|
||||||
|
this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
|
|
||||||
|
return newIndicatorTemplate;
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<IndicatorTemplate> saveIndicatorTemplate(final IndicatorTemplate indicatorTemplate) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Save indicator template: {}", indicatorTemplate);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Long examTemplatePK = indicatorTemplate.examTemplateId;
|
||||||
|
final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper
|
||||||
|
.selectByPrimaryKey(examTemplatePK);
|
||||||
|
final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates();
|
||||||
|
final Collection<IndicatorTemplate> indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON))
|
||||||
|
? this.jsonMapper.readValue(
|
||||||
|
indicatorTemplatesJSON,
|
||||||
|
new TypeReference<Collection<IndicatorTemplate>>() {
|
||||||
|
})
|
||||||
|
: Collections.emptyList();
|
||||||
|
|
||||||
|
checkUniqueIndicatorName(indicatorTemplate, indicators);
|
||||||
|
|
||||||
|
final List<IndicatorTemplate> newIndicators = indicators
|
||||||
|
.stream()
|
||||||
|
.map(i -> indicatorTemplate.id.equals(i.id) ? indicatorTemplate : i)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final String newIndicatorTemplatesJSON = newIndicators.isEmpty()
|
||||||
|
? StringUtils.EMPTY
|
||||||
|
: this.jsonMapper.writeValueAsString(newIndicators);
|
||||||
|
|
||||||
|
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
|
||||||
|
examTemplatePK, null, null, null, null, null, null,
|
||||||
|
newIndicatorTemplatesJSON, null);
|
||||||
|
|
||||||
|
this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
|
|
||||||
|
return indicatorTemplate;
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<EntityKey> deleteIndicatorTemplate(
|
||||||
|
final String examTemplateId,
|
||||||
|
final String indicatorTemplateId) {
|
||||||
|
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug(
|
||||||
|
"Delete indicator template for exam template: {} indicator template id",
|
||||||
|
examTemplateId,
|
||||||
|
indicatorTemplateId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final Long examTemplatePK = Long.valueOf(examTemplateId);
|
||||||
|
final ExamTemplateRecord examTemplateRec = this.examTemplateRecordMapper
|
||||||
|
.selectByPrimaryKey(examTemplatePK);
|
||||||
|
final String indicatorTemplatesJSON = examTemplateRec.getIndicatorTemplates();
|
||||||
|
final Collection<IndicatorTemplate> indicators = (StringUtils.isNotBlank(indicatorTemplatesJSON))
|
||||||
|
? this.jsonMapper.readValue(
|
||||||
|
indicatorTemplatesJSON,
|
||||||
|
new TypeReference<Collection<IndicatorTemplate>>() {
|
||||||
|
})
|
||||||
|
: Collections.emptyList();
|
||||||
|
|
||||||
|
final List<IndicatorTemplate> newIndicators = indicators.stream()
|
||||||
|
.filter(indicatorTemplate -> !indicatorTemplateId.equals(indicatorTemplate.getModelId()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
|
||||||
|
final String newIndicatorTemplatesJSON = newIndicators.isEmpty()
|
||||||
|
? StringUtils.EMPTY
|
||||||
|
: this.jsonMapper.writeValueAsString(newIndicators);
|
||||||
|
|
||||||
|
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
|
||||||
|
examTemplatePK, null, null, null, null, null, null,
|
||||||
|
newIndicatorTemplatesJSON, null);
|
||||||
|
|
||||||
|
this.examTemplateRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
|
|
||||||
|
return new EntityKey(indicatorTemplateId, EntityType.INDICATOR);
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<EntityDependency> getDependencies(final BulkAction bulkAction) {
|
public Set<EntityDependency> getDependencies(final BulkAction bulkAction) {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
|
@ -251,7 +396,9 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
log.info("Delete exam templates: {}", all);
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Delete exam templates: {}", all);
|
||||||
|
}
|
||||||
|
|
||||||
final List<Long> ids = extractListOfPKs(all);
|
final List<Long> ids = extractListOfPKs(all);
|
||||||
if (ids == null || ids.isEmpty()) {
|
if (ids == null || ids.isEmpty()) {
|
||||||
|
@ -387,4 +534,24 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void checkUniqueIndicatorName(final IndicatorTemplate indicatorTemplate,
|
||||||
|
final Collection<IndicatorTemplate> indicators) {
|
||||||
|
// check unique name
|
||||||
|
indicators.stream()
|
||||||
|
.filter(it -> Objects.equals(it.name, indicatorTemplate.name))
|
||||||
|
.findAny()
|
||||||
|
.ifPresent(it -> {
|
||||||
|
throw new FieldValidationException(
|
||||||
|
"name",
|
||||||
|
"indicatorTemplate:name:exists");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getNextIndicatorId(final Collection<IndicatorTemplate> indicators) {
|
||||||
|
return indicators.stream()
|
||||||
|
.map(IndicatorTemplate::getId)
|
||||||
|
.max(Long::compare)
|
||||||
|
.orElse(-1L) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,15 @@ public class UserActivityLogDAOImpl implements UserActivityLogDAO {
|
||||||
return log(UserLogActivityType.DELETE, entity);
|
return log(UserLogActivityType.DELETE, entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Transactional
|
||||||
|
public Result<EntityKey> logDelete(final EntityKey entityKey) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
log(UserLogActivityType.DELETE, entityKey.entityType, entityKey.modelId, null);
|
||||||
|
return entityKey;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<EntityProcessingReport> logBulkAction(final EntityProcessingReport bulkActionReport) {
|
public Result<EntityProcessingReport> logBulkAction(final EntityProcessingReport bulkActionReport) {
|
||||||
|
|
|
@ -130,6 +130,13 @@ public class MockupLmsAPITemplate implements LmsAPITemplate {
|
||||||
DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS)
|
DateTime.now(DateTimeZone.UTC).plus(6 * Constants.MINUTE_IN_MILLIS)
|
||||||
.toString(Constants.DEFAULT_DATE_TIME_FORMAT),
|
.toString(Constants.DEFAULT_DATE_TIME_FORMAT),
|
||||||
"http://lms.mockup.com/api/"));
|
"http://lms.mockup.com/api/"));
|
||||||
|
this.mockups.add(new QuizData(
|
||||||
|
"quiz11", institutionId, lmsSetupId, lmsType, "Demo Quiz 11 (MOCKUP)",
|
||||||
|
"Starts in a minute and ends never",
|
||||||
|
DateTime.now(DateTimeZone.UTC).plus(Constants.MINUTE_IN_MILLIS)
|
||||||
|
.toString(Constants.DEFAULT_DATE_TIME_FORMAT),
|
||||||
|
null,
|
||||||
|
"http://lms.mockup.com/api/"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,6 +35,13 @@ public interface ExamConfigService {
|
||||||
* @throws FieldValidationException on validation exception */
|
* @throws FieldValidationException on validation exception */
|
||||||
void validate(ConfigurationTableValues tableValue) throws FieldValidationException;
|
void validate(ConfigurationTableValues tableValue) throws FieldValidationException;
|
||||||
|
|
||||||
|
/** Get the follow-up configuration identifier for a given configuration node identifier.
|
||||||
|
*
|
||||||
|
* @param examConfigNodeId the exam configuration node identifier
|
||||||
|
* @return Result refer to the follow-up configuration identifier of the given config node or to an error when
|
||||||
|
* happened */
|
||||||
|
Result<Long> getFollowupConfigurationId(final Long examConfigNodeId);
|
||||||
|
|
||||||
/** Used to export a specified SEB Exam Configuration as plain XML
|
/** Used to export a specified SEB Exam Configuration as plain XML
|
||||||
* This exports the values of the follow-up configuration defined by a given
|
* This exports the values of the follow-up configuration defined by a given
|
||||||
* ConfigurationNode (configurationNodeId)
|
* ConfigurationNode (configurationNodeId)
|
||||||
|
|
|
@ -128,6 +128,10 @@ public class ExamConfigServiceImpl implements ExamConfigService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Result<Long> getFollowupConfigurationId(final Long examConfigNodeId) {
|
||||||
|
return this.configurationDAO.getFollowupConfigurationId(examConfigNodeId);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void exportPlainXML(
|
public void exportPlainXML(
|
||||||
final OutputStream out,
|
final OutputStream out,
|
||||||
|
|
|
@ -173,8 +173,12 @@ public class ExamSessionCacheService {
|
||||||
byteOut,
|
byteOut,
|
||||||
institutionId,
|
institutionId,
|
||||||
examId);
|
examId);
|
||||||
|
final Long followupId = this.sebExamConfigService
|
||||||
|
.getFollowupConfigurationId(configId)
|
||||||
|
.onError(error -> log.error("Failed to get follow-up id for config node: {}", configId, error))
|
||||||
|
.getOr(-1L);
|
||||||
|
|
||||||
return new InMemorySEBConfig(configId, examId, byteOut.toByteArray());
|
return new InMemorySEBConfig(configId, followupId, examId, byteOut.toByteArray());
|
||||||
|
|
||||||
} catch (final Exception e) {
|
} catch (final Exception e) {
|
||||||
log.error("Unexpected error while getting default exam configuration for running exam; {}", examId, e);
|
log.error("Unexpected error while getting default exam configuration for running exam; {}", examId, e);
|
||||||
|
@ -182,6 +186,19 @@ public class ExamSessionCacheService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isUpToDate(final InMemorySEBConfig inMemorySEBConfig) {
|
||||||
|
try {
|
||||||
|
final Long followupId = this.sebExamConfigService
|
||||||
|
.getFollowupConfigurationId(inMemorySEBConfig.configId)
|
||||||
|
.getOrThrow();
|
||||||
|
|
||||||
|
return followupId.equals(inMemorySEBConfig.follwupId);
|
||||||
|
} catch (final Exception e) {
|
||||||
|
log.error("Failed to check if InMemorySEBConfig is up to date for: {}", inMemorySEBConfig);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@CacheEvict(
|
@CacheEvict(
|
||||||
cacheNames = CACHE_NAME_SEB_CONFIG_EXAM,
|
cacheNames = CACHE_NAME_SEB_CONFIG_EXAM,
|
||||||
key = "#examId")
|
key = "#examId")
|
||||||
|
|
|
@ -161,7 +161,7 @@ public class ExamSessionControlTask implements DisposableBean {
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(exam -> exam.startTime.minus(this.examTimePrefix).isBefore(now))
|
.filter(exam -> exam.startTime.minus(this.examTimePrefix).isBefore(now))
|
||||||
.filter(exam -> exam.endTime != null && exam.endTime.plus(this.examTimeSuffix).isAfter(now))
|
.filter(exam -> exam.endTime == null || exam.endTime.plus(this.examTimeSuffix).isAfter(now))
|
||||||
.flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId)))
|
.flatMap(exam -> Result.skipOnError(this.examUpdateHandler.setRunning(exam, updateId)))
|
||||||
.collect(Collectors.toMap(Exam::getId, Exam::getName));
|
.collect(Collectors.toMap(Exam::getId, Exam::getName));
|
||||||
|
|
||||||
|
|
|
@ -285,7 +285,7 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
log.trace("Trying to get exam from InMemorySEBConfig");
|
log.trace("Trying to get exam from InMemorySEBConfig");
|
||||||
}
|
}
|
||||||
|
|
||||||
final InMemorySEBConfig sebConfigForExam = this.examSessionCacheService
|
InMemorySEBConfig sebConfigForExam = this.examSessionCacheService
|
||||||
.getDefaultSEBConfigForExam(connection.examId, institutionId);
|
.getDefaultSEBConfigForExam(connection.examId, institutionId);
|
||||||
|
|
||||||
if (sebConfigForExam == null) {
|
if (sebConfigForExam == null) {
|
||||||
|
@ -293,6 +293,23 @@ public class ExamSessionServiceImpl implements ExamSessionService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for distributed setups check if cached config is still up to date. Flush and reload if not.
|
||||||
|
if (this.distributedSetup && !this.examSessionCacheService.isUpToDate(sebConfigForExam)) {
|
||||||
|
|
||||||
|
if (log.isDebugEnabled()) {
|
||||||
|
log.debug("Detected new version of exam configuration for exam {} ...flush cache", connection.examId);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.examSessionCacheService.evictDefaultSEBConfig(connection.examId);
|
||||||
|
sebConfigForExam = this.examSessionCacheService
|
||||||
|
.getDefaultSEBConfigForExam(connection.examId, institutionId);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sebConfigForExam == null) {
|
||||||
|
log.error("Failed to get and cache InMemorySEBConfig for connection: {}", connection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
if (log.isTraceEnabled()) {
|
if (log.isTraceEnabled()) {
|
||||||
|
|
|
@ -11,12 +11,19 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.session.impl;
|
||||||
public final class InMemorySEBConfig {
|
public final class InMemorySEBConfig {
|
||||||
|
|
||||||
public final Long configId;
|
public final Long configId;
|
||||||
|
public final Long follwupId;
|
||||||
public final Long examId;
|
public final Long examId;
|
||||||
private final byte[] data;
|
private final byte[] data;
|
||||||
|
|
||||||
protected InMemorySEBConfig(final Long configId, final Long examId, final byte[] data) {
|
protected InMemorySEBConfig(
|
||||||
|
final Long configId,
|
||||||
|
final Long follwupId,
|
||||||
|
final Long examId,
|
||||||
|
final byte[] data) {
|
||||||
|
|
||||||
super();
|
super();
|
||||||
this.configId = configId;
|
this.configId = configId;
|
||||||
|
this.follwupId = follwupId;
|
||||||
this.examId = examId;
|
this.examId = examId;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
}
|
}
|
||||||
|
@ -39,6 +46,7 @@ public final class InMemorySEBConfig {
|
||||||
int result = 1;
|
int result = 1;
|
||||||
result = prime * result + ((this.configId == null) ? 0 : this.configId.hashCode());
|
result = prime * result + ((this.configId == null) ? 0 : this.configId.hashCode());
|
||||||
result = prime * result + ((this.examId == null) ? 0 : this.examId.hashCode());
|
result = prime * result + ((this.examId == null) ? 0 : this.examId.hashCode());
|
||||||
|
result = prime * result + ((this.follwupId == null) ? 0 : this.follwupId.hashCode());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +69,25 @@ public final class InMemorySEBConfig {
|
||||||
return false;
|
return false;
|
||||||
} else if (!this.examId.equals(other.examId))
|
} else if (!this.examId.equals(other.examId))
|
||||||
return false;
|
return false;
|
||||||
|
if (this.follwupId == null) {
|
||||||
|
if (other.follwupId != null)
|
||||||
|
return false;
|
||||||
|
} else if (!this.follwupId.equals(other.follwupId))
|
||||||
|
return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
final StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append("InMemorySEBConfig [configId=");
|
||||||
|
builder.append(this.configId);
|
||||||
|
builder.append(", follwupId=");
|
||||||
|
builder.append(this.follwupId);
|
||||||
|
builder.append(", examId=");
|
||||||
|
builder.append(this.examId);
|
||||||
|
builder.append("]");
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
package ch.ethz.seb.sebserver.webservice.weblayer.api;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
|
@ -21,8 +20,6 @@ import javax.validation.Valid;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.mybatis.dynamic.sql.SqlTable;
|
import org.mybatis.dynamic.sql.SqlTable;
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.util.MultiValueMap;
|
import org.springframework.util.MultiValueMap;
|
||||||
import org.springframework.web.bind.annotation.PathVariable;
|
import org.springframework.web.bind.annotation.PathVariable;
|
||||||
|
@ -48,7 +45,6 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.PaginationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.AuthorizationService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.authorization.UserService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.bulkaction.BulkActionService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.EntityDAO;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.UserActivityLogDAO;
|
||||||
|
@ -59,12 +55,12 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.validation.BeanValidationSe
|
||||||
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_TEMPLATE_ENDPOINT)
|
@RequestMapping("${sebserver.webservice.api.admin.endpoint}" + API.EXAM_TEMPLATE_ENDPOINT)
|
||||||
public class ExamTemplateController extends EntityController<ExamTemplate, ExamTemplate> {
|
public class ExamTemplateController extends EntityController<ExamTemplate, ExamTemplate> {
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(ExamTemplateController.class);
|
private final ExamTemplateDAO examTemplateDAO;
|
||||||
|
|
||||||
protected ExamTemplateController(
|
protected ExamTemplateController(
|
||||||
final AuthorizationService authorization,
|
final AuthorizationService authorization,
|
||||||
final BulkActionService bulkActionService,
|
final BulkActionService bulkActionService,
|
||||||
final EntityDAO<ExamTemplate, ExamTemplate> entityDAO,
|
final ExamTemplateDAO entityDAO,
|
||||||
final UserActivityLogDAO userActivityLogDAO,
|
final UserActivityLogDAO userActivityLogDAO,
|
||||||
final PaginationService paginationService,
|
final PaginationService paginationService,
|
||||||
final BeanValidationService beanValidationService) {
|
final BeanValidationService beanValidationService) {
|
||||||
|
@ -76,6 +72,8 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
||||||
userActivityLogDAO,
|
userActivityLogDAO,
|
||||||
paginationService,
|
paginationService,
|
||||||
beanValidationService);
|
beanValidationService);
|
||||||
|
|
||||||
|
this.examTemplateDAO = entityDAO;
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
|
@ -177,41 +175,17 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
||||||
|
|
||||||
// check write privilege for requested institution and concrete entityType
|
// check write privilege for requested institution and concrete entityType
|
||||||
this.checkWritePrivilege(institutionId);
|
this.checkWritePrivilege(institutionId);
|
||||||
|
|
||||||
final POSTMapper postMap = new POSTMapper(allRequestParams, request.getQueryString())
|
final POSTMapper postMap = new POSTMapper(allRequestParams, request.getQueryString())
|
||||||
.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
.putIfAbsent(API.PARAM_INSTITUTION_ID, String.valueOf(institutionId));
|
||||||
|
|
||||||
final String examTemplateId = postMap.getString(IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID);
|
return this.beanValidationService
|
||||||
|
.validateBean(new IndicatorTemplate(
|
||||||
final ExamTemplate examTemplate = super.entityDAO
|
null,
|
||||||
.byModelId(examTemplateId)
|
postMap.getLong(IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID),
|
||||||
|
postMap))
|
||||||
|
.flatMap(this.examTemplateDAO::createNewIndicatorTemplate)
|
||||||
|
.flatMap(this.userActivityLogDAO::logCreate)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final IndicatorTemplate newIndicator = new IndicatorTemplate(
|
|
||||||
(long) examTemplate.getIndicatorTemplates().size(),
|
|
||||||
Long.parseLong(examTemplateId),
|
|
||||||
postMap);
|
|
||||||
|
|
||||||
this.beanValidationService.validateBean(newIndicator)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
final ArrayList<IndicatorTemplate> indicators = new ArrayList<>(examTemplate.indicatorTemplates);
|
|
||||||
indicators.add(newIndicator);
|
|
||||||
final ExamTemplate newExamTemplate = new ExamTemplate(
|
|
||||||
examTemplate.id,
|
|
||||||
null, null, null, null, null, null,
|
|
||||||
examTemplate.institutionalDefault,
|
|
||||||
indicators,
|
|
||||||
null);
|
|
||||||
|
|
||||||
super.entityDAO
|
|
||||||
.save(newExamTemplate)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
this.userActivityLogDAO.logCreate(newIndicator)
|
|
||||||
.onError(error -> log.error("Failed to log indicator template creation: {}", newIndicator, error));
|
|
||||||
|
|
||||||
return newIndicator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
|
@ -228,46 +202,11 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
||||||
|
|
||||||
// check modify privilege for requested institution and concrete entityType
|
// check modify privilege for requested institution and concrete entityType
|
||||||
this.checkModifyPrivilege(institutionId);
|
this.checkModifyPrivilege(institutionId);
|
||||||
|
return this.beanValidationService
|
||||||
final ExamTemplate examTemplate = super.entityDAO
|
.validateBean(modifyData)
|
||||||
.byPK(modifyData.examTemplateId)
|
.flatMap(this.examTemplateDAO::saveIndicatorTemplate)
|
||||||
|
.flatMap(this.userActivityLogDAO::logModify)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final String modelId = modifyData.getModelId();
|
|
||||||
final List<IndicatorTemplate> newIndicators = examTemplate.indicatorTemplates
|
|
||||||
.stream()
|
|
||||||
.map(i -> {
|
|
||||||
if (modelId.equals(i.getModelId())) {
|
|
||||||
return new IndicatorTemplate(
|
|
||||||
modifyData.id,
|
|
||||||
modifyData.examTemplateId,
|
|
||||||
modifyData.name,
|
|
||||||
(modifyData.type != null) ? modifyData.type : i.type,
|
|
||||||
(modifyData.defaultColor != null) ? modifyData.defaultColor : i.defaultColor,
|
|
||||||
(modifyData.defaultIcon != null) ? modifyData.defaultIcon : i.defaultIcon,
|
|
||||||
(modifyData.tags != null) ? modifyData.tags : i.tags,
|
|
||||||
(modifyData.thresholds != null) ? modifyData.thresholds : i.thresholds);
|
|
||||||
} else {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
|
|
||||||
final ExamTemplate newExamTemplate = new ExamTemplate(
|
|
||||||
examTemplate.id,
|
|
||||||
null, null, null, null, null, null,
|
|
||||||
examTemplate.institutionalDefault,
|
|
||||||
newIndicators,
|
|
||||||
null);
|
|
||||||
|
|
||||||
super.entityDAO
|
|
||||||
.save(newExamTemplate)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
this.userActivityLogDAO.logModify(modifyData)
|
|
||||||
.onError(error -> log.error("Failed to log indicator template modification: {}", modifyData, error));
|
|
||||||
|
|
||||||
return modifyData;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequestMapping(
|
@RequestMapping(
|
||||||
|
@ -286,35 +225,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
|
||||||
|
|
||||||
// check write privilege for requested institution and concrete entityType
|
// check write privilege for requested institution and concrete entityType
|
||||||
this.checkWritePrivilege(institutionId);
|
this.checkWritePrivilege(institutionId);
|
||||||
|
return this.examTemplateDAO.deleteIndicatorTemplate(parentModelId, modelId)
|
||||||
final ExamTemplate examTemplate = super.entityDAO
|
.flatMap(this.userActivityLogDAO::logDelete)
|
||||||
.byModelId(parentModelId)
|
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
final IndicatorTemplate toDelete = examTemplate.indicatorTemplates
|
|
||||||
.stream()
|
|
||||||
.filter(i -> modelId.equals(i.getModelId()))
|
|
||||||
.findFirst()
|
|
||||||
.orElse(null);
|
|
||||||
|
|
||||||
final List<IndicatorTemplate> newIndicators = new ArrayList<>(examTemplate.indicatorTemplates);
|
|
||||||
newIndicators.remove(toDelete);
|
|
||||||
|
|
||||||
final ExamTemplate newExamTemplate = new ExamTemplate(
|
|
||||||
examTemplate.id,
|
|
||||||
null, null, null, null, null, null,
|
|
||||||
examTemplate.institutionalDefault,
|
|
||||||
newIndicators,
|
|
||||||
null);
|
|
||||||
|
|
||||||
super.entityDAO
|
|
||||||
.save(newExamTemplate)
|
|
||||||
.getOrThrow();
|
|
||||||
|
|
||||||
this.userActivityLogDAO.logDelete(toDelete)
|
|
||||||
.onError(error -> log.error("Failed to log indicator template modification: {}", toDelete, error));
|
|
||||||
|
|
||||||
return new EntityKey(modelId, EntityType.INDICATOR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,6 +15,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
@ -248,11 +249,12 @@ public abstract class AdministrationAPIIntegrationTester {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected String getOrderedUUIDs(final Collection<? extends Entity> list) {
|
protected String getOrderedUUIDs(final Collection<? extends Entity> list) {
|
||||||
return list
|
final List<String> l = list
|
||||||
.stream()
|
.stream()
|
||||||
.map(userInfo -> userInfo.getModelId())
|
.map(userInfo -> userInfo.getModelId())
|
||||||
.collect(Collectors.toList())
|
.collect(Collectors.toList());
|
||||||
.toString();
|
l.sort((s1, s2) -> s1.compareTo(s2));
|
||||||
|
return l.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester {
|
||||||
});
|
});
|
||||||
|
|
||||||
assertNotNull(quizzes);
|
assertNotNull(quizzes);
|
||||||
assertTrue(quizzes.content.size() == 8);
|
assertTrue(quizzes.content.size() == 9);
|
||||||
|
|
||||||
// for the inactive LmsSetup we should'nt get any quizzes
|
// for the inactive LmsSetup we should'nt get any quizzes
|
||||||
quizzes = new RestAPITestHelper()
|
quizzes = new RestAPITestHelper()
|
||||||
|
@ -109,7 +109,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester {
|
||||||
});
|
});
|
||||||
|
|
||||||
assertNotNull(quizzes);
|
assertNotNull(quizzes);
|
||||||
assertTrue(quizzes.content.size() == 8);
|
assertTrue(quizzes.content.size() == 9);
|
||||||
|
|
||||||
// but for the now active lmsSetup2 we should get the quizzes
|
// but for the now active lmsSetup2 we should get the quizzes
|
||||||
quizzes = new RestAPITestHelper()
|
quizzes = new RestAPITestHelper()
|
||||||
|
@ -120,7 +120,7 @@ public class QuizDataTest extends AdministrationAPIIntegrationTester {
|
||||||
});
|
});
|
||||||
|
|
||||||
assertNotNull(quizzes);
|
assertNotNull(quizzes);
|
||||||
assertTrue(quizzes.content.size() == 8);
|
assertTrue(quizzes.content.size() == 9);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -268,7 +268,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
|
||||||
assertTrue(userInfos.numberOfPages == 1);
|
assertTrue(userInfos.numberOfPages == 1);
|
||||||
assertNotNull(userInfos.content);
|
assertNotNull(userInfos.content);
|
||||||
assertTrue(userInfos.content.size() == 3);
|
assertTrue(userInfos.content.size() == 3);
|
||||||
assertEquals("[user5, user2, user1]", getOrderedUUIDs(userInfos.content));
|
assertEquals("[user1, user2, user5]", getOrderedUUIDs(userInfos.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -347,7 +347,7 @@ public class UserAPITest extends AdministrationAPIIntegrationTester {
|
||||||
assertTrue(userInfos.numberOfPages == 2);
|
assertTrue(userInfos.numberOfPages == 2);
|
||||||
assertNotNull(userInfos.content);
|
assertNotNull(userInfos.content);
|
||||||
assertTrue(userInfos.content.size() == 3);
|
assertTrue(userInfos.content.size() == 3);
|
||||||
assertEquals("[user7, user6, user4]", getOrderedUUIDs(userInfos.content));
|
assertEquals("[user4, user6, user7]", getOrderedUUIDs(userInfos.content));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
Loading…
Reference in a new issue