From 911332e30fd2e95a34101782f40b6fea583bf50c Mon Sep 17 00:00:00 2001 From: anhefti Date: Wed, 27 Apr 2022 10:53:58 +0200 Subject: [PATCH] SEBSERV-233 implementation --- .../seb/sebserver/gbl/model/exam/Exam.java | 9 +++ .../sebserver/gui/content/exam/ExamList.java | 8 +++ .../sebserver/gui/widget/WidgetFactory.java | 1 + .../servicelayer/dao/impl/ExamDAOImpl.java | 66 +++++++++++++++++-- .../lms/impl/SEBRestrictionServiceImpl.java | 2 +- src/main/resources/static/css/sebserver.css | 8 +++ .../gbl/model/ModelObjectJSONGenerator.java | 2 +- .../integration/UseCasesIntegrationTest.java | 1 + .../integration/api/admin/ExamAPITest.java | 4 +- .../impl/SEBClientEventCSVExporterTest.java | 6 +- 10 files changed, 97 insertions(+), 10 deletions(-) diff --git a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java index 51bb37f3..2a3fc960 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java +++ b/src/main/java/ch/ethz/seb/sebserver/gbl/model/exam/Exam.java @@ -38,6 +38,7 @@ public final class Exam implements GrantEntity { -1L, -1L, Constants.EMPTY_NOTE, + false, Constants.EMPTY_NOTE, Constants.EMPTY_NOTE, null, @@ -59,6 +60,7 @@ public final class Exam implements GrantEntity { public static final String FILTER_ATTR_STATUS = "status"; public static final String FILTER_CACHED_QUIZZES = "cached-quizzes"; + public static final String ATTR_LMS_DATA_AVAILABLE = "lmsDataAvailable"; public static final String ATTR_ADDITIONAL_ATTRIBUTES = "additionalAttributes"; public enum ExamStatus { @@ -91,6 +93,9 @@ public final class Exam implements GrantEntity { @NotNull public final String externalId; + @JsonProperty(ATTR_LMS_DATA_AVAILABLE) + public final Boolean lmsDataAvailable; + @JsonProperty(QuizData.QUIZ_ATTR_NAME) public final String name; @@ -146,6 +151,7 @@ public final class Exam implements GrantEntity { @JsonProperty(EXAM.ATTR_INSTITUTION_ID) final Long institutionId, @JsonProperty(EXAM.ATTR_LMS_SETUP_ID) final Long lmsSetupId, @JsonProperty(EXAM.ATTR_EXTERNAL_ID) final String externalId, + @JsonProperty(ATTR_LMS_DATA_AVAILABLE) final Boolean lmsDataAvailable, @JsonProperty(QuizData.QUIZ_ATTR_NAME) final String name, @JsonProperty(QuizData.QUIZ_ATTR_DESCRIPTION) final String description, @JsonProperty(QuizData.QUIZ_ATTR_START_TIME) final DateTime startTime, @@ -167,6 +173,7 @@ public final class Exam implements GrantEntity { this.institutionId = institutionId; this.lmsSetupId = lmsSetupId; this.externalId = externalId; + this.lmsDataAvailable = lmsDataAvailable; this.name = name; this.description = description; this.startTime = startTime; @@ -195,6 +202,7 @@ public final class Exam implements GrantEntity { this.institutionId = quizData.institutionId; this.lmsSetupId = quizData.lmsSetupId; this.externalId = quizData.id; + this.lmsDataAvailable = true; this.name = quizData.name; this.description = quizData.description; this.startTime = quizData.startTime; @@ -225,6 +233,7 @@ public final class Exam implements GrantEntity { this.institutionId = null; this.lmsSetupId = null; this.externalId = null; + this.lmsDataAvailable = true; this.name = null; this.description = null; this.startTime = null; diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java index e3fb12d1..06c48ad5 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/content/exam/ExamList.java @@ -12,6 +12,7 @@ import java.util.function.BiConsumer; import java.util.function.BooleanSupplier; import java.util.function.Function; +import org.apache.commons.lang3.BooleanUtils; import org.eclipse.rap.rwt.RWT; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.TableItem; @@ -258,6 +259,11 @@ public class ExamList implements TemplateComposer { final Exam exam, final PageService pageService) { + if (BooleanUtils.isFalse(exam.lmsDataAvailable)) { + item.setData(RWT.CUSTOM_VARIANT, CustomVariant.DISABLED.key); + return; + } + if (exam.getStatus() == ExamStatus.UP_COMING || exam.getStatus() == ExamStatus.FINISHED) { return; } @@ -270,6 +276,8 @@ public class ExamList implements TemplateComposer { item.setData(RWT.CUSTOM_VARIANT, CustomVariant.WARNING.key); } }); + + item.setGrayed(true); } private static Function examLmsSetupNameFunction(final ResourceService resourceService) { diff --git a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java index 08f18eaa..8cab238f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java +++ b/src/main/java/ch/ethz/seb/sebserver/gui/widget/WidgetFactory.java @@ -206,6 +206,7 @@ public class WidgetFactory { MESSAGE("message"), ERROR("error"), WARNING("warning"), + DISABLED("disabled"), CONFIG_INPUT_READONLY("inputreadonly"), DARK_COLOR_LABEL("colordark"), diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java index 666487df..0634bc50 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/dao/impl/ExamDAOImpl.java @@ -69,7 +69,7 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.lms.impl.moodle.legacy.Mood @WebServiceProfile 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 ExamRecordDAO examRecordDAO; @@ -873,6 +873,10 @@ public class ExamDAOImpl implements ExamDAO { return Result.tryCatch(() -> { + if (quizData != null) { + saveFormerName(record.getId(), quizData.name); + } + final Collection supporter = (StringUtils.isNotBlank(record.getSupporter())) ? Arrays.asList(StringUtils.split(record.getSupporter(), Constants.LIST_SEPARATOR_CHAR)) : null; @@ -905,9 +909,10 @@ public class ExamDAOImpl implements ExamDAO { record.getInstitutionId(), record.getLmsSetupId(), record.getExternalId(), - (quizData != null) ? quizData.name : FAILED_TO_LOAD_QUIZ_DATA_MARK, - (quizData != null) ? quizData.description : FAILED_TO_LOAD_QUIZ_DATA_MARK, - (quizData != null) ? quizData.startTime : new DateTime(0), + (quizData != null), + (quizData != null) ? quizData.name : getFormerName(record.getId()), + (quizData != null) ? quizData.description : null, + (quizData != null) ? quizData.startTime : null, (quizData != null) ? quizData.endTime : null, (quizData != null) ? quizData.startURL : Constants.EMPTY_NOTE, ExamType.valueOf(record.getType()), @@ -924,4 +929,57 @@ public class ExamDAOImpl implements ExamDAO { }); } + private void saveFormerName(final Long id, final String name) { + try { + final Integer updated = this.additionalAttributeRecordMapper + .updateByExampleSelective(new AdditionalAttributeRecord(null, null, null, null, name)) + .where( + AdditionalAttributeRecordDynamicSqlSupport.entityType, + SqlBuilder.isEqualTo(EntityType.EXAM.name())) + .and( + AdditionalAttributeRecordDynamicSqlSupport.entityId, + SqlBuilder.isEqualTo(id)) + .and( + AdditionalAttributeRecordDynamicSqlSupport.name, + SqlBuilder.isEqualTo("formerQuizName")) + .build() + .execute(); + + if (updated == null || updated.intValue() < 1) { + this.additionalAttributeRecordMapper.insert(new AdditionalAttributeRecord( + null, + EntityType.EXAM.name(), + id, + "formerQuizName", + name)); + } + } catch (final Exception e) { + log.error("Failed to save former name: examId: {}, name: {} error: {}", id, name, e.getMessage()); + } + } + + private String getFormerName(final Long id) { + try { + return this.additionalAttributeRecordMapper + .selectByExample() + .where( + AdditionalAttributeRecordDynamicSqlSupport.entityType, + SqlBuilder.isEqualTo(EntityType.EXAM.name())) + .and( + AdditionalAttributeRecordDynamicSqlSupport.entityId, + SqlBuilder.isEqualTo(id)) + .and( + AdditionalAttributeRecordDynamicSqlSupport.name, + SqlBuilder.isEqualTo("formerQuizName")) + .build() + .execute() + .stream() + .collect(Utils.toSingleton()) + .getValue(); + } catch (final Exception e) { + log.error("Failed to get former name: examId: {} error: {}", id, e.getMessage()); + return null; + } + } + } diff --git a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java index 4a5b90fd..8b9b6c7f 100644 --- a/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java +++ b/src/main/java/ch/ethz/seb/sebserver/webservice/servicelayer/lms/impl/SEBRestrictionServiceImpl.java @@ -156,7 +156,7 @@ public class SEBRestrictionServiceImpl implements SEBRestrictionService { final Collection browserExamKeys = sebRestriction.getBrowserExamKeys(); final Exam newExam = new Exam( exam.id, - null, null, null, null, null, null, null, null, null, null, + null, null, null, null, null, null, null, null, null, null, null, exam.supporter, exam.status, null, diff --git a/src/main/resources/static/css/sebserver.css b/src/main/resources/static/css/sebserver.css index 2be30294..7c0a1e55 100644 --- a/src/main/resources/static/css/sebserver.css +++ b/src/main/resources/static/css/sebserver.css @@ -890,6 +890,14 @@ TableItem { background-image: none; } +TableItem.disabled { + background-color: transparent; + color: #aaaaaa; + text-decoration: none; + text-shadow: none; + background-image: none; +} + Table-RowOverlay.warning { background-color: rgba( 168, 50, 45, 0.5 ); background-gradient-color: rgba( 168, 50, 45, 0.5 ); diff --git a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java index de442a7d..72e089d8 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java +++ b/src/test/java/ch/ethz/seb/sebserver/gbl/model/ModelObjectJSONGenerator.java @@ -193,7 +193,7 @@ public class ModelObjectJSONGenerator { System.out.println(writerWithDefaultPrettyPrinter.writeValueAsString(domainObject)); domainObject = new Exam( - 1L, 1L, 1L, "externalId", "name", "description", DateTime.now(), DateTime.now(), + 1L, 1L, 1L, "externalId", true, "name", "description", DateTime.now(), DateTime.now(), "startURL", ExamType.BYOD, "owner", Arrays.asList("user1", "user2"), ExamStatus.RUNNING, false, "browserExamKeys", true, null, null, null, null); diff --git a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java index 26d0f9e1..db194e55 100644 --- a/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/gui/integration/UseCasesIntegrationTest.java @@ -883,6 +883,7 @@ public class UseCasesIntegrationTest extends GuiIntegrationTest { newExam.institutionId, newExam.lmsSetupId, newExam.externalId, + true, newExam.name, newExam.description, newExam.startTime, diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java index ae0b4590..733dad65 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/integration/api/admin/ExamAPITest.java @@ -54,7 +54,7 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.id, exam.institutionId, exam.lmsSetupId, - exam.externalId, + exam.externalId, true, exam.name, exam.description, exam.startTime, @@ -85,7 +85,7 @@ public class ExamAPITest extends AdministrationAPIIntegrationTester { exam.id, exam.institutionId, exam.lmsSetupId, - exam.externalId, + exam.externalId, true, exam.name, exam.description, exam.startTime, diff --git a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java index 3b7462b5..5fd809ba 100644 --- a/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java +++ b/src/test/java/ch/ethz/seb/sebserver/webservice/servicelayer/exam/impl/SEBClientEventCSVExporterTest.java @@ -109,7 +109,8 @@ public class SEBClientEventCSVExporterTest { public void streamDataTestWithExam() { final SEBClientEventCSVExporter exporter = new SEBClientEventCSVExporter(); final ClientEventRecord event = new ClientEventRecord(0L, 1L, 2, 3L, 4L, new BigDecimal(5), "text"); - final Exam exam = new Exam(0L, 1L, 3L, "externalid", "name", "description", new DateTime(1L), new DateTime(1L), + final Exam exam = new Exam(0L, 1L, 3L, "externalid", true, "name", "description", new DateTime(1L), + new DateTime(1L), "startURL", Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, "lastUpdate", 4L, null, null); final ByteArrayOutputStream stream = new ByteArrayOutputStream(); @@ -132,7 +133,8 @@ public class SEBClientEventCSVExporterTest { "seb_os_name", "seb_machine_name", "seb_version"); final SEBClientEventCSVExporter exporter = new SEBClientEventCSVExporter(); final ClientEventRecord event = new ClientEventRecord(0L, 1L, 2, 3L, 4L, new BigDecimal(5), "text"); - final Exam exam = new Exam(0L, 1L, 3L, "externalid", "name", "description", new DateTime(1L), new DateTime(1L), + final Exam exam = new Exam(0L, 1L, 3L, "externalid", true, "name", "description", new DateTime(1L), + new DateTime(1L), "startURL", Exam.ExamType.BYOD, "owner", new ArrayList<>(), Exam.ExamStatus.RUNNING, false, "bek", true, "lastUpdate", 4L, null, null); final ByteArrayOutputStream stream = new ByteArrayOutputStream();