separated exam-record loading (transactional) from quiz data loading
before the loading of the persistent exam data and the loading of the quiz data was running all in the same transaction what caused long-time transaction holds while fetching data from LMS. No always exam records are fetched within a DB transaction and after the transaction the exam records get mapped to Exam domain objects that needs also to load LMS data
This commit is contained in:
parent
7de512d7fe
commit
cf8aa0cd00
5 changed files with 402 additions and 276 deletions
|
@ -98,16 +98,26 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
|
||||||
*
|
*
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @param updateId an update identifier
|
* @param updateId an update identifier
|
||||||
* @return Result refer to the specified exam or to an error if happened */
|
* @return Result refer to the specified exam identifier or to an error if happened */
|
||||||
Result<Exam> placeLock(Long examId, String updateId);
|
Result<Long> placeLock(Long examId, String updateId);
|
||||||
|
|
||||||
|
default Result<Exam> placeLock(final Exam exam, final String updateId) {
|
||||||
|
return placeLock(exam.id, updateId)
|
||||||
|
.map(id -> exam);
|
||||||
|
}
|
||||||
|
|
||||||
/** This is used to release an internal (write)lock for the specified exam.
|
/** This is used to release an internal (write)lock for the specified exam.
|
||||||
* The exam will be marked as not locked on the persistence level.
|
* The exam will be marked as not locked on the persistence level.
|
||||||
*
|
*
|
||||||
* @param examId the exam identifier
|
* @param examId the exam identifier
|
||||||
* @param updateId an update identifier
|
* @param updateId an update identifier
|
||||||
* @return Result refer to the specified exam or to an error if happened */
|
* @return Result refer to the specified exam identifier or to an error if happened */
|
||||||
Result<Exam> releaseLock(Long examId, String updateId);
|
Result<Long> releaseLock(Long examId, String updateId);
|
||||||
|
|
||||||
|
default Result<Exam> releaseLock(final Exam exam, final String updateId) {
|
||||||
|
return releaseLock(exam.id, updateId)
|
||||||
|
.map(id -> exam);
|
||||||
|
}
|
||||||
|
|
||||||
/** This is used to force release an internal (write)lock for the specified exam.
|
/** This is used to force release an internal (write)lock for the specified exam.
|
||||||
* The exam will be marked as not locked on the persistence level even if it is currently locked by another process
|
* The exam will be marked as not locked on the persistence level even if it is currently locked by another process
|
||||||
|
|
|
@ -10,7 +10,6 @@ package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
|
||||||
|
|
||||||
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
@ -28,8 +27,6 @@ import org.apache.commons.lang3.BooleanUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.joda.time.DateTime;
|
import org.joda.time.DateTime;
|
||||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||||
import org.mybatis.dynamic.sql.select.MyBatis3SelectModelAdapter;
|
|
||||||
import org.mybatis.dynamic.sql.select.QueryExpressionDSL;
|
|
||||||
import org.springframework.context.ApplicationEventPublisher;
|
import org.springframework.context.ApplicationEventPublisher;
|
||||||
import org.springframework.context.annotation.Lazy;
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
@ -52,18 +49,13 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
import ch.ethz.seb.sebserver.gbl.util.Utils;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordDynamicSqlSupport;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordDynamicSqlSupport;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordMapper;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.AdditionalAttributeRecordMapper;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper;
|
|
||||||
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.ExamRecordMapper;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordMapper;
|
||||||
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.model.AdditionalAttributeRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.AdditionalAttributeRecord;
|
||||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord;
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamRecord;
|
||||||
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.DuplicateResourceException;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPIService;
|
||||||
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
import ch.ethz.seb.sebserver.webservice.servicelayer.lms.LmsAPITemplate;
|
||||||
|
@ -77,22 +69,22 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
public static final String FAILED_TO_LOAD_QUIZ_DATA_MARK = "[FAILED TO LOAD DATA FROM LMS]";
|
public static final String FAILED_TO_LOAD_QUIZ_DATA_MARK = "[FAILED TO LOAD DATA FROM LMS]";
|
||||||
|
|
||||||
private final ExamRecordMapper examRecordMapper;
|
private final ExamRecordMapper examRecordMapper;
|
||||||
private final ClientConnectionRecordMapper clientConnectionRecordMapper;
|
private final ExamRecordDAO examRecordDAO;
|
||||||
private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper;
|
|
||||||
private final ApplicationEventPublisher applicationEventPublisher;
|
private final ApplicationEventPublisher applicationEventPublisher;
|
||||||
|
private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper;
|
||||||
private final LmsAPIService lmsAPIService;
|
private final LmsAPIService lmsAPIService;
|
||||||
|
|
||||||
public ExamDAOImpl(
|
public ExamDAOImpl(
|
||||||
final ExamRecordMapper examRecordMapper,
|
final ExamRecordMapper examRecordMapper,
|
||||||
final ClientConnectionRecordMapper clientConnectionRecordMapper,
|
final ExamRecordDAO examRecordDAO,
|
||||||
final AdditionalAttributeRecordMapper additionalAttributeRecordMapper,
|
|
||||||
final ApplicationEventPublisher applicationEventPublisher,
|
final ApplicationEventPublisher applicationEventPublisher,
|
||||||
|
final AdditionalAttributeRecordMapper additionalAttributeRecordMapper,
|
||||||
final LmsAPIService lmsAPIService) {
|
final LmsAPIService lmsAPIService) {
|
||||||
|
|
||||||
this.examRecordMapper = examRecordMapper;
|
this.examRecordMapper = examRecordMapper;
|
||||||
this.clientConnectionRecordMapper = clientConnectionRecordMapper;
|
this.examRecordDAO = examRecordDAO;
|
||||||
this.additionalAttributeRecordMapper = additionalAttributeRecordMapper;
|
|
||||||
this.applicationEventPublisher = applicationEventPublisher;
|
this.applicationEventPublisher = applicationEventPublisher;
|
||||||
|
this.additionalAttributeRecordMapper = additionalAttributeRecordMapper;
|
||||||
this.lmsAPIService = lmsAPIService;
|
this.lmsAPIService = lmsAPIService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,63 +94,35 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<Exam> byPK(final Long id) {
|
public Result<Exam> byPK(final Long id) {
|
||||||
return recordById(id)
|
return this.examRecordDAO
|
||||||
|
.recordById(id)
|
||||||
.flatMap(this::toDomainModel);
|
.flatMap(this::toDomainModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<GrantEntity> examGrantEntityByPK(final Long id) {
|
public Result<GrantEntity> examGrantEntityByPK(final Long id) {
|
||||||
return recordById(id)
|
return this.examRecordDAO.recordById(id)
|
||||||
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<GrantEntity> examGrantEntityByClientConnection(final Long connectionId) {
|
public Result<GrantEntity> examGrantEntityByClientConnection(final Long connectionId) {
|
||||||
return Result.tryCatch(() -> this.clientConnectionRecordMapper
|
return this.examRecordDAO
|
||||||
.selectByPrimaryKey(connectionId))
|
.recordByClientConnection(connectionId)
|
||||||
.flatMap(ccRecord -> recordById(ccRecord.getExamId()))
|
|
||||||
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
.map(record -> toDomainModel(record, null, null).getOrThrow());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<Collection<Exam>> all(final Long institutionId, final Boolean active) {
|
public Result<Collection<Exam>> all(final Long institutionId, final Boolean active) {
|
||||||
return Result.tryCatch(() -> (active != null)
|
return this.examRecordDAO
|
||||||
? this.examRecordMapper.selectByExample()
|
.all(institutionId, active)
|
||||||
.where(
|
|
||||||
ExamRecordDynamicSqlSupport.institutionId,
|
|
||||||
isEqualToWhenPresent(institutionId))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualToWhenPresent(BooleanUtils.toIntegerObject(active)))
|
|
||||||
.build()
|
|
||||||
.execute()
|
|
||||||
: this.examRecordMapper.selectByExample()
|
|
||||||
.build()
|
|
||||||
.execute())
|
|
||||||
.flatMap(this::toDomainModel);
|
.flatMap(this::toDomainModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Collection<Long>> allInstitutionIdsByQuizId(final String quizId) {
|
public Result<Collection<Long>> allInstitutionIdsByQuizId(final String quizId) {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO.allInstitutionIdsByQuizId(quizId);
|
||||||
return this.examRecordMapper.selectByExample()
|
|
||||||
.where(
|
|
||||||
ExamRecordDynamicSqlSupport.externalId,
|
|
||||||
isEqualToWhenPresent(quizId))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualToWhenPresent(BooleanUtils.toIntegerObject(true)))
|
|
||||||
.build()
|
|
||||||
.execute()
|
|
||||||
.stream()
|
|
||||||
.map(rec -> rec.getInstitutionId())
|
|
||||||
.collect(Collectors.toList());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -189,51 +153,9 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// If we have a sort on institution name, join the institution table
|
return this.examRecordDAO
|
||||||
// If we have a sort on lms setup name, join lms setup table
|
.allMatching(filterMap)
|
||||||
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<ExamRecord>>>.QueryExpressionWhereBuilder whereClause =
|
.flatMap(this::toDomainModel)
|
||||||
(filterMap.getBoolean(FilterMap.ATTR_ADD_INSITUTION_JOIN))
|
|
||||||
? this.examRecordMapper
|
|
||||||
.selectByExample()
|
|
||||||
.join(InstitutionRecordDynamicSqlSupport.institutionRecord)
|
|
||||||
.on(
|
|
||||||
InstitutionRecordDynamicSqlSupport.id,
|
|
||||||
SqlBuilder.equalTo(ExamRecordDynamicSqlSupport.institutionId))
|
|
||||||
.where(
|
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
|
||||||
: (filterMap.getBoolean(FilterMap.ATTR_ADD_LMS_SETUP_JOIN))
|
|
||||||
? this.examRecordMapper
|
|
||||||
.selectByExample()
|
|
||||||
.join(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord)
|
|
||||||
.on(
|
|
||||||
LmsSetupRecordDynamicSqlSupport.id,
|
|
||||||
SqlBuilder.equalTo(ExamRecordDynamicSqlSupport.lmsSetupId))
|
|
||||||
.where(
|
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
|
||||||
: this.examRecordMapper.selectByExample()
|
|
||||||
.where(
|
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualToWhenPresent(filterMap.getActiveAsInt()));
|
|
||||||
|
|
||||||
final List<ExamRecord> records = whereClause
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.institutionId,
|
|
||||||
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.lmsSetupId,
|
|
||||||
isEqualToWhenPresent(filterMap.getLmsSetupId()))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.type,
|
|
||||||
isEqualToWhenPresent(filterMap.getExamType()))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.status,
|
|
||||||
isEqualToWhenPresent(filterMap.getExamStatus()))
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
return this.toDomainModel(records)
|
|
||||||
.getOrThrow()
|
.getOrThrow()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(quizDataFilter.and(predicate))
|
.filter(quizDataFilter.and(predicate))
|
||||||
|
@ -243,128 +165,32 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Result<Exam> updateState(final Long examId, final ExamStatus status, final String updateId) {
|
public Result<Exam> updateState(final Long examId, final ExamStatus status, final String updateId) {
|
||||||
return recordById(examId)
|
return this.examRecordDAO
|
||||||
.map(examRecord -> {
|
.updateState(examId, status, updateId)
|
||||||
if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(examRecord.getUpdating()))) {
|
.flatMap(this::toDomainModel);
|
||||||
if (!updateId.equals(examRecord.getLastupdate())) {
|
|
||||||
throw new IllegalStateException("Exam is currently locked: " + examRecord.getExternalId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ExamRecord newExamRecord = new ExamRecord(
|
|
||||||
examRecord.getId(),
|
|
||||||
null, null, null, null, null, null, null, null,
|
|
||||||
status.name(),
|
|
||||||
null, null, null, null);
|
|
||||||
|
|
||||||
this.examRecordMapper.updateByPrimaryKeySelective(newExamRecord);
|
|
||||||
return this.examRecordMapper.selectByPrimaryKey(examId);
|
|
||||||
})
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
|
||||||
public Result<Exam> save(final Exam exam) {
|
public Result<Exam> save(final Exam exam) {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO
|
||||||
|
.save(exam)
|
||||||
// check internal persistent write-lock
|
.flatMap(this::toDomainModel);
|
||||||
final ExamRecord oldRecord = this.examRecordMapper.selectByPrimaryKey(exam.id);
|
|
||||||
if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(oldRecord.getUpdating()))) {
|
|
||||||
throw new IllegalStateException("Exam is currently locked: " + exam.externalId);
|
|
||||||
}
|
|
||||||
|
|
||||||
final ExamRecord examRecord = new ExamRecord(
|
|
||||||
exam.id,
|
|
||||||
null, null, null, null,
|
|
||||||
(exam.supporter != null)
|
|
||||||
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
|
|
||||||
: null,
|
|
||||||
(exam.type != null)
|
|
||||||
? exam.type.name()
|
|
||||||
: null,
|
|
||||||
null,
|
|
||||||
exam.browserExamKeys,
|
|
||||||
(exam.status != null)
|
|
||||||
? exam.status.name()
|
|
||||||
: null,
|
|
||||||
1, // seb restriction (deprecated)
|
|
||||||
null, // updating
|
|
||||||
null, // lastUpdate
|
|
||||||
null // active
|
|
||||||
);
|
|
||||||
|
|
||||||
this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
|
|
||||||
return this.examRecordMapper.selectByPrimaryKey(exam.id);
|
|
||||||
})
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<Exam> setSEBRestriction(final Long examId, final boolean sebRestriction) {
|
public Result<Exam> setSEBRestriction(final Long examId, final boolean sebRestriction) {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO
|
||||||
|
.setSEBRestriction(examId, sebRestriction)
|
||||||
final ExamRecord examRecord = new ExamRecord(
|
.flatMap(this::toDomainModel);
|
||||||
examId,
|
|
||||||
null, null, null, null, null, null, null, null, null,
|
|
||||||
BooleanUtils.toInteger(sebRestriction),
|
|
||||||
null, null, null);
|
|
||||||
|
|
||||||
this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
|
|
||||||
return this.examRecordMapper.selectByPrimaryKey(examId);
|
|
||||||
})
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public Result<Exam> createNew(final Exam exam) {
|
public Result<Exam> createNew(final Exam exam) {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO
|
||||||
|
.createNew(exam)
|
||||||
// fist check if it is not already existing
|
.flatMap(this::toDomainModel);
|
||||||
final List<ExamRecord> records = this.examRecordMapper.selectByExample()
|
|
||||||
.where(ExamRecordDynamicSqlSupport.lmsSetupId, isEqualTo(exam.lmsSetupId))
|
|
||||||
.and(ExamRecordDynamicSqlSupport.externalId, isEqualTo(exam.externalId))
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
// if there is already an existing imported exam for the quiz, this is
|
|
||||||
// used to save instead of create a new one
|
|
||||||
if (records != null && records.size() > 0) {
|
|
||||||
final ExamRecord examRecord = records.get(0);
|
|
||||||
// if the same institution tries to import an exam that already exists throw an error
|
|
||||||
if (exam.institutionId.equals(examRecord.getInstitutionId())) {
|
|
||||||
throw new DuplicateResourceException(EntityType.EXAM, exam.externalId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final ExamRecord examRecord = new ExamRecord(
|
|
||||||
null,
|
|
||||||
exam.institutionId,
|
|
||||||
exam.lmsSetupId,
|
|
||||||
exam.externalId,
|
|
||||||
exam.owner,
|
|
||||||
(exam.supporter != null)
|
|
||||||
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
|
|
||||||
: null,
|
|
||||||
(exam.type != null) ? exam.type.name() : ExamType.UNDEFINED.name(),
|
|
||||||
null, // quitPassword
|
|
||||||
null, // browser keys
|
|
||||||
(exam.status != null) ? exam.status.name() : ExamStatus.UP_COMING.name(),
|
|
||||||
1, // seb restriction (deprecated)
|
|
||||||
BooleanUtils.toInteger(false),
|
|
||||||
null, // lastUpdate
|
|
||||||
BooleanUtils.toInteger(true));
|
|
||||||
|
|
||||||
this.examRecordMapper.insert(examRecord);
|
|
||||||
return examRecord;
|
|
||||||
})
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -442,57 +268,27 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
|
||||||
public Result<Collection<Exam>> allForRunCheck() {
|
public Result<Collection<Exam>> allForRunCheck() {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO
|
||||||
final List<ExamRecord> records = this.examRecordMapper.selectByExample()
|
.allForRunCheck()
|
||||||
.where(
|
.flatMap(this::toDomainModel);
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualTo(BooleanUtils.toInteger(true)))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.status,
|
|
||||||
isNotEqualTo(ExamStatus.RUNNING.name()))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.updating,
|
|
||||||
isEqualTo(BooleanUtils.toInteger(false)))
|
|
||||||
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
return new ArrayList<>(this.toDomainModel(records)
|
|
||||||
.getOrThrow());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Collection<Exam>> allForEndCheck() {
|
public Result<Collection<Exam>> allForEndCheck() {
|
||||||
return Result.tryCatch(() -> {
|
return this.examRecordDAO
|
||||||
final List<ExamRecord> records = this.examRecordMapper.selectByExample()
|
.allForEndCheck()
|
||||||
.where(
|
.flatMap(this::toDomainModel);
|
||||||
ExamRecordDynamicSqlSupport.active,
|
|
||||||
isEqualTo(BooleanUtils.toInteger(true)))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.status,
|
|
||||||
isEqualTo(ExamStatus.RUNNING.name()))
|
|
||||||
.and(
|
|
||||||
ExamRecordDynamicSqlSupport.updating,
|
|
||||||
isEqualTo(BooleanUtils.toInteger(false)))
|
|
||||||
|
|
||||||
.build()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
return new ArrayList<>(this.toDomainModel(records)
|
|
||||||
.getOrThrow());
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
public Result<Exam> placeLock(final Long examId, final String updateId) {
|
public Result<Long> placeLock(final Long examId, final String updateId) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
final ExamRecord examRec = this.recordById(examId)
|
final ExamRecord examRec = this.examRecordDAO
|
||||||
|
.recordById(examId)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
// consistency check
|
// consistency check
|
||||||
|
@ -509,19 +305,18 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
null);
|
null);
|
||||||
|
|
||||||
this.examRecordMapper.updateByPrimaryKeySelective(newRecord);
|
this.examRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
return newRecord;
|
return examId;
|
||||||
})
|
})
|
||||||
.flatMap(rec -> this.recordById(rec.getId()))
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
.onError(TransactionHandler::rollback);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
@Transactional(propagation = Propagation.REQUIRES_NEW)
|
||||||
public Result<Exam> releaseLock(final Long examId, final String updateId) {
|
public Result<Long> releaseLock(final Long examId, final String updateId) {
|
||||||
return Result.tryCatch(() -> {
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
final ExamRecord examRec = this.recordById(examId)
|
final ExamRecord examRec = this.examRecordDAO
|
||||||
|
.recordById(examId)
|
||||||
.getOrThrow();
|
.getOrThrow();
|
||||||
|
|
||||||
// consistency check
|
// consistency check
|
||||||
|
@ -540,10 +335,8 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
null);
|
null);
|
||||||
|
|
||||||
this.examRecordMapper.updateByPrimaryKeySelective(newRecord);
|
this.examRecordMapper.updateByPrimaryKeySelective(newRecord);
|
||||||
return newRecord;
|
return examId;
|
||||||
})
|
})
|
||||||
.flatMap(rec -> this.recordById(rec.getId()))
|
|
||||||
.flatMap(this::toDomainModel)
|
|
||||||
.onError(TransactionHandler::rollback);
|
.onError(TransactionHandler::rollback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,7 +359,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
return examRecord.getId();
|
return examRecord.getId();
|
||||||
})
|
})
|
||||||
.onError(TransactionHandler::rollback);
|
.onError(TransactionHandler::rollback);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -597,7 +389,8 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Boolean> isLocked(final Long examId) {
|
public Result<Boolean> isLocked(final Long examId) {
|
||||||
return this.recordById(examId)
|
return this.examRecordDAO
|
||||||
|
.recordById(examId)
|
||||||
.map(rec -> BooleanUtils.toBooleanObject(rec.getUpdating()));
|
.map(rec -> BooleanUtils.toBooleanObject(rec.getUpdating()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -637,7 +430,8 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Boolean> upToDate(final Long examId, final String updateId) {
|
public Result<Boolean> upToDate(final Long examId, final String updateId) {
|
||||||
return this.recordById(examId)
|
return this.examRecordDAO
|
||||||
|
.recordById(examId)
|
||||||
.map(rec -> {
|
.map(rec -> {
|
||||||
if (updateId == null) {
|
if (updateId == null) {
|
||||||
return rec.getLastupdate() == null;
|
return rec.getLastupdate() == null;
|
||||||
|
@ -707,10 +501,9 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
@Override
|
@Override
|
||||||
@Transactional(readOnly = true)
|
@Transactional(readOnly = true)
|
||||||
public Result<Collection<Exam>> allOf(final Set<Long> pks) {
|
public Result<Collection<Exam>> allOf(final Set<Long> pks) {
|
||||||
return Result.tryCatch(() -> this.examRecordMapper.selectByExample()
|
return this.examRecordDAO
|
||||||
.where(ExamRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
|
.allOf(pks)
|
||||||
.build()
|
.flatMap(this::toDomainModel);
|
||||||
.execute()).flatMap(this::toDomainModel);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -757,18 +550,6 @@ public class ExamDAOImpl implements ExamDAO {
|
||||||
userKey));
|
userKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Result<ExamRecord> recordById(final Long id) {
|
|
||||||
return Result.tryCatch(() -> {
|
|
||||||
final ExamRecord record = this.examRecordMapper.selectByPrimaryKey(id);
|
|
||||||
if (record == null) {
|
|
||||||
throw new ResourceNotFoundException(
|
|
||||||
EntityType.EXAM,
|
|
||||||
String.valueOf(id));
|
|
||||||
}
|
|
||||||
return record;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private Collection<EntityDependency> toDependencies(
|
private Collection<EntityDependency> toDependencies(
|
||||||
final List<ExamRecord> records,
|
final List<ExamRecord> records,
|
||||||
final EntityKey parent) {
|
final EntityKey parent) {
|
||||||
|
|
|
@ -0,0 +1,333 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||||
|
*
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package ch.ethz.seb.sebserver.webservice.servicelayer.dao.impl;
|
||||||
|
|
||||||
|
import static org.mybatis.dynamic.sql.SqlBuilder.*;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import org.apache.commons.lang3.BooleanUtils;
|
||||||
|
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.stereotype.Component;
|
||||||
|
import org.springframework.transaction.annotation.Transactional;
|
||||||
|
|
||||||
|
import ch.ethz.seb.sebserver.gbl.Constants;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.api.EntityType;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
|
||||||
|
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.profile.WebServiceProfile;
|
||||||
|
import ch.ethz.seb.sebserver.gbl.util.Result;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ClientConnectionRecordMapper;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordDynamicSqlSupport;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.ExamRecordMapper;
|
||||||
|
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.model.ExamRecord;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DuplicateResourceException;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
|
||||||
|
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
||||||
|
|
||||||
|
@Lazy
|
||||||
|
@Component
|
||||||
|
@WebServiceProfile
|
||||||
|
public class ExamRecordDAO {
|
||||||
|
|
||||||
|
private final ExamRecordMapper examRecordMapper;
|
||||||
|
private final ClientConnectionRecordMapper clientConnectionRecordMapper;
|
||||||
|
|
||||||
|
public ExamRecordDAO(
|
||||||
|
final ExamRecordMapper examRecordMapper,
|
||||||
|
final ClientConnectionRecordMapper clientConnectionRecordMapper) {
|
||||||
|
|
||||||
|
this.examRecordMapper = examRecordMapper;
|
||||||
|
this.clientConnectionRecordMapper = clientConnectionRecordMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<ExamRecord> recordById(final Long id) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
final ExamRecord record = this.examRecordMapper.selectByPrimaryKey(id);
|
||||||
|
if (record == null) {
|
||||||
|
throw new ResourceNotFoundException(
|
||||||
|
EntityType.EXAM,
|
||||||
|
String.valueOf(id));
|
||||||
|
}
|
||||||
|
return record;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<ExamRecord> recordByClientConnection(final Long connectionId) {
|
||||||
|
return Result.tryCatch(() -> this.clientConnectionRecordMapper
|
||||||
|
.selectByPrimaryKey(connectionId))
|
||||||
|
.flatMap(ccRecord -> recordById(ccRecord.getExamId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<ExamRecord>> all(final Long institutionId, final Boolean active) {
|
||||||
|
return Result.tryCatch(() -> (active != null)
|
||||||
|
? this.examRecordMapper.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.institutionId,
|
||||||
|
isEqualToWhenPresent(institutionId))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualToWhenPresent(BooleanUtils.toIntegerObject(active)))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
: this.examRecordMapper.selectByExample()
|
||||||
|
.build()
|
||||||
|
.execute());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<Long>> allInstitutionIdsByQuizId(final String quizId) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
return this.examRecordMapper.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.externalId,
|
||||||
|
isEqualToWhenPresent(quizId))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualToWhenPresent(BooleanUtils.toIntegerObject(true)))
|
||||||
|
.build()
|
||||||
|
.execute()
|
||||||
|
.stream()
|
||||||
|
.map(rec -> rec.getInstitutionId())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<ExamRecord>> allMatching(final FilterMap filterMap) {
|
||||||
|
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
// If we have a sort on institution name, join the institution table
|
||||||
|
// If we have a sort on lms setup name, join lms setup table
|
||||||
|
final QueryExpressionDSL<MyBatis3SelectModelAdapter<List<ExamRecord>>>.QueryExpressionWhereBuilder whereClause =
|
||||||
|
(filterMap.getBoolean(FilterMap.ATTR_ADD_INSITUTION_JOIN))
|
||||||
|
? this.examRecordMapper
|
||||||
|
.selectByExample()
|
||||||
|
.join(InstitutionRecordDynamicSqlSupport.institutionRecord)
|
||||||
|
.on(
|
||||||
|
InstitutionRecordDynamicSqlSupport.id,
|
||||||
|
SqlBuilder.equalTo(ExamRecordDynamicSqlSupport.institutionId))
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
||||||
|
: (filterMap.getBoolean(FilterMap.ATTR_ADD_LMS_SETUP_JOIN))
|
||||||
|
? this.examRecordMapper
|
||||||
|
.selectByExample()
|
||||||
|
.join(LmsSetupRecordDynamicSqlSupport.lmsSetupRecord)
|
||||||
|
.on(
|
||||||
|
LmsSetupRecordDynamicSqlSupport.id,
|
||||||
|
SqlBuilder.equalTo(ExamRecordDynamicSqlSupport.lmsSetupId))
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualToWhenPresent(filterMap.getActiveAsInt()))
|
||||||
|
: this.examRecordMapper.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualToWhenPresent(filterMap.getActiveAsInt()));
|
||||||
|
|
||||||
|
final List<ExamRecord> records = whereClause
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.institutionId,
|
||||||
|
isEqualToWhenPresent(filterMap.getInstitutionId()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.lmsSetupId,
|
||||||
|
isEqualToWhenPresent(filterMap.getLmsSetupId()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.type,
|
||||||
|
isEqualToWhenPresent(filterMap.getExamType()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.status,
|
||||||
|
isEqualToWhenPresent(filterMap.getExamStatus()))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return records;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Result<ExamRecord> updateState(final Long examId, final ExamStatus status, final String updateId) {
|
||||||
|
return recordById(examId)
|
||||||
|
.map(examRecord -> {
|
||||||
|
if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(examRecord.getUpdating()))) {
|
||||||
|
if (!updateId.equals(examRecord.getLastupdate())) {
|
||||||
|
throw new IllegalStateException("Exam is currently locked: " + examRecord.getExternalId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ExamRecord newExamRecord = new ExamRecord(
|
||||||
|
examRecord.getId(),
|
||||||
|
null, null, null, null, null, null, null, null,
|
||||||
|
status.name(),
|
||||||
|
null, null, null, null);
|
||||||
|
|
||||||
|
this.examRecordMapper.updateByPrimaryKeySelective(newExamRecord);
|
||||||
|
return this.examRecordMapper.selectByPrimaryKey(examId);
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Result<ExamRecord> save(final Exam exam) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
// check internal persistent write-lock
|
||||||
|
final ExamRecord oldRecord = this.examRecordMapper.selectByPrimaryKey(exam.id);
|
||||||
|
if (BooleanUtils.isTrue(BooleanUtils.toBooleanObject(oldRecord.getUpdating()))) {
|
||||||
|
throw new IllegalStateException("Exam is currently locked: " + exam.externalId);
|
||||||
|
}
|
||||||
|
|
||||||
|
final ExamRecord examRecord = new ExamRecord(
|
||||||
|
exam.id,
|
||||||
|
null, null, null, null,
|
||||||
|
(exam.supporter != null)
|
||||||
|
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
|
||||||
|
: null,
|
||||||
|
(exam.type != null)
|
||||||
|
? exam.type.name()
|
||||||
|
: null,
|
||||||
|
null,
|
||||||
|
exam.browserExamKeys,
|
||||||
|
(exam.status != null)
|
||||||
|
? exam.status.name()
|
||||||
|
: null,
|
||||||
|
1, // seb restriction (deprecated)
|
||||||
|
null, // updating
|
||||||
|
null, // lastUpdate
|
||||||
|
null // active
|
||||||
|
);
|
||||||
|
|
||||||
|
this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
|
||||||
|
return this.examRecordMapper.selectByPrimaryKey(exam.id);
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Result<ExamRecord> setSEBRestriction(final Long examId, final boolean sebRestriction) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
final ExamRecord examRecord = new ExamRecord(
|
||||||
|
examId,
|
||||||
|
null, null, null, null, null, null, null, null, null,
|
||||||
|
BooleanUtils.toInteger(sebRestriction),
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
this.examRecordMapper.updateByPrimaryKeySelective(examRecord);
|
||||||
|
return this.examRecordMapper.selectByPrimaryKey(examId);
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional
|
||||||
|
public Result<ExamRecord> createNew(final Exam exam) {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
|
||||||
|
// fist check if it is not already existing
|
||||||
|
final List<ExamRecord> records = this.examRecordMapper.selectByExample()
|
||||||
|
.where(ExamRecordDynamicSqlSupport.lmsSetupId, isEqualTo(exam.lmsSetupId))
|
||||||
|
.and(ExamRecordDynamicSqlSupport.externalId, isEqualTo(exam.externalId))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
// if there is already an existing imported exam for the quiz, this is
|
||||||
|
// used to save instead of create a new one
|
||||||
|
if (records != null && records.size() > 0) {
|
||||||
|
final ExamRecord examRecord = records.get(0);
|
||||||
|
// if the same institution tries to import an exam that already exists throw an error
|
||||||
|
if (exam.institutionId.equals(examRecord.getInstitutionId())) {
|
||||||
|
throw new DuplicateResourceException(EntityType.EXAM, exam.externalId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final ExamRecord examRecord = new ExamRecord(
|
||||||
|
null,
|
||||||
|
exam.institutionId,
|
||||||
|
exam.lmsSetupId,
|
||||||
|
exam.externalId,
|
||||||
|
exam.owner,
|
||||||
|
(exam.supporter != null)
|
||||||
|
? StringUtils.join(exam.supporter, Constants.LIST_SEPARATOR_CHAR)
|
||||||
|
: null,
|
||||||
|
(exam.type != null) ? exam.type.name() : ExamType.UNDEFINED.name(),
|
||||||
|
null, // quitPassword
|
||||||
|
null, // browser keys
|
||||||
|
(exam.status != null) ? exam.status.name() : ExamStatus.UP_COMING.name(),
|
||||||
|
1, // seb restriction (deprecated)
|
||||||
|
BooleanUtils.toInteger(false),
|
||||||
|
null, // lastUpdate
|
||||||
|
BooleanUtils.toInteger(true));
|
||||||
|
|
||||||
|
this.examRecordMapper.insert(examRecord);
|
||||||
|
return examRecord;
|
||||||
|
})
|
||||||
|
.onError(TransactionHandler::rollback);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<ExamRecord>> allForRunCheck() {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
return this.examRecordMapper.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualTo(BooleanUtils.toInteger(true)))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.status,
|
||||||
|
isNotEqualTo(ExamStatus.RUNNING.name()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.updating,
|
||||||
|
isEqualTo(BooleanUtils.toInteger(false)))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<ExamRecord>> allForEndCheck() {
|
||||||
|
return Result.tryCatch(() -> {
|
||||||
|
return this.examRecordMapper.selectByExample()
|
||||||
|
.where(
|
||||||
|
ExamRecordDynamicSqlSupport.active,
|
||||||
|
isEqualTo(BooleanUtils.toInteger(true)))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.status,
|
||||||
|
isEqualTo(ExamStatus.RUNNING.name()))
|
||||||
|
.and(
|
||||||
|
ExamRecordDynamicSqlSupport.updating,
|
||||||
|
isEqualTo(BooleanUtils.toInteger(false)))
|
||||||
|
.build()
|
||||||
|
.execute();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Transactional(readOnly = true)
|
||||||
|
public Result<Collection<ExamRecord>> allOf(final Set<Long> pks) {
|
||||||
|
return Result.tryCatch(() -> this.examRecordMapper.selectByExample()
|
||||||
|
.where(ExamRecordDynamicSqlSupport.id, isIn(new ArrayList<>(pks)))
|
||||||
|
.build()
|
||||||
|
.execute());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -315,7 +315,9 @@ public class ExamConfigUpdateServiceImpl implements ExamConfigUpdateService {
|
||||||
|
|
||||||
private Collection<Result<Exam>> lockForUpdate(final Collection<Long> examIds, final String update) {
|
private Collection<Result<Exam>> lockForUpdate(final Collection<Long> examIds, final String update) {
|
||||||
return examIds.stream()
|
return examIds.stream()
|
||||||
.map(id -> this.examDAO.placeLock(id, update))
|
.map(id -> this.examDAO.byPK(id)
|
||||||
|
.map(exam -> this.examDAO.placeLock(exam, update))
|
||||||
|
.getOrThrow())
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,7 +84,7 @@ class ExamUpdateHandler {
|
||||||
ExamStatus.RUNNING,
|
ExamStatus.RUNNING,
|
||||||
updateId))
|
updateId))
|
||||||
.flatMap(this.sebRestrictionService::applySEBClientRestriction)
|
.flatMap(this.sebRestrictionService::applySEBClientRestriction)
|
||||||
.flatMap(e -> this.examDAO.releaseLock(e.id, updateId))
|
.flatMap(e -> this.examDAO.releaseLock(e, updateId))
|
||||||
.onError(error -> this.examDAO.forceUnlock(exam.id)
|
.onError(error -> this.examDAO.forceUnlock(exam.id)
|
||||||
.onError(unlockError -> log.error("Failed to force unlock update look for exam: {}", exam.id)));
|
.onError(unlockError -> log.error("Failed to force unlock update look for exam: {}", exam.id)));
|
||||||
}
|
}
|
||||||
|
@ -101,7 +101,7 @@ class ExamUpdateHandler {
|
||||||
ExamStatus.FINISHED,
|
ExamStatus.FINISHED,
|
||||||
updateId))
|
updateId))
|
||||||
.flatMap(this.sebRestrictionService::releaseSEBClientRestriction)
|
.flatMap(this.sebRestrictionService::releaseSEBClientRestriction)
|
||||||
.flatMap(e -> this.examDAO.releaseLock(e.id, updateId))
|
.flatMap(e -> this.examDAO.releaseLock(e, updateId))
|
||||||
.onError(error -> this.examDAO.forceUnlock(exam.id));
|
.onError(error -> this.examDAO.forceUnlock(exam.id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue