Index: lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20210506.sql =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20210506.sql (.../patch20210506.sql) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_common/src/java/org/lamsfoundation/lams/dbupdates/patch20210506.sql (.../patch20210506.sql) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -8,16 +8,16 @@ CREATE TABLE lams_discussion_sentiment ( uid BIGINT AUTO_INCREMENT, lesson_id BIGINT NOT NULL, - tool_content_id BIGINT NOT NULL, + tool_question_uid BIGINT NOT NULL, burning_question_uid BIGINT, user_id BIGINT, selected_option TINYINT UNSIGNED, PRIMARY KEY (uid), INDEX IDX_lams_discussion_sentiment_1 (burning_question_uid), - UNIQUE INDEX UQ_lams_discussion_sentiment_2 (lesson_id, tool_content_id, burning_question_id, user_id), + UNIQUE INDEX UQ_lams_discussion_sentiment_2 (lesson_id, tool_question_uid, burning_question_uid, user_id), CONSTRAINT FK_lams_discussion_sentiment_1 FOREIGN KEY (lesson_id) REFERENCES lams_lesson (lesson_id) ON DELETE CASCADE ON UPDATE CASCADE, - CONSTRAINT FK_lams_discussion_sentiment_2 FOREIGN KEY (tool_content_id) REFERENCES lams_tool_content (tool_content_id) + CONSTRAINT FK_lams_discussion_sentiment_2 FOREIGN KEY (tool_question_uid) REFERENCES lams_qb_tool_question (tool_question_uid) ON DELETE CASCADE ON UPDATE CASCADE, CONSTRAINT FK_lams_discussion_sentiment_3 FOREIGN KEY (user_id) REFERENCES lams_user (user_id) ON DELETE CASCADE ON UPDATE CASCADE Index: lams_learning/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r1a9c48126670b7854b03e61711ccef3299357963 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 1a9c48126670b7854b03e61711ccef3299357963) +++ lams_learning/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -187,3 +187,14 @@ label.password.gate.message = Enter password label.password.gate.incorrect.password = You provided an incorrect password. Retry again? label.password.gate.title = Password Gate +label.discussion.header = Discussion +label.discussion.stay.header = Stay here +label.discussion.move.header = Move on +label.discussion.stay.option.1 = Pls clarify in simpler terms +label.discussion.stay.option.2 = Need time to process / discuss in our teams, pls +label.discussion.stay.option.3 = Pls back-track to explain the more basic concepts first +label.discussion.stay.option.4 = Other +label.discussion.move.option.11 = Outside LOs (pls KIV to Post-Session) +label.discussion.move.option.12 = Pls state key point(s) and move on +label.discussion.move.option.13 = We already understand! +label.discussion.move.option.14 = Other \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java (.../CommandWebsocketServer.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/command/CommandWebsocketServer.java (.../CommandWebsocketServer.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -16,7 +16,6 @@ import org.apache.log4j.Logger; import org.lamsfoundation.lams.learning.command.model.Command; -import org.lamsfoundation.lams.learning.discussion.service.IDiscussionSentimentService; import org.lamsfoundation.lams.learning.service.ILearnerFullService; import org.lamsfoundation.lams.learning.service.ILearnerService; import org.lamsfoundation.lams.util.hibernate.HibernateSessionManager; @@ -35,8 +34,6 @@ private static ILearnerFullService learnerService; - private static IDiscussionSentimentService discussionSentimentService; - /** * A singleton which updates Learners with messages and commands. */ @@ -138,8 +135,6 @@ String login = websocket.getUserPrincipal().getName(); sessionWebsockets.put(login, websocket); - - discussionSentimentService.restartDiscussionForLearner(lessonId, login); } /** Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/controller/DiscussionSentimentController.java =================================================================== diff -u --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/controller/DiscussionSentimentController.java (revision 0) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/controller/DiscussionSentimentController.java (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -0,0 +1,105 @@ +package org.lamsfoundation.lams.learning.discussion.controller; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; + +import org.lamsfoundation.lams.learning.discussion.service.IDiscussionSentimentService; +import org.lamsfoundation.lams.usermanagement.dto.UserDTO; +import org.lamsfoundation.lams.web.session.SessionManager; +import org.lamsfoundation.lams.web.util.AttributeNames; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.ui.Model; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.ResponseBody; + +@Controller +@RequestMapping("/discussionSentiment") +public class DiscussionSentimentController { + @Autowired + private IDiscussionSentimentService discussionSentimentService; + + /** + * Called via Page.tag. + * Checks if there is a discussion running. If so, send the learner a command to show widget. + */ + @RequestMapping("/checkLearner") + @ResponseBody + public void checkLearnerWidget(@RequestParam long lessonId, HttpServletResponse response) throws IOException { + UserDTO user = DiscussionSentimentController.getCurrentUserDto(); + if (user == null) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return; + } + + discussionSentimentService.startDiscussionForLearner(lessonId, user.getLogin()); + } + + /** + * Called via Page.tag. + * Sends a simple JSP page with JavaScript code to initialise the widget + */ + @RequestMapping("/startLearner") + public String startLearnerWidget(@RequestParam long lessonId, HttpServletResponse response, Model model) + throws IOException { + UserDTO user = DiscussionSentimentController.getCurrentUserDto(); + if (user == null) { + response.sendError(HttpServletResponse.SC_UNAUTHORIZED); + return null; + } + + // tell learner what he already selected + Integer selectedOption = discussionSentimentService.getDiscussionVoteSelectedOption(lessonId, user.getUserID()); + if (selectedOption != null) { + model.addAttribute("selectedOption", selectedOption); + } + return "/discussion/startLearner"; + } + + /** + * Called via Page.tag. + * Sends a simple JSP page with JavaScript code to remove the widget + */ + @RequestMapping("/stopLearner") + public String stopLearnerWidget() { + return "/discussion/stopLearner"; + } + + @RequestMapping(path = "/vote", method = RequestMethod.POST) + @ResponseBody + public ResponseEntity learnerVote(@RequestParam long lessonId, @RequestParam int selectedOption) { + UserDTO user = DiscussionSentimentController.getCurrentUserDto(); + if (user == null) { + return ResponseEntity.status(HttpStatus.UNAUTHORIZED).build(); + } + + // it should always return true as there should be no widget displayed if no discussion is active + boolean discussionActive = discussionSentimentService.addDiscussionVoteForLearner(lessonId, user.getUserID(), + selectedOption); + return ResponseEntity.ok(discussionActive ? "voted" : "stop"); + } + + @RequestMapping(path = "/startMonitor", method = RequestMethod.POST) + @ResponseBody + public void startMonitorWidget(@RequestParam long toolQuestionUid, + @RequestParam(required = false) Long burningQuestionUid) { + discussionSentimentService.startDiscussionForMonitor(toolQuestionUid, burningQuestionUid); + } + + @RequestMapping(path = "/stopMonitor", method = RequestMethod.POST) + @ResponseBody + public void stopMonitorWidget(@RequestParam long toolQuestionUid) { + discussionSentimentService.stopDiscussionForMonitor(toolQuestionUid); + } + + private static UserDTO getCurrentUserDto() { + HttpSession ss = SessionManager.getSession(); + return (UserDTO) ss.getAttribute(AttributeNames.USER); + } +} \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/IDiscussionSentimentDAO.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/IDiscussionSentimentDAO.java (.../IDiscussionSentimentDAO.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/IDiscussionSentimentDAO.java (.../IDiscussionSentimentDAO.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -9,7 +9,7 @@ DiscussionSentimentVote getActiveDiscussion(long lessonId); - Map getDiscussionAggregatedVotes(long lessonId, long toolContentId, Long burningQuestionUid); + Map getDiscussionAggregatedVotes(long toolQuestionUid, Long burningQuestionUid); - DiscussionSentimentVote getDiscussionVote(long lessonId, long toolContentId, Long burningQuestionUid, int userId); + DiscussionSentimentVote getDiscussionVote(long toolQuestionUid, Long burningQuestionUid, int userId); } \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/hibernate/DiscussionSentimentDAO.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/hibernate/DiscussionSentimentDAO.java (.../DiscussionSentimentDAO.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/dao/hibernate/DiscussionSentimentDAO.java (.../DiscussionSentimentDAO.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -21,10 +21,9 @@ } @Override - public Map getDiscussionAggregatedVotes(long lessonId, long toolContentId, Long burningQuestionUid) { + public Map getDiscussionAggregatedVotes(long toolQuestionUid, Long burningQuestionUid) { Map properties = new HashMap<>(); - properties.put("lessonId", lessonId); - properties.put("toolContentId", toolContentId); + properties.put("toolQuestionUid", toolQuestionUid); if (burningQuestionUid != null) { properties.put("burningQuestionUid", burningQuestionUid); } @@ -34,11 +33,9 @@ } @Override - public DiscussionSentimentVote getDiscussionVote(long lessonId, long toolContentId, Long burningQuestionUid, - int userId) { + public DiscussionSentimentVote getDiscussionVote(long toolQuestionUid, Long burningQuestionUid, int userId) { Map properties = new HashMap<>(); - properties.put("lessonId", lessonId); - properties.put("toolContentId", toolContentId); + properties.put("toolQuestionUid", toolQuestionUid); if (burningQuestionUid != null) { properties.put("burningQuestionUid", burningQuestionUid); } Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/model/DiscussionSentimentVote.java =================================================================== diff -u -r8967cf285e8f080454bfeb80ea34e9022041ee34 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/model/DiscussionSentimentVote.java (.../DiscussionSentimentVote.java) (revision 8967cf285e8f080454bfeb80ea34e9022041ee34) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/model/DiscussionSentimentVote.java (.../DiscussionSentimentVote.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -18,8 +18,8 @@ @Column(name = "lesson_id") private Long lessonId; - @Column(name = "tool_content_id") - private Long toolContentId; + @Column(name = "tool_question_uid") + private Long toolQuestionUid; @Column(name = "burning_question_uid") private Long burningQuestionUid; @@ -33,9 +33,10 @@ public DiscussionSentimentVote() { } - public DiscussionSentimentVote(Long lessonId, Long toolContentId) { + public DiscussionSentimentVote(Long lessonId, Long toolQuestionUid, Long burningQuestionUid) { this.lessonId = lessonId; - this.toolContentId = toolContentId; + this.toolQuestionUid = toolQuestionUid; + this.burningQuestionUid = burningQuestionUid; } public Long getUid() { @@ -54,12 +55,12 @@ this.lessonId = lessonId; } - public Long getToolContentId() { - return toolContentId; + public Long getToolQuestionUid() { + return toolQuestionUid; } - public void setToolContentId(Long toolContentId) { - this.toolContentId = toolContentId; + public void setToolQuestionUid(Long toolQuestionUid) { + this.toolQuestionUid = toolQuestionUid; } public Long getBurningQuestionUid() { Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/DiscussionSentimentService.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/DiscussionSentimentService.java (.../DiscussionSentimentService.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/DiscussionSentimentService.java (.../DiscussionSentimentService.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -5,97 +5,136 @@ import org.lamsfoundation.lams.learning.discussion.dao.IDiscussionSentimentDAO; import org.lamsfoundation.lams.learning.discussion.model.DiscussionSentimentVote; -import org.lamsfoundation.lams.learning.service.ILearnerService; -import org.lamsfoundation.lams.usermanagement.User; -import org.lamsfoundation.lams.usermanagement.service.IUserManagementService; +import org.lamsfoundation.lams.learning.service.ILearnerFullService; +import org.lamsfoundation.lams.learningdesign.ToolActivity; +import org.lamsfoundation.lams.qb.model.QbToolQuestion; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; + public class DiscussionSentimentService implements IDiscussionSentimentService { private IDiscussionSentimentDAO discussionSentimentDAO; - private ILearnerService learnerService; + private ILearnerFullService learnerService; - private IUserManagementService userManagementService; - + // we are caching active discussions as many students can ask for their details in short period of time private Map> cachedActiveDiscussionTokens = new ConcurrentHashMap<>(); private static long ACTIVE_DISCUSSION_TOKEN_REFRESH_PERIOD = 5 * 1000; @Override - public void startDiscussion(long lessonId, long toolContentId, Long burningQuestionUid) { + public void startDiscussionForMonitor(long toolQuestionUid, Long burningQuestionUid) { + long toolContentId = getToolContentId(toolQuestionUid); + long lessonId = getLessonIdByToolContentId(toolContentId); + // stop current discussion, if any stopDiscussionInternal(lessonId); - DiscussionSentimentVote activeDiscussionToken = new DiscussionSentimentVote(lessonId, toolContentId); - activeDiscussionToken.setBurningQuestionUid(burningQuestionUid); + // put a token entry in DB with no user ID, indicating which question ID is currently discussed in given lesson + DiscussionSentimentVote activeDiscussionToken = new DiscussionSentimentVote(lessonId, toolQuestionUid, + burningQuestionUid); discussionSentimentDAO.insert(activeDiscussionToken); + // update cached token + discussionSentimentDAO.releaseFromCache(activeDiscussionToken); cachedActiveDiscussionTokens.put(lessonId, Map.of(System.currentTimeMillis(), activeDiscussionToken)); - learnerService.createCommandForLessonLearners(toolContentId, "discussion-start"); + // tell all learners in the lesson that teacher has (re)started a discussion + ObjectNode jsonCommand = JsonNodeFactory.instance.objectNode(); + jsonCommand.put("discussion", "startLearner"); + + learnerService.createCommandForLessonLearners(toolContentId, jsonCommand.toString()); } @Override - public void stopDiscussion(long lessonId) { - cachedActiveDiscussionTokens.remove(lessonId); + public void stopDiscussionForMonitor(long toolQuestionUid) { + long toolContentId = getToolContentId(toolQuestionUid); + long lessonId = getLessonIdByToolContentId(toolContentId); + // stop discussion DiscussionSentimentVote activeDiscussionToken = stopDiscussionInternal(lessonId); if (activeDiscussionToken != null) { - learnerService.createCommandForLessonLearners(activeDiscussionToken.getToolContentId(), "discussion-stop"); + // tell all learners in the lesson that discussion has stopped + ObjectNode jsonCommand = JsonNodeFactory.instance.objectNode(); + jsonCommand.put("discussion", "stopLearner"); + learnerService.createCommandForLessonLearners(toolContentId, jsonCommand.toString()); } } + /** + * Run when a learner connects to a websocket. + * Checks if a discussion is running and the widget needs to be shown. + */ @Override - public void restartDiscussionForLearner(long lessonId, String login) { + public void startDiscussionForLearner(long lessonId, String login) { DiscussionSentimentVote cachedActiveDiscussionToken = getCachedActiveDiscussion(lessonId); if (cachedActiveDiscussionToken == null) { return; } - User learner = userManagementService.getUserByLogin(login); - if (learner == null) { - return; - } - DiscussionSentimentVote learnerVote = discussionSentimentDAO.getDiscussionVote(lessonId, - cachedActiveDiscussionToken.getToolContentId(), cachedActiveDiscussionToken.getBurningQuestionUid(), - learner.getUserId()); - - learnerService.createCommandForLearner(lessonId, login, - "discussion-start" + (learnerVote == null ? "" : "-vote-" + learnerVote.getSelectedOption())); + // tell just this learner that discussion is running + ObjectNode jsonCommand = JsonNodeFactory.instance.objectNode(); + jsonCommand.put("discussion", "startLearner"); + learnerService.createCommandForLearner(lessonId, login, jsonCommand.toString()); } @Override - public void addDiscussionVoteForLearner(long lessonId, int userId, int selectedOption) { + public boolean addDiscussionVoteForLearner(long lessonId, int userId, int selectedOption) { DiscussionSentimentVote cachedActiveDiscussionToken = getCachedActiveDiscussion(lessonId); if (cachedActiveDiscussionToken == null) { - return; + // no discussion is running - why widget was shown at all? maybe a monitor just stopped the discussion + return false; } - DiscussionSentimentVote learnerVote = discussionSentimentDAO.getDiscussionVote(lessonId, - cachedActiveDiscussionToken.getToolContentId(), cachedActiveDiscussionToken.getBurningQuestionUid(), + // just one vote per learner, so remove the previous vote if it exists + DiscussionSentimentVote learnerVote = discussionSentimentDAO.getDiscussionVote( + cachedActiveDiscussionToken.getToolQuestionUid(), cachedActiveDiscussionToken.getBurningQuestionUid(), userId); if (learnerVote != null) { discussionSentimentDAO.delete(learnerVote); } - learnerVote = new DiscussionSentimentVote(lessonId, cachedActiveDiscussionToken.getToolContentId()); + // add the vote + // it is different to discussion token as it contains user ID + learnerVote = new DiscussionSentimentVote(lessonId, cachedActiveDiscussionToken.getToolQuestionUid(), + cachedActiveDiscussionToken.getBurningQuestionUid()); learnerVote.setBurningQuestionUid(cachedActiveDiscussionToken.getBurningQuestionUid()); learnerVote.setUserId(userId); learnerVote.setSelectedOption(selectedOption); discussionSentimentDAO.insert(learnerVote); + + return true; } @Override + public Integer getDiscussionVoteSelectedOption(long lessonId, int userId) { + DiscussionSentimentVote cachedActiveDiscussionToken = getCachedActiveDiscussion(lessonId); + if (cachedActiveDiscussionToken == null) { + // no discussion is running - why widget was shown at all? maybe a monitor just stopped the discussion + return null; + } + + // just asking for selected option, not all discussion details + DiscussionSentimentVote vote = discussionSentimentDAO.getDiscussionVote( + cachedActiveDiscussionToken.getToolQuestionUid(), cachedActiveDiscussionToken.getBurningQuestionUid(), + userId); + return vote == null ? null : vote.getSelectedOption(); + } + + @Override public Map getDiscussionAggregatedVotes(long lessonId, long toolContentId, Long burningQuestionUid) { - return discussionSentimentDAO.getDiscussionAggregatedVotes(lessonId, toolContentId, burningQuestionUid); + return discussionSentimentDAO.getDiscussionAggregatedVotes(toolContentId, burningQuestionUid); } private DiscussionSentimentVote getCachedActiveDiscussion(long lessonId) { Map cachedActiveDiscussion = cachedActiveDiscussionTokens.get(lessonId); + // if cache is fresh, return the discussion token if (cachedActiveDiscussion != null && System.currentTimeMillis() - cachedActiveDiscussion.keySet().iterator().next() < ACTIVE_DISCUSSION_TOKEN_REFRESH_PERIOD) { return cachedActiveDiscussion.values().iterator().next(); } + // there is a token in cache, but there is no discussion in DB, so clear cache DiscussionSentimentVote activeDiscussionToken = discussionSentimentDAO.getActiveDiscussion(lessonId); if (activeDiscussionToken == null) { if (cachedActiveDiscussion != null) { @@ -104,17 +143,41 @@ return null; } + // cache the token + discussionSentimentDAO.releaseFromCache(activeDiscussionToken); cachedActiveDiscussion = Map.of(System.currentTimeMillis(), activeDiscussionToken); cachedActiveDiscussionTokens.put(lessonId, cachedActiveDiscussion); return activeDiscussionToken; } private DiscussionSentimentVote stopDiscussionInternal(long lessonId) { + // remove discussion token from DB DiscussionSentimentVote currentActiveDiscussionToken = discussionSentimentDAO.getActiveDiscussion(lessonId); if (currentActiveDiscussionToken != null) { discussionSentimentDAO.delete(currentActiveDiscussionToken); } + + // remove cached discussion token + cachedActiveDiscussionTokens.remove(lessonId); + return currentActiveDiscussionToken; } + + private long getToolContentId(long toolQuestionUid) { + return discussionSentimentDAO.find(QbToolQuestion.class, toolQuestionUid).getToolContentId(); + } + + private long getLessonIdByToolContentId(long toolContentId) { + return discussionSentimentDAO.findByProperty(ToolActivity.class, "toolContentId", toolContentId).get(0) + .getLearningDesign().getLessons().iterator().next().getLessonId(); + } + + public void setDiscussionSentimentDAO(IDiscussionSentimentDAO discussionSentimentDAO) { + this.discussionSentimentDAO = discussionSentimentDAO; + } + + public void setLearnerService(ILearnerFullService learnerService) { + this.learnerService = learnerService; + } } \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/IDiscussionSentimentService.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/IDiscussionSentimentService.java (.../IDiscussionSentimentService.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/discussion/service/IDiscussionSentimentService.java (.../IDiscussionSentimentService.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -4,14 +4,15 @@ public interface IDiscussionSentimentService { - void startDiscussion(long lessonId, long toolContentId, Long burningQuestionUid); + void startDiscussionForMonitor(long toolQuestionUid, Long burningQuestionUid); - void stopDiscussion(long lessonId); + void stopDiscussionForMonitor(long toolQuestionUid); - void restartDiscussionForLearner(long lessonId, String login); + void startDiscussionForLearner(long lessonId, String login); - void addDiscussionVoteForLearner(long lessonId, int userId, int selectedOption); + boolean addDiscussionVoteForLearner(long lessonId, int userId, int selectedOption); - Map getDiscussionAggregatedVotes(long lessonId, long toolContentId, Long burningQuestionUid); + Integer getDiscussionVoteSelectedOption(long lessonId, int userId); + Map getDiscussionAggregatedVotes(long lessonId, long toolQuestionUid, Long burningQuestionUid); } \ No newline at end of file Index: lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml =================================================================== diff -u -r01c2a55767c613282c319ed8b38a0c78712e3661 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml (.../learningApplicationContext.xml) (revision 01c2a55767c613282c319ed8b38a0c78712e3661) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/learningApplicationContext.xml (.../learningApplicationContext.xml) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -132,6 +132,26 @@ + + + + + + + + + + + + + + + + PROPAGATION_REQUIRED + + + + @@ -144,8 +164,10 @@ - + + + + Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/ILearnerFullService.java =================================================================== diff -u -ra61b6ad192148c0ae514f37a9b488c3a15535ee9 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/ILearnerFullService.java (.../ILearnerFullService.java) (revision a61b6ad192148c0ae514f37a9b488c3a15535ee9) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/ILearnerFullService.java (.../ILearnerFullService.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -5,16 +5,12 @@ import java.util.Date; import java.util.List; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - import org.lamsfoundation.lams.learning.command.model.Command; import org.lamsfoundation.lams.learning.web.util.ActivityMapping; import org.lamsfoundation.lams.learningdesign.Activity; import org.lamsfoundation.lams.learningdesign.BranchingActivity; import org.lamsfoundation.lams.learningdesign.Grouping; import org.lamsfoundation.lams.learningdesign.SequenceActivity; -import org.lamsfoundation.lams.learningdesign.dao.IActivityDAO; import org.lamsfoundation.lams.learningdesign.dto.ActivityPositionDTO; import org.lamsfoundation.lams.learningdesign.dto.GateActivityDTO; import org.lamsfoundation.lams.lesson.LearnerProgress; @@ -23,7 +19,7 @@ /** * Contains methods intended for internal usage by lams_learning. - * + * * @author Andrey Balan */ public interface ILearnerFullService extends ILearnerService { @@ -60,7 +56,7 @@ * @return LearnerProgress */ LearnerProgress chooseActivity(Integer learnerId, Long lessonId, Activity activity, Boolean clearCompletedFlag); - + /** * "Complete" an activity from the web layer's perspective. Used for CompleteActivityAction and the Gate and * Grouping actions. Calls the completeActivity(Integer, Activity, Long) to actually complete the activity and @@ -128,8 +124,8 @@ * the learner who wants to go through the gate. * @param forceGate * if forceGate==true and the lesson is a preview lesson then the gate is opened straight away. - * @param key - * additional information provided by user to open gate, for example password + * @param key + * additional information provided by user to open gate, for example password * @return Updated gate details */ GateActivityDTO knockGate(Long gateActivityId, User knocker, boolean forceGate, Object key); Index: lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java =================================================================== diff -u -r89350b37c57bdf618cc90c09d792ce7d865ad4da -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 89350b37c57bdf618cc90c09d792ce7d865ad4da) +++ lams_learning/src/java/org/lamsfoundation/lams/learning/service/LearnerService.java (.../LearnerService.java) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -1443,6 +1443,7 @@ } } + @Override @SuppressWarnings("unchecked") public void createCommandForLessonLearners(Long toolContentId, String jsonCommand) { // find lesson for given tool content ID Index: lams_learning/web/WEB-INF/spring-servlet.xml =================================================================== diff -u -r4b281aa2685bb05bee4c6d7573dc785e406df5dc -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_learning/web/WEB-INF/spring-servlet.xml (.../spring-servlet.xml) (revision 4b281aa2685bb05bee4c6d7573dc785e406df5dc) +++ lams_learning/web/WEB-INF/spring-servlet.xml (.../spring-servlet.xml) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -9,6 +9,7 @@ http://www.springframework.org/schema/context/spring-context-4.3.xsd"> + Index: lams_learning/web/discussion/learner.jsp =================================================================== diff -u --- lams_learning/web/discussion/learner.jsp (revision 0) +++ lams_learning/web/discussion/learner.jsp (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -0,0 +1,149 @@ +<%@include file="/common/taglibs.jsp"%> + + + + + +
+
+ + + +
+ +
+ + + + + + + + + <%-- Stay options start from 1, Move options start from 11 --%> + + + +
+ + + +
+ + + +
+
+
\ No newline at end of file Index: lams_learning/web/discussion/startLearner.jsp =================================================================== diff -u --- lams_learning/web/discussion/startLearner.jsp (revision 0) +++ lams_learning/web/discussion/startLearner.jsp (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -0,0 +1,12 @@ +<%@include file="/common/taglibs.jsp"%> + \ No newline at end of file Index: lams_learning/web/discussion/stopLearner.jsp =================================================================== diff -u --- lams_learning/web/discussion/stopLearner.jsp (revision 0) +++ lams_learning/web/discussion/stopLearner.jsp (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -0,0 +1,9 @@ + \ No newline at end of file Index: lams_tool_assessment/conf/language/lams/ApplicationResources.properties =================================================================== diff -u -r3f1a7cbc1c960cdad9bf3cd6ca6400e8788aca68 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 3f1a7cbc1c960cdad9bf3cd6ca6400e8788aca68) +++ lams_tool_assessment/conf/language/lams/ApplicationResources.properties (.../ApplicationResources.properties) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -437,3 +437,5 @@ label.monitoring.leader.not.changed = Leader was not changed label.export.overall.summary = Overall summary label.monitoring.summary.time.limit.expired = Expired +label.monitoring.discussion.start = Start Discussion +label.monitoring.discussion.stop = Stop \ No newline at end of file Index: lams_tool_assessment/web/WEB-INF/tags/Page.tag =================================================================== diff -u -r973a921a8f59a78026f54c84d6b04e2378bac517 -r4f526e3455fe0ed91e6a838c833f1cd916844b98 --- lams_tool_assessment/web/WEB-INF/tags/Page.tag (.../Page.tag) (revision 973a921a8f59a78026f54c84d6b04e2378bac517) +++ lams_tool_assessment/web/WEB-INF/tags/Page.tag (.../Page.tag) (revision 4f526e3455fe0ed91e6a838c833f1cd916844b98) @@ -217,22 +217,41 @@ } else { alert(command.message); } - } + // if learner's currently displayed page has hookTrigger same as in the JSON // then a function also defined on that page will run if (command.hookTrigger && command.hookTrigger == commandWebsocketHookTrigger && typeof commandWebsocketHook === 'function') { commandWebsocketHook(command.hookParams); } + if (command.redirectURL) { window.location.href = command.redirectURL; } + if (command.discussion) { + var discussionCommand = $('#discussion-sentiment-command'); + if (discussionCommand.length === 0) { + discussionCommand = $('
').attr('id', 'discussion-sentiment-command').appendTo('body'); + } + discussionCommand.load(LEARNING_URL + "discussionSentiment/" + command.discussion + ".do", { + lessonId : lessonId + }); + } + // reset ping timer clearTimeout(commandWebsocketPingTimeout); commandWebsocketPingFunc(true); }; + + // check if there is a running discussion; if so, a websocket command will come to display the widget + $.ajax({ + url : LEARNING_URL + "discussionSentiment/checkLearner.do", + data: { + lessonId : lessonId + } + }); } $(document).ready(function() {