Index: lams_common/src/java/org/lamsfoundation/lams/flux/FluxRegistry.java =================================================================== diff -u -r6af10a84f90327690b640c49390c0c4084353cdd -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_common/src/java/org/lamsfoundation/lams/flux/FluxRegistry.java (.../FluxRegistry.java) (revision 6af10a84f90327690b640c49390c0c4084353cdd) +++ lams_common/src/java/org/lamsfoundation/lams/flux/FluxRegistry.java (.../FluxRegistry.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -63,7 +63,7 @@ sink = FluxRegistry.getSink(sinkName); } if (itemEqualsPredicate == null) { - itemEqualsPredicate = (key, item) -> item.equals(key); + itemEqualsPredicate = (item, key) -> item.equals(key); } FluxMap fluxMap = new FluxMap<>(fluxName, sink.getFlux(), itemEqualsPredicate, fetchFunction, throttleSeconds, timeoutSeconds); Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java =================================================================== diff -u -r616c7119289412164b78e0f420725a865e60c933 -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java (.../LessonDAO.java) (revision 616c7119289412164b78e0f420725a865e60c933) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dao/hibernate/LessonDAO.java (.../LessonDAO.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -104,7 +104,7 @@ private final static String FIND_LESSON_IDS_BY_ORG_ID = "SELECT lesson.lessonId FROM " + Lesson.class.getName() + " AS lesson WHERE lesson.organisation.organisationId = :organisationId"; - private final static String FIND_ABSOLUTE_TIME_LIMITS = "SELECT a.activity_id AS activityId, a.title AS activityTitle, " + private final static String FIND_ABSOLUTE_TIME_LIMITS = "SELECT a.tool_content_id AS toolContentId, a.title AS activityTitle, " + "(SELECT absolute_time_limit FROM tl_lascrt11_scratchie WHERE content_id = tool_content_id UNION " + " SELECT absolute_time_limit FROM tl_laasse10_assessment WHERE content_id = tool_content_id " + ") AS absolute_time_limit " Index: lams_common/src/java/org/lamsfoundation/lams/lesson/dto/ActivityTimeLimitDTO.java =================================================================== diff -u -rd40a833e3c39b4517628fcd947d8da92fc30b37d -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_common/src/java/org/lamsfoundation/lams/lesson/dto/ActivityTimeLimitDTO.java (.../ActivityTimeLimitDTO.java) (revision d40a833e3c39b4517628fcd947d8da92fc30b37d) +++ lams_common/src/java/org/lamsfoundation/lams/lesson/dto/ActivityTimeLimitDTO.java (.../ActivityTimeLimitDTO.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -4,18 +4,18 @@ import java.time.temporal.ChronoUnit; public class ActivityTimeLimitDTO { - private Long activityId; + private Long toolContentId; private String activityTitle; private LocalDateTime absoluteTimeLimit; - public ActivityTimeLimitDTO(Long activityId, String activityTitle, LocalDateTime absoluteTimeLimit) { - this.activityId = activityId; + public ActivityTimeLimitDTO(Long toolContentId, String activityTitle, LocalDateTime absoluteTimeLimit) { + this.toolContentId = toolContentId; this.activityTitle = activityTitle; this.absoluteTimeLimit = absoluteTimeLimit; } - public Long getActivityId() { - return activityId; + public Long getToolContentId() { + return toolContentId; } public String getActivityTitle() { Index: lams_common/src/java/org/lamsfoundation/lams/util/CommonConstants.java =================================================================== diff -u -r616c7119289412164b78e0f420725a865e60c933 -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_common/src/java/org/lamsfoundation/lams/util/CommonConstants.java (.../CommonConstants.java) (revision 616c7119289412164b78e0f420725a865e60c933) +++ lams_common/src/java/org/lamsfoundation/lams/util/CommonConstants.java (.../CommonConstants.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -76,4 +76,6 @@ public static final String LESSON_JOINED_SINK_NAME = "lesson joined by learner"; // updated when a there was a change to lesson progress public static final String LESSON_PROGRESSED_SINK_NAME = "lesson progress changed"; + // updated when time limit changes in an activity + public static final String ACTIVITY_TIME_LIMIT_CHANGED_SINK_NAME = "activity time limit changed"; } Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java =================================================================== diff -u -r198c8a9ea2d927fdf19aa00b222831934f6af176 -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java (.../MonitoringConstants.java) (revision 198c8a9ea2d927fdf19aa00b222831934f6af176) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/MonitoringConstants.java (.../MonitoringConstants.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -69,4 +69,5 @@ // flux management public static final String CANVAS_REFRESH_FLUX_NAME = "canvas refresh"; public static final String GRADEBOOK_REFRESH_FLUX_NAME = "gradebook refresh"; + public static final String TIME_LIMIT_REFRESH_FLUX_NAME = "time limit refresh"; } \ No newline at end of file Index: lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java =================================================================== diff -u -rd40a833e3c39b4517628fcd947d8da92fc30b37d -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java (.../MonitoringController.java) (revision d40a833e3c39b4517628fcd947d8da92fc30b37d) +++ lams_monitoring/src/java/org/lamsfoundation/lams/monitoring/web/MonitoringController.java (.../MonitoringController.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -105,6 +105,7 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.http.MediaType; import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; @@ -176,6 +177,10 @@ FluxRegistry.initFluxMap(MonitoringConstants.GRADEBOOK_REFRESH_FLUX_NAME, CommonConstants.LESSON_PROGRESSED_SINK_NAME, null, lessonId -> "doRefresh", FluxMap.STANDARD_THROTTLE, FluxMap.STANDARD_TIMEOUT); + FluxRegistry.initFluxMap(MonitoringConstants.TIME_LIMIT_REFRESH_FLUX_NAME, + CommonConstants.ACTIVITY_TIME_LIMIT_CHANGED_SINK_NAME, + (Collection key, Collection item) -> key.containsAll(item), toolContentIds -> "doRefresh", + FluxMap.SHORT_THROTTLE, FluxMap.STANDARD_TIMEOUT); } private Integer getUserId() { @@ -208,6 +213,13 @@ return FluxRegistry.get(MonitoringConstants.GRADEBOOK_REFRESH_FLUX_NAME, lessonId); } + @RequestMapping(path = "/getTimeLimitUpdateFlux", method = RequestMethod.GET, produces = MediaType.TEXT_EVENT_STREAM_VALUE) + @ResponseBody + public Flux getTimeLimitUpdateFlux(@RequestParam Set toolContentIds) + throws JsonProcessingException, IOException { + return FluxRegistry.get(MonitoringConstants.TIME_LIMIT_REFRESH_FLUX_NAME, toolContentIds); + } + /** * Initializes a lesson for specific learning design with the given lesson title and lesson description. If * initialization is successful, this method will the ID of new lesson. @@ -1619,6 +1631,22 @@ return "timer"; } + @GetMapping("/getTimeLimits") + @ResponseBody + public String getTimeLimits(@RequestParam long lessonID, HttpServletResponse response) throws IOException { + ArrayNode responseJSON = null; + + List absoluteTimeLimits = lessonService.getRunningAbsoluteTimeLimits(lessonID); + if (absoluteTimeLimits.isEmpty()) { + responseJSON = JsonNodeFactory.instance.arrayNode(); + } else { + responseJSON = JsonUtil.readArray(absoluteTimeLimits); + } + + response.setContentType("application/json;charset=utf-8"); + return responseJSON.toString(); + } + /** * Creates a list of users out of string with comma-delimited user IDs. */ Index: lams_monitoring/web/includes/javascript/monitorLesson5.js =================================================================== diff -u -rd40a833e3c39b4517628fcd947d8da92fc30b37d -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_monitoring/web/includes/javascript/monitorLesson5.js (.../monitorLesson5.js) (revision d40a833e3c39b4517628fcd947d8da92fc30b37d) +++ lams_monitoring/web/includes/javascript/monitorLesson5.js (.../monitorLesson5.js) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -1578,37 +1578,68 @@ updateContributeActivities(response.contributeActivities); - let timeLimitsDiv = $('#lesson-time-limits').toggleClass('d-none', !response.timeLimits); + // set up flux for updating time limits on dashboard if (response.timeLimits) { - $('.is-countdown', timeLimitsDiv).countdown('destroy').closest('.row').remove(); - + let timeLimitFluxUrl = LAMS_URL + 'monitoring/monitoring/getTimeLimitUpdateFlux.do?'; $.each(response.timeLimits, function(){ - let timeLimit = this, - row = $('
').appendTo(timeLimitsDiv) - $('
').text(timeLimit.activityTitle).appendTo(row); - $('
') - .appendTo(row) - .countdown({ - until: '+' + timeLimit.secondsLeft +'S', - format: 'hMS', - compact: true, - alwaysExpire : false, - onTick: function(periods) { - // check for 30 seconds or less and display timer in red - var secondsLeft = $.countdown.periodsToSeconds(periods); - if (secondsLeft <= 30) { - $(this).addClass('countdown-timeout'); - } else { - $(this).removeClass('countdown-timeout'); - } - } - }); + // it is a list of tool content IDs to which the dashboard will react and update time limits + timeLimitFluxUrl += 'toolContentIds=' + this.toolContentId + '&'; }); + + openEventSource(timeLimitFluxUrl, + function (event) { + if ("doRefresh" == event.data && $('#sequence-tab-content').length === 1){ + updateTimeLimits(); + } + }); } } }); } +/** + Gets running absolute time limits for the lesson and displays them as countdown timers + */ +function updateTimeLimits(){ + $.ajax({ + dataType : 'json', + url : LAMS_URL + 'monitoring/monitoring/getTimeLimits.do', + cache : false, + data : { + 'lessonID' : lessonId + }, + success : function(timeLimits) { + let timeLimitsDiv = $('#lesson-time-limits').toggleClass('d-none', timeLimits.length === 0); + $('.is-countdown', timeLimitsDiv).countdown('destroy').closest('.row').remove(); + + $.each(timeLimits, function(){ + let timeLimit = this, + row = $('
').appendTo(timeLimitsDiv) + $('
').text(timeLimit.activityTitle).appendTo(row); + $('
') + .appendTo(row) + .countdown({ + until: '+' + timeLimit.secondsLeft +'S', + format: 'hMS', + compact: true, + alwaysExpire : false, + onTick: function(periods) { + // check for 30 seconds or less and display timer in red + var secondsLeft = $.countdown.periodsToSeconds(periods); + if (secondsLeft <= 30) { + $(this).addClass('countdown-timeout'); + } else { + $(this).removeClass('countdown-timeout'); + } + } + }); + }); + } + }); + + +} + function updateLiveEdit() { if ( liveEditEnabled ) { if ( lockedForEdit ) { Index: lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java =================================================================== diff -u -r2918197dbd481269aa40c7f1ac9a7612dceceb05 -r976df526805398d7daa7d49e4e11b81272ec3cb5 --- lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 2918197dbd481269aa40c7f1ac9a7612dceceb05) +++ lams_tool_assessment/src/java/org/lamsfoundation/lams/tool/assessment/web/controller/MonitoringController.java (.../MonitoringController.java) (revision 976df526805398d7daa7d49e4e11b81272ec3cb5) @@ -51,6 +51,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.log4j.Logger; +import org.lamsfoundation.lams.flux.FluxRegistry; import org.lamsfoundation.lams.learningdesign.Group; import org.lamsfoundation.lams.learningdesign.Grouping; import org.lamsfoundation.lams.logevent.LearnerInteractionEvent; @@ -901,6 +902,9 @@ assessment.setAbsoluteTimeLimit(absoluteTimeLimit == null ? null : LocalDateTime.ofEpochSecond(absoluteTimeLimit, 0, OffsetDateTime.now().getOffset())); service.saveOrUpdateAssessment(assessment); + + // update monitoring UI where time limits are reflected on dashboard + FluxRegistry.emit(CommonConstants.ACTIVITY_TIME_LIMIT_CHANGED_SINK_NAME, Set.of(toolContentId)); } @RequestMapping(path = "/getPossibleIndividualTimeLimits", method = RequestMethod.GET) @@ -919,7 +923,8 @@ if (grouping != null) { Set groups = grouping.getGroups(); for (Group group : groups) { - if (!group.getUsers().isEmpty() && group.getGroupName().toLowerCase().contains(searchString.toLowerCase())) { + if (!group.getUsers().isEmpty() + && group.getGroupName().toLowerCase().contains(searchString.toLowerCase())) { ObjectNode groupJSON = JsonNodeFactory.instance.objectNode(); groupJSON.put("label", groupLabel + group.getGroupName() + "\""); groupJSON.put("value", "group-" + group.getGroupId());