Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java =================================================================== diff -u -r911eb9cfa3db76d9b0f495f76833dc82545bee9d -r55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 911eb9cfa3db76d9b0f495f76833dc82545bee9d) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/service/ScratchieServiceImpl.java (.../ScratchieServiceImpl.java) (revision 55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b) @@ -28,10 +28,8 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; -import java.util.Calendar; import java.util.Collection; import java.util.Date; -import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedHashMap; @@ -42,7 +40,6 @@ import java.util.Map; import java.util.Set; import java.util.SortedMap; -import java.util.TimeZone; import java.util.TreeSet; import org.apache.commons.lang.StringEscapeUtils; @@ -93,7 +90,6 @@ import org.lamsfoundation.lams.tool.scratchie.model.ScratchieItem; import org.lamsfoundation.lams.tool.scratchie.model.ScratchieSession; import org.lamsfoundation.lams.tool.scratchie.model.ScratchieUser; -import org.lamsfoundation.lams.tool.scratchie.util.FinishScratchingJob; import org.lamsfoundation.lams.tool.scratchie.util.ScratchieAnswerComparator; import org.lamsfoundation.lams.tool.scratchie.util.ScratchieItemComparator; import org.lamsfoundation.lams.tool.scratchie.util.ScratchieToolContentHandler; @@ -106,12 +102,8 @@ import org.lamsfoundation.lams.util.MessageService; import org.lamsfoundation.lams.util.NumberUtil; import org.lamsfoundation.lams.util.audit.IAuditService; -import org.quartz.JobBuilder; -import org.quartz.JobDetail; import org.quartz.Scheduler; import org.quartz.SchedulerException; -import org.quartz.Trigger; -import org.quartz.TriggerBuilder; /** * @author Andrey Balan @@ -162,7 +154,7 @@ private IEventNotificationService eventNotificationService; private ScratchieOutputFactory scratchieOutputFactory; - + private Scheduler scheduler; // ******************************************************************************* @@ -271,46 +263,27 @@ } return leader; } - + @Override public void launchTimeLimit(Long sessionId) throws SchedulerException { ScratchieSession session = getScratchieSessionBySessionId(sessionId); int timeLimit = session.getScratchie().getTimeLimit(); if (timeLimit == 0) { return; } - + //store timeLimitLaunchedDate into DB Date timeLimitLaunchedDate = new Date(); session.setTimeLimitLaunchedDate(timeLimitLaunchedDate); scratchieSessionDao.saveObject(session); - - //calculate timeLimit finish date - Calendar timeLimitFinishDate = new GregorianCalendar(TimeZone.getDefault()); - timeLimitFinishDate.setTime(timeLimitLaunchedDate); - timeLimitFinishDate.add(Calendar.MINUTE, timeLimit); - //adding 5 extra seconds to let leader auto-submit results and store them in DB - timeLimitFinishDate.add(Calendar.SECOND, 5); - - //start quartz job to notify non-leaders time is over - JobDetail finishScratchingJob = JobBuilder.newJob(FinishScratchingJob.class) - .withIdentity("finishScratching:" + sessionId) - .withDescription("Group name: " + session.getSessionName()) - .usingJobData("toolSessionId", sessionId).build(); - - Trigger fnishScratchingTrigger = TriggerBuilder.newTrigger() - .withIdentity("fnishScratchingTrigger:" + sessionId) - .startAt(timeLimitFinishDate.getTime()).build(); - - scheduler.scheduleJob(finishScratchingJob, fnishScratchingTrigger); } - + @Override public boolean isWaitingForLeaderToSubmit(ScratchieSession toolSession) { Long toolSessionId = toolSession.getSessionId(); Scratchie scratchie = toolSession.getScratchie(); ScratchieUser groupLeader = toolSession.getGroupLeader(); - + boolean isReflectOnActivity = scratchie.isReflectOnActivity(); // get notebook entry NotebookEntry notebookEntry = null; @@ -325,7 +298,7 @@ boolean isWaitingForLeaderToSubmitNotebook = isReflectOnActivity && (notebookEntry == null); boolean isWaitingForLeaderToSubmitBurningQuestions = scratchie.isBurningQuestionsEnabled() && ((burningQuestions == null) || burningQuestions.isEmpty()) && !toolSession.isSessionFinished(); - + return isWaitingForLeaderToSubmitNotebook || isWaitingForLeaderToSubmitBurningQuestions; } @@ -433,7 +406,7 @@ List deletedItems) { // create list of modified questions - List modifiedItems = new ArrayList(); + List modifiedItems = new ArrayList<>(); for (ScratchieItem oldItem : oldItems) { for (ScratchieItem newItem : newItems) { if (oldItem.getUid().equals(newItem.getUid())) { @@ -471,7 +444,7 @@ List sessionList = scratchieSessionDao.getByContentId(scratchie.getContentId()); for (ScratchieSession session : sessionList) { Long toolSessionId = session.getSessionId(); - List visitLogsToDelete = new ArrayList(); + List visitLogsToDelete = new ArrayList<>(); boolean isRecalculateMarks = false; // remove all scratches for modified and removed items @@ -575,7 +548,7 @@ @Override public Set getAllLeaders(Long contentId) { - Set leaders = new TreeSet(); + Set leaders = new TreeSet<>(); List sessionList = scratchieSessionDao.getByContentId(contentId); for (ScratchieSession session : sessionList) { ScratchieUser leader = session.getGroupLeader(); @@ -598,7 +571,7 @@ @Override public List getMonitoringSummary(Long contentId, boolean isIncludeOnlyLeaders) { - List groupSummaryList = new ArrayList(); + List groupSummaryList = new ArrayList<>(); List sessions = scratchieSessionDao.getByContentId(contentId); for (ScratchieSession session : sessions) { @@ -611,7 +584,7 @@ groupSummary.setTotalAttempts(totalAttempts); List sessionUsers = scratchieUserDao.getBySessionID(sessionId); - List usersToShow = new LinkedList(); + List usersToShow = new LinkedList<>(); for (ScratchieUser user : sessionUsers) { boolean isUserGroupLeader = session.isUserGroupLeader(user.getUid()); @@ -654,7 +627,7 @@ public Collection getItemsWithIndicatedScratches(Long toolSessionId) { Scratchie scratchie = this.getScratchieBySessionId(toolSessionId); - Set items = new TreeSet(new ScratchieItemComparator()); + Set items = new TreeSet<>(new ScratchieItemComparator()); items.addAll(scratchie.getScratchieItems()); return getItemsWithIndicatedScratches(toolSessionId, items); @@ -769,7 +742,7 @@ @Override public List getQuestionSummary(Long contentId, Long itemUid) { - List groupSummaryList = new ArrayList(); + List groupSummaryList = new ArrayList<>(); ScratchieItem item = scratchieItemDao.getByUid(itemUid); Collection answers = item.getAnswers(); @@ -780,7 +753,7 @@ // one new summary for one session. GroupSummary groupSummary = new GroupSummary(session); - Map answerMap = new HashMap(); + Map answerMap = new HashMap<>(); for (ScratchieAnswer dbAnswer : (Set) answers) { // clone it so it doesn't interfere with values from other sessions @@ -807,7 +780,7 @@ } } - Collection sortedAnswers = new TreeSet(new ScratchieAnswerComparator()); + Collection sortedAnswers = new TreeSet<>(new ScratchieAnswerComparator()); sortedAnswers.addAll(answerMap.values()); groupSummary.setAnswers(sortedAnswers); groupSummaryList.add(groupSummary); @@ -820,7 +793,7 @@ groupSummaryTotal.setSessionName("Summary"); groupSummaryTotal.setMark(0); - Map answerMapTotal = new HashMap(); + Map answerMapTotal = new HashMap<>(); for (ScratchieAnswer dbAnswer : (Set) answers) { // clone it so it doesn't interfere with values from other sessions ScratchieAnswer answer = (ScratchieAnswer) dbAnswer.clone(); @@ -842,7 +815,7 @@ } } - Collection sortedAnswers = new TreeSet(new ScratchieAnswerComparator()); + Collection sortedAnswers = new TreeSet<>(new ScratchieAnswerComparator()); sortedAnswers.addAll(answerMapTotal.values()); groupSummaryTotal.setAnswers(sortedAnswers); groupSummaryList.add(0, groupSummaryTotal); @@ -854,17 +827,17 @@ @Override public List getBurningQuestionDtos(Scratchie scratchie, Long sessionId) { - Set items = new TreeSet(new ScratchieItemComparator()); + Set items = new TreeSet<>(new ScratchieItemComparator()); items.addAll(scratchie.getScratchieItems()); List burningQuestionDtos = scratchieBurningQuestionDao .getBurningQuestionsByContentId(scratchie.getUid(), sessionId); //in order to group BurningQuestions by items, organise them as a list of BurningQuestionItemDTOs - List burningQuestionItemDtos = new ArrayList(); + List burningQuestionItemDtos = new ArrayList<>(); for (ScratchieItem item : items) { - List burningQuestionDtosOfSpecifiedItem = new ArrayList(); + List burningQuestionDtosOfSpecifiedItem = new ArrayList<>(); for (BurningQuestionDTO burningQuestionDto : burningQuestionDtos) { ScratchieBurningQuestion burningQuestion = burningQuestionDto.getBurningQuestion(); @@ -889,7 +862,7 @@ final String generalQuestionMessage = messageService.getMessage("label.general.burning.question"); generalDummyItem.setTitle(generalQuestionMessage); generalBurningQuestionItemDto.setScratchieItem(generalDummyItem); - List burningQuestionDtosOfSpecifiedItem = new ArrayList(); + List burningQuestionDtosOfSpecifiedItem = new ArrayList<>(); for (BurningQuestionDTO burningQuestionDto : burningQuestionDtos) { ScratchieBurningQuestion burningQuestion = burningQuestionDto.getBurningQuestion(); @@ -927,7 +900,7 @@ @Override public List getReflectionList(Long contentId) { - ArrayList reflections = new ArrayList(); + ArrayList reflections = new ArrayList<>(); // get all available leaders associated with this content as only leaders have reflections List sessionList = scratchieSessionDao.getByContentId(contentId); @@ -986,16 +959,16 @@ @Override public LinkedHashMap exportExcel(Long contentId) { Scratchie scratchie = scratchieDao.getByContentId(contentId); - Collection items = new TreeSet(new ScratchieItemComparator()); + Collection items = new TreeSet<>(new ScratchieItemComparator()); items.addAll(scratchie.getScratchieItems()); int numberOfItems = items.size(); - LinkedHashMap dataToExport = new LinkedHashMap(); + LinkedHashMap dataToExport = new LinkedHashMap<>(); // ======================================================= For Immediate Analysis page // ======================================= - List rowList = new LinkedList(); + List rowList = new LinkedList<>(); ExcelCell[] row = new ExcelCell[1]; row[0] = new ExcelCell(getMessage("label.quick.analysis"), true); @@ -1057,7 +1030,7 @@ // ======================================================= For Report by Team TRA page // ======================================= - rowList = new LinkedList(); + rowList = new LinkedList<>(); row = new ExcelCell[1]; row[0] = new ExcelCell(getMessage("label.quick.analysis"), true); @@ -1168,7 +1141,7 @@ // ======================================= // all rows - rowList = new LinkedList(); + rowList = new LinkedList<>(); // Caption row = new ExcelCell[2]; @@ -1412,7 +1385,7 @@ // ======================================================= For_XLS_export(SPSS analysis) page // ======================================= - rowList = new LinkedList(); + rowList = new LinkedList<>(); // Table header------------------------------------ @@ -1501,7 +1474,7 @@ List logs = scratchieAnswerVisitDao.getLogsBySessionAndItem(sessionId, item.getUid()); if (logs == null) { - logs = new ArrayList(); + logs = new ArrayList<>(); } for (ScratchieAnswerVisitLog log : logs) { @@ -1549,7 +1522,7 @@ // ======================================= if (scratchie.isBurningQuestionsEnabled()) { - rowList = new LinkedList(); + rowList = new LinkedList<>(); row = new ExcelCell[1]; row[0] = new ExcelCell(getMessage("label.burning.questions"), true); @@ -1597,11 +1570,17 @@ public LeaderResultsDTO getLeaderResultsDTOForLeaders(Long contentId) { LeaderResultsDTO newDto = new LeaderResultsDTO(contentId); Object[] markStats = scratchieSessionDao.getStatsMarksForLeaders(contentId); - if ( markStats != null ) { - newDto.setMinMark(markStats[0] != null ? NumberUtil.formatLocalisedNumber((Float)markStats[0], (Locale)null, 2) : "0.00"); - newDto.setAvgMark(markStats[1] != null ? NumberUtil.formatLocalisedNumber((Float)markStats[1], (Locale)null, 2) : "0.00"); - newDto.setMaxMark(markStats[2] != null ? NumberUtil.formatLocalisedNumber((Float)markStats[2], (Locale)null, 2) : "0.00"); - newDto.setNumberGroupsLeaderFinished((Integer)markStats[3]); + if (markStats != null) { + newDto.setMinMark( + markStats[0] != null ? NumberUtil.formatLocalisedNumber((Float) markStats[0], (Locale) null, 2) + : "0.00"); + newDto.setAvgMark( + markStats[1] != null ? NumberUtil.formatLocalisedNumber((Float) markStats[1], (Locale) null, 2) + : "0.00"); + newDto.setMaxMark( + markStats[2] != null ? NumberUtil.formatLocalisedNumber((Float) markStats[2], (Locale) null, 2) + : "0.00"); + newDto.setNumberGroupsLeaderFinished((Integer) markStats[3]); } return newDto; } @@ -1621,7 +1600,7 @@ * Serves merely for excel export purposes. Produces data for "Summary By Team" section. */ private List getSummaryByTeam(Scratchie scratchie, Collection sortedItems) { - List groupSummaries = new ArrayList(); + List groupSummaries = new ArrayList<>(); String presetMarks = getConfigItem(ScratchieConfigItem.KEY_PRESET_MARKS).getConfigValue(); @@ -1630,7 +1609,7 @@ Long sessionId = session.getSessionId(); // one new summary for one session. GroupSummary groupSummary = new GroupSummary(session); - ArrayList items = new ArrayList(); + ArrayList items = new ArrayList<>(); ScratchieUser groupLeader = session.getGroupLeader(); @@ -1715,10 +1694,10 @@ public boolean isGroupedActivity(long toolContentID) { return toolService.isGroupedActivity(toolContentID); } - + @Override public void auditLogStartEditingActivityInMonitor(long toolContentID) { - toolService.auditLogStartEditingActivityInMonitor(toolContentID); + toolService.auditLogStartEditingActivityInMonitor(toolContentID); } // ***************************************************************************** @@ -2041,10 +2020,10 @@ public ToolOutput getToolOutput(String name, Long toolSessionId, Long learnerId) { return getScratchieOutputFactory().getToolOutput(name, this, toolSessionId, learnerId); } - + @Override public List getToolOutputs(String name, Long toolContentId) { - return new ArrayList(); + return new ArrayList<>(); } @Override @@ -2137,7 +2116,7 @@ public void setScratchieOutputFactory(ScratchieOutputFactory scratchieOutputFactory) { this.scratchieOutputFactory = scratchieOutputFactory; } - + /** * @param scheduler * The scheduler to set. @@ -2188,7 +2167,7 @@ scratchie.setReflectInstructions(JsonUtil.opt(toolContentJSON, RestTags.REFLECT_INSTRUCTIONS, (String) null)); // Scratchie Items - Set newItems = new LinkedHashSet(); + Set newItems = new LinkedHashSet<>(); JSONArray questions = toolContentJSON.getJSONArray(RestTags.QUESTIONS); for (int i = 0; i < questions.length(); i++) { @@ -2203,7 +2182,7 @@ newItems.add(item); // set options - Set newAnswers = new LinkedHashSet(); + Set newAnswers = new LinkedHashSet<>(); JSONArray answersData = questionData.getJSONArray(RestTags.ANSWERS); for (int j = 0; j < answersData.length(); j++) { Fisheye: Tag 55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b refers to a dead (removed) revision in file `lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/util/FinishScratchingJob.java'. Fisheye: No comparison available. Pass `N' to diff? Index: lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/LearningWebsocketServer.java =================================================================== diff -u -r2bc7ead1354db2ac929b07aad9ae32e82c0b7456 -r55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b --- lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 2bc7ead1354db2ac929b07aad9ae32e82c0b7456) +++ lams_tool_scratchie/src/java/org/lamsfoundation/lams/tool/scratchie/web/action/LearningWebsocketServer.java (.../LearningWebsocketServer.java) (revision 55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b) @@ -1,11 +1,14 @@ package org.lamsfoundation.lams.tool.scratchie.web.action; import java.io.IOException; +import java.util.Calendar; import java.util.Collection; +import java.util.GregorianCalendar; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; +import java.util.TimeZone; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; @@ -46,7 +49,6 @@ @Override public void run() { - while (!stopFlag) { try { // websocket communication bypasses standard HTTP filters, so Hibernate session needs to be initialised manually @@ -67,22 +69,42 @@ ScratchieSession toolSession = LearningWebsocketServer.getScratchieService() .getScratchieSessionBySessionId(toolSessionId); - if (toolSession.isScratchingFinished()) { + boolean timeLimitUp = false; + if (toolSession.getTimeLimitLaunchedDate() != null) { + if (LearningWebsocketServer.cache.get(toolSessionId) == null) { + timeLimitUp = true; + } else { + Calendar currentTime = new GregorianCalendar(TimeZone.getDefault()); + Calendar timeLimitFinishDate = new GregorianCalendar(TimeZone.getDefault()); + timeLimitFinishDate.setTime(toolSession.getTimeLimitLaunchedDate()); + timeLimitFinishDate.add(Calendar.MINUTE, toolSession.getScratchie().getTimeLimit()); + //adding 5 extra seconds to let leader auto-submit results and store them in DB + timeLimitFinishDate.add(Calendar.SECOND, 5); + if (timeLimitFinishDate.compareTo(currentTime) <= 0) { + if (!toolSession.isScratchingFinished()) { + toolSession.setScratchingFinished(true); + LearningWebsocketServer.getScratchieService() + .saveOrUpdateScratchieSession(toolSession); + } + LearningWebsocketServer.cache.remove(toolSessionId); + timeLimitUp = true; + } + } + } + + if (timeLimitUp) { boolean isWaitingForLeaderToSubmit = LearningWebsocketServer.getScratchieService() .isWaitingForLeaderToSubmit(toolSession); - if (!isWaitingForLeaderToSubmit) { - // this should make all websockets close on client side, - // so next run will not happen + if (isWaitingForLeaderToSubmit) { + LearningWebsocketServer.sendPageRefreshRequest(toolSessionId); + } else { LearningWebsocketServer.sendCloseRequest(toolSessionId); } - continue; + } else if (toolSession.isScratchingFinished()) { + LearningWebsocketServer.sendCloseRequest(toolSessionId); + } else { + SendWorker.send(toolSessionId); } - - try { - send(toolSessionId); - } catch (JSONException e) { - LearningWebsocketServer.log.error("Error while building Scratchie answer JSON", e); - } } } catch (Exception e) { // error caught, but carry on @@ -103,12 +125,12 @@ * Feeds websockets with scratched answers. */ @SuppressWarnings("unchecked") - private void send(Long toolSessionId) throws JSONException, IOException { + private static void send(Long toolSessionId) throws JSONException, IOException { JSONObject responseJSON = new JSONObject(); Collection items = LearningWebsocketServer.getScratchieService() .getItemsWithIndicatedScratches(toolSessionId); - Map> sessionCache = null; + Map> sessionCache = LearningWebsocketServer.cache.get(toolSessionId); for (ScratchieItem item : items) { Long itemUid = item.getUid(); // do not init variables below until it's really needed @@ -118,14 +140,6 @@ if (answer.isScratched()) { // answer is scratched, check if it is present in cache if (itemCache == null) { - // init required cache variables - if (sessionCache == null) { - sessionCache = LearningWebsocketServer.cache.get(toolSessionId); - if (sessionCache == null) { - sessionCache = new TreeMap<>(); - LearningWebsocketServer.cache.put(toolSessionId, sessionCache); - } - } itemCache = sessionCache.get(itemUid); if (itemCache == null) { itemCache = new TreeMap<>(); @@ -190,6 +204,9 @@ if (sessionWebsockets == null) { sessionWebsockets = ConcurrentHashMap.newKeySet(); LearningWebsocketServer.websockets.put(toolSessionId, sessionWebsockets); + + Map> sessionCache = new TreeMap<>(); + LearningWebsocketServer.cache.put(toolSessionId, sessionCache); } sessionWebsockets.add(websocket); @@ -241,6 +258,27 @@ } } + /** + * The time limit is expired but leader hasn't submitted required notebook/burning questions yet. Non-leaders + * will need to refresh the page in order to stop showing them questions page. + */ + public static void sendPageRefreshRequest(Long toolSessionId) throws JSONException, IOException { + Set sessionWebsockets = LearningWebsocketServer.websockets.get(toolSessionId); + if (sessionWebsockets == null) { + return; + } + + JSONObject responseJSON = new JSONObject(); + responseJSON.put("pageRefresh", true); + String response = responseJSON.toString(); + + for (Session websocket : sessionWebsockets) { + if (websocket.isOpen()) { + websocket.getBasicRemote().sendText(response); + } + } + } + private static IScratchieService getScratchieService() { if (LearningWebsocketServer.scratchieService == null) { LearningWebsocketServer.scratchieService = ScratchieServiceProxy Index: lams_tool_scratchie/web/pages/learning/questionlist.jsp =================================================================== diff -u -r3fd4509df504512bd71ae707ac5988eaca7b8bca -r55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b --- lams_tool_scratchie/web/pages/learning/questionlist.jsp (.../questionlist.jsp) (revision 3fd4509df504512bd71ae707ac5988eaca7b8bca) +++ lams_tool_scratchie/web/pages/learning/questionlist.jsp (.../questionlist.jsp) (revision 55ce2e8e7dc351b8d21dcc2c0aa4b79aadc3d73b) @@ -65,13 +65,19 @@ // create JSON object var input = JSON.parse(e.data); + //time limit is expired but leader hasn't submitted required notebook/burning questions yet. Non-leaders + //will need to refresh the page in order to stop showing them questions page. + if (input.pageRefresh) { + location.reload(); + return; + } + // reset ping timer clearTimeout(scratchieWebsocketPingTimeout); scratchieWebsocketPingFunc(true); // leader finished the activity if (input.close) { - scratchieWebsocket.close(); $('#finishButton').show(); return; }