SEBSERV-162 implementations and test

This commit is contained in:
anhefti 2021-09-09 17:16:56 +02:00
parent ef3a633ce1
commit f794ab5e7d
11 changed files with 245 additions and 13 deletions

View file

@ -232,11 +232,15 @@ public class POSTMapper {
return Collections.emptyList();
}
return thresholdStrings.stream()
return thresholdStrings
.stream()
.map(ts -> {
try {
final String[] split = StringUtils.split(ts, Constants.EMBEDDED_LIST_SEPARATOR);
return new Threshold(Double.parseDouble(split[0]), split[1], null);
return new Threshold(Double.parseDouble(
split[0]),
(split.length > 1) ? split[1] : null,
(split.length > 2) ? split[2] : null);
} catch (final Exception e) {
return null;
}

View file

@ -61,6 +61,8 @@ public class ExamTemplateForm implements TemplateComposer {
new LocTextKey("sebserver.examtemplate.form.name");
private static final LocTextKey FORM_DESCRIPTION_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.form.description");
private static final LocTextKey FORM_DEFAULT_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.form.default");
private static final LocTextKey FORM_TYPE_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.form.examType");
private static final LocTextKey FORM_CONFIG_TEMPLATE_TEXT_KEY =
@ -149,6 +151,11 @@ public class ExamTemplateForm implements TemplateComposer {
examTemplate.description)
.asArea())
.addField(FormBuilder.checkbox(
Domain.EXAM_TEMPLATE.ATTR_INSTITUTIONAL_DEFAULT,
FORM_DEFAULT_TEXT_KEY,
String.valueOf(examTemplate.getInstitutionalDefault())))
.addField(FormBuilder.singleSelection(
Domain.EXAM_TEMPLATE.ATTR_EXAM_TYPE,
FORM_TYPE_TEXT_KEY,
@ -159,11 +166,10 @@ public class ExamTemplateForm implements TemplateComposer {
.addFieldIf(
() -> !examConfigTemplateResources.isEmpty(),
() -> FormBuilder.singleSelection(
Domain.CONFIGURATION_NODE.ATTR_TEMPLATE_ID,
Domain.EXAM_TEMPLATE.ATTR_CONFIGURATION_TEMPLATE_ID,
FORM_CONFIG_TEMPLATE_TEXT_KEY,
String.valueOf(examTemplate.configTemplateId),
this.resourceService::getExamConfigTemplateResources)
.readonly(!isNew))
this.resourceService::getExamConfigTemplateResources))
.addField(FormBuilder.multiComboSelection(
Domain.EXAM_TEMPLATE.ATTR_SUPPORTER,

View file

@ -24,6 +24,7 @@ import ch.ethz.seb.sebserver.gbl.model.user.UserRole;
import ch.ethz.seb.sebserver.gbl.profile.GuiProfile;
import ch.ethz.seb.sebserver.gui.content.action.ActionDefinition;
import ch.ethz.seb.sebserver.gui.service.ResourceService;
import ch.ethz.seb.sebserver.gui.service.i18n.I18nSupport;
import ch.ethz.seb.sebserver.gui.service.i18n.LocTextKey;
import ch.ethz.seb.sebserver.gui.service.page.PageContext;
import ch.ethz.seb.sebserver.gui.service.page.PageService;
@ -53,9 +54,16 @@ public class ExamTemplateList implements TemplateComposer {
new LocTextKey("sebserver.examtemplate.list.column.name");
public final static LocTextKey COLUMN_TITLE_EXAM_TYPE_KEY =
new LocTextKey("sebserver.examtemplate.list.column.examType");
public final static LocTextKey COLUMN_TITLE_DEFAULT_KEY =
new LocTextKey("sebserver.examtemplate.list.column.default");
public final static LocTextKey EMPTY_LIST_TEXT_KEY =
new LocTextKey("sebserver.examtemplate.list.empty");
public final static LocTextKey COLUMN_TITLE_DEFAULT_TRUE_KEY =
new LocTextKey("sebserver.examtemplate.list.column.default.true");
public final static LocTextKey COLUMN_TITLE_DEFAULT_FALSE_KEY =
new LocTextKey("sebserver.examtemplate.list.column.default.false");
public static final LocTextKey NO_MODIFY_PRIVILEGE_ON_OTHER_INSTITUTION =
new LocTextKey("sebserver.examtemplate.list.action.no.modify.privilege");
public final static LocTextKey EMPTY_SELECTION_TEXT_KEY =
@ -94,6 +102,7 @@ public class ExamTemplateList implements TemplateComposer {
final WidgetFactory widgetFactory = this.pageService.getWidgetFactory();
final CurrentUser currentUser = this.resourceService.getCurrentUser();
final RestService restService = this.resourceService.getRestService();
final I18nSupport i18nSupport = this.pageService.getI18nSupport();
// content page layout with title
final Composite content = widgetFactory.defaultPageLayout(
@ -142,6 +151,13 @@ public class ExamTemplateList implements TemplateComposer {
.withFilter(this.typeFilter)
.sortable())
.withColumn(new ColumnDefinition<ExamTemplate>(
Domain.EXAM_TEMPLATE.ATTR_INSTITUTIONAL_DEFAULT,
COLUMN_TITLE_DEFAULT_KEY,
et -> (et.institutionalDefault)
? i18nSupport.getText(COLUMN_TITLE_DEFAULT_TRUE_KEY)
: i18nSupport.getText(COLUMN_TITLE_DEFAULT_FALSE_KEY)))
.withDefaultAction(actionBuilder
.newAction(ActionDefinition.EXAM_TEMPLATE_VIEW_FROM_LIST)
.create())

View file

@ -135,7 +135,7 @@ public class IndicatorTemplateForm implements TemplateComposer {
Domain.EXAM.ATTR_INSTITUTION_ID,
String.valueOf(examTemplate.getInstitutionId()))
.putStaticValue(
Domain.INDICATOR.ATTR_EXAM_ID,
IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID,
parentEntityKey.getModelId())
.addField(FormBuilder.text(
Domain.EXAM_TEMPLATE.ATTR_NAME,

View file

@ -768,7 +768,6 @@ public class WidgetFactory {
final Image image = type.getImage(parent.getDisplay());
imageButton.setImage(image);
if (listener != null) {
imageButton.addListener(SWT.MouseDown, listener);
imageButton.addListener(SWT.Selection, listener);
}
setARIARole(imageButton, AriaRole.button);

View file

@ -12,6 +12,7 @@ import java.util.Collection;
import org.springframework.cache.annotation.CacheEvict;
import ch.ethz.seb.sebserver.gbl.model.EntityKey;
import ch.ethz.seb.sebserver.gbl.model.GrantEntity;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam;
import ch.ethz.seb.sebserver.gbl.model.exam.Exam.ExamStatus;
@ -145,4 +146,11 @@ public interface ExamDAO extends ActivatableEntityDAO<Exam, Exam>, BulkActionSup
* @return Result refer to the updated Exam or to an error if happened */
Result<Exam> setSEBRestriction(Long examId, boolean sebRestriction);
/** This deletes the exam template reference of all exams that has a given
* template reference.
*
* @param examTemplateId The exam template reference identifier
* @return Result refer to the collection of entity keys of all involved exams or to an error when happened */
Result<Collection<EntityKey>> deleteTemplateReferences(Long examTemplateId);
}

View file

@ -721,6 +721,53 @@ public class ExamDAOImpl implements ExamDAO {
.execute());
}
@Override
@Transactional
public Result<Collection<EntityKey>> deleteTemplateReferences(final Long examTemplateId) {
return Result.tryCatch(() -> {
final List<ExamRecord> records = this.examRecordMapper.selectByExample()
.where(
ExamRecordDynamicSqlSupport.examTemplateId,
isEqualTo(examTemplateId))
.build()
.execute();
if (records == null || records.isEmpty()) {
return Collections.emptyList();
}
final ArrayList<EntityKey> result = new ArrayList<>();
for (final ExamRecord rec : records) {
try {
this.examRecordMapper.updateByPrimaryKey(new ExamRecord(
rec.getId(),
rec.getInstitutionId(),
rec.getLmsSetupId(),
rec.getExternalId(),
rec.getOwner(),
rec.getSupporter(),
rec.getType(),
rec.getQuitPassword(),
rec.getBrowserKeys(),
rec.getStatus(),
rec.getLmsSebRestriction(),
rec.getUpdating(),
rec.getLastupdate(),
rec.getActive(),
null));
result.add(new EntityKey(rec.getId(), EntityType.EXAM));
} catch (final Exception e) {
log.error("Failed to delete template references for exam: {}", rec, e);
}
}
return result;
});
}
private Result<Collection<EntityDependency>> allIdsOfInstitution(final EntityKey institutionKey) {
return Result.tryCatch(() -> toDependencies(
this.examRecordMapper.selectByExample()

View file

@ -44,6 +44,7 @@ import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.IndicatorRecordDy
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.ExamTemplateRecord;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.DAOLoggingSupport;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ExamTemplateDAO;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.FilterMap;
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.ResourceNotFoundException;
@ -56,15 +57,18 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
private final ExamTemplateRecordMapper examTemplateRecordMapper;
private final AdditionalAttributesDAO additionalAttributesDAO;
private final ExamDAO examDAO;
private final JSONMapper jsonMapper;
public ExamTemplateDAOImpl(
final ExamTemplateRecordMapper examTemplateRecordMapper,
final AdditionalAttributesDAO additionalAttributesDAO,
final ExamDAO examDAO,
final JSONMapper jsonMapper) {
this.examTemplateRecordMapper = examTemplateRecordMapper;
this.additionalAttributesDAO = additionalAttributesDAO;
this.examDAO = examDAO;
this.jsonMapper = jsonMapper;
}
@ -195,8 +199,8 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
: null;
final ExamTemplateRecord newRecord = new ExamTemplateRecord(
data.id,
null,
data.institutionId,
data.configTemplateId,
data.name,
data.description,
@ -233,8 +237,21 @@ public class ExamTemplateDAOImpl implements ExamTemplateDAO {
public Result<Collection<EntityKey>> delete(final Set<EntityKey> all) {
return Result.tryCatch(() -> {
log.info("Delete exam templates: {}", all);
final List<Long> ids = extractListOfPKs(all);
ids.stream()
.forEach(id -> {
final Collection<EntityKey> deletedReferences = this.examDAO
.deleteTemplateReferences(id)
.getOrThrow();
if (deletedReferences != null && !deletedReferences.isEmpty()) {
log.info("Deleted template references for exams: {}", deletedReferences);
}
});
this.examTemplateRecordMapper.deleteByExample()
.where(ExamTemplateRecordDynamicSqlSupport.id, isIn(ids))
.build()

View file

@ -174,6 +174,9 @@ public class ExamTemplateController extends EntityController<ExamTemplate, ExamT
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(

View file

@ -1614,7 +1614,10 @@ sebserver.examtemplate.list.column.name=Name
sebserver.examtemplate.list.column.name.tooltip=The name of the exam template<br/><br/>Use the filter above to narrow down to a specific name<br/>{0}
sebserver.examtemplate.list.column.examType=Exam Type
sebserver.examtemplate.list.column.examType.tooltip=The exam type defined by the exam template<br/><br/>Use the filter above to select a specific exam type<br/>{0}
sebserver.examtemplate.list.column.default=Default Template
sebserver.examtemplate.list.column.default.tooltip=Indicates the current default exam template for the institution<br/>{0}
sebserver.examtemplate.list.column.default.true=Yes
sebserver.examtemplate.list.column.default.flase=No
sebserver.examtemplate.info.pleaseSelect=At first please select an Exam Template from the list
@ -1624,6 +1627,8 @@ sebserver.examtemplate.form.name=Name
sebserver.examtemplate.form.name.tooltip=The name of the exam template
sebserver.examtemplate.form.description=Description
sebserver.examtemplate.form.description.tooltip=The description of the exam template
sebserver.examtemplate.form.default=Institutional Default
sebserver.examtemplate.form.default.tooltip=Set this to mark this as the default exam template for the institution.<br/>Only one exam template at the time can be marked as default. Please be aware that an existing default exam template will be reset if this is set as default.
sebserver.examtemplate.form.examType=Exam Type
sebserver.examtemplate.form.examType.tooltip=The exam type group identifier for if the exam template
sebserver.examtemplate.form.examConfigTemplate=Configuration Template
@ -1635,7 +1640,7 @@ sebserver.examtemplate.form.action.save=Save
sebserver.examtemplate.form.action.edit=Edit Template Settings
sebserver.examtemplate.indicator.list.actions=
sebserver.examtemplate.indicator.list.title=Indicators
sebserver.examtemplate.indicator.list.title=Indicator Templates
sebserver.examtemplate.indicator.list.title.tooltip=A list of indicators that will automatically be created when importing a exam with this template
sebserver.examtemplate.indicator.list.column.type=Type
sebserver.examtemplate.indicator.list.column.type.tooltip=The type of indicator
@ -1646,10 +1651,11 @@ sebserver.examtemplate.indicator.list.column.thresholds.tooltip=The thresholds o
sebserver.examtemplate.indicator.list.empty=There is currently no indicator defined for this exam template. Please create a new one
sebserver.examtemplate.indicator.list.pleaseSelect=At first please select an indicator from the list
sebserver.examtemplate.indicator.action.save=Save Indicator Template
sebserver.examtemplate.indicator.list.actions=
sebserver.examtemplate.indicator.action.list.new=New Indicator
sebserver.examtemplate.indicator.action.list.modify=Edit Indicator
sebserver.examtemplate.indicator.action.list.delete=Delete Indicator
sebserver.examtemplate.indicator.action.list.new=Add Indicator Template
sebserver.examtemplate.indicator.action.list.modify=Edit Indicator Template
sebserver.examtemplate.indicator.action.list.delete=Delete Indicator Template

View file

@ -38,6 +38,8 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.ClassPathResource;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.StreamUtils;
import ch.ethz.seb.sebserver.gbl.Constants;
@ -59,9 +61,11 @@ 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.model.exam.ExamConfigurationMap;
import ch.ethz.seb.sebserver.gbl.model.exam.ExamTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.IndicatorType;
import ch.ethz.seb.sebserver.gbl.model.exam.Indicator.Threshold;
import ch.ethz.seb.sebserver.gbl.model.exam.IndicatorTemplate;
import ch.ethz.seb.sebserver.gbl.model.exam.QuizData;
import ch.ethz.seb.sebserver.gbl.model.institution.Institution;
import ch.ethz.seb.sebserver.gbl.model.institution.LmsSetup;
@ -105,13 +109,22 @@ import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfi
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamConfigMappingsPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamNames;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplatePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetExamTemplates;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorPage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.GetIndicatorTemplatePage;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamConfigMapping;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.NewIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExam;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamConfigMapping;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveExamTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicator;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.exam.SaveIndicatorTemplate;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.ActivateInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitution;
import ch.ethz.seb.sebserver.gui.service.remote.webservice.api.institution.GetInstitutionNames;
@ -2567,4 +2580,117 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest {
// TODO how to mockup an Open edX response
}
@Test
@Order(22)
// *************************************
// Use Case 22: Login as TestInstAdmin and create new Exam Template
// - login as TestInstAdmin : 987654321
// - check exam template list is empty
// - create new exam template with existing configuration template
// - check exam template list contains created exam template
// - add indicator templates to the exam template
public void testUsecase22_CreateExamTemplate() {
final RestServiceImpl restService = createRestServiceForUser(
"TestInstAdmin",
"987654321",
new GetExamTemplatePage(),
new GetExamTemplate(),
new GetExamTemplates(),
new NewExamTemplate(),
new NewExamConfig(),
new SaveExamTemplate(),
new GetIndicatorTemplatePage(),
new NewIndicatorTemplate(),
new SaveIndicatorTemplate(),
new GetIndicatorTemplate());
Page<ExamTemplate> examTemplatePage = restService
.getBuilder(GetExamTemplatePage.class)
.call()
.getOrThrow();
assertTrue(examTemplatePage.isEmpty());
// create new exam config template
final ConfigurationNode configTemplate = restService
.getBuilder(NewExamConfig.class)
.withFormParam(Domain.CONFIGURATION_NODE.ATTR_NAME, "templateTest")
.withFormParam(Domain.CONFIGURATION_NODE.ATTR_TYPE, ConfigurationType.TEMPLATE.name())
.call()
.getOrThrow();
assertNotNull(configTemplate);
assertEquals("templateTest", configTemplate.name);
// create exam template with config template reference
final ExamTemplate examTemplate = restService
.getBuilder(NewExamTemplate.class)
.withFormParam(Domain.EXAM_TEMPLATE.ATTR_NAME, "examTemplate")
.withFormParam(Domain.EXAM_TEMPLATE.ATTR_CONFIGURATION_TEMPLATE_ID, configTemplate.getModelId())
.withFormParam(Domain.EXAM_TEMPLATE.ATTR_INSTITUTIONAL_DEFAULT, "true")
.withFormParam(Domain.EXAM_TEMPLATE.ATTR_EXAM_TYPE, ExamType.MANAGED.name())
.call()
.getOrThrow();
assertNotNull(examTemplate);
assertEquals("examTemplate", examTemplate.name);
assertTrue(examTemplate.institutionalDefault);
assertEquals(configTemplate.institutionId, examTemplate.institutionId);
assertEquals(configTemplate.id, examTemplate.configTemplateId);
// get list again and check entry
examTemplatePage = restService
.getBuilder(GetExamTemplatePage.class)
.call()
.getOrThrow();
assertFalse(examTemplatePage.isEmpty());
final ExamTemplate templateFromList = examTemplatePage.getContent().iterator().next();
assertNotNull(templateFromList);
assertEquals("examTemplate", templateFromList.name);
assertTrue(templateFromList.institutionalDefault);
assertEquals(configTemplate.institutionId, templateFromList.institutionId);
assertEquals(configTemplate.id, templateFromList.configTemplateId);
// create new indicator template
final MultiValueMap<String, String> thresholds = new LinkedMultiValueMap<>();
thresholds.add(Domain.THRESHOLD.REFERENCE_NAME, "1|000001");
thresholds.add(Domain.THRESHOLD.REFERENCE_NAME, "2|000002");
thresholds.add(Domain.THRESHOLD.REFERENCE_NAME, "3|000003");
final IndicatorTemplate indicatorTemplate = restService
.getBuilder(NewIndicatorTemplate.class)
.withFormParam(IndicatorTemplate.ATTR_EXAM_TEMPLATE_ID, examTemplate.getModelId())
.withFormParam(Domain.INDICATOR.ATTR_NAME, "Errors")
.withFormParam(Domain.INDICATOR.ATTR_TYPE, IndicatorType.ERROR_COUNT.name)
.withFormParam(Domain.INDICATOR.ATTR_COLOR, "000001")
.withFormParams(thresholds)
.call()
.getOrThrow();
assertNotNull(indicatorTemplate);
assertEquals(examTemplate.id, indicatorTemplate.examTemplateId);
assertEquals("Errors", indicatorTemplate.name);
assertTrue(indicatorTemplate.thresholds.size() == 3);
// get indicator list for template
final Page<IndicatorTemplate> indicatorList = restService
.getBuilder(GetIndicatorTemplatePage.class)
.withURIVariable(API.PARAM_PARENT_MODEL_ID, examTemplate.getModelId())
.call()
.getOrThrow();
assertNotNull(indicatorList);
assertFalse(indicatorList.isEmpty());
assertTrue(indicatorList.content.size() == 1);
// TODO save exam template
// TODO edit indicator template
// TODO remove indicator template
// TODO delete exam template
}
}