From e9160e808bb2bfcd9a894cf5cddfa5447557ee73 Mon Sep 17 00:00:00 2001 From: agrt56 <andreas.gaertner@mni.thm.de> Date: Wed, 11 Mar 2015 14:25:28 +0100 Subject: [PATCH] Added implementation for delayed pi-round change. Task #15346 The delayed pi-round change can now be triggered via a simple api call. After the api call a timed task will be started, which executes the necessary steps for a round change. --- .../LecturerQuestionController.java | 13 ++++ .../java/de/thm/arsnova/dao/CacheBuster.java | 8 +++ .../domain/LearningProgressFactory.java | 8 +++ .../de/thm/arsnova/entities/Question.java | 28 ++++++++ .../thm/arsnova/events/NovaEventVisitor.java | 4 ++ .../events/PiRoundDelayedStartEvent.java | 71 +++++++++++++++++++ .../thm/arsnova/events/PiRoundEndEvent.java | 43 +++++++++++ .../arsnova/services/IQuestionService.java | 8 +++ .../thm/arsnova/services/QuestionService.java | 70 +++++++++++++++++- .../arsnova/socket/ARSnovaSocketIOServer.java | 18 +++++ 10 files changed, 270 insertions(+), 1 deletion(-) create mode 100644 src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java create mode 100644 src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java index 07f17995a..cf15ea470 100644 --- a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java +++ b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java @@ -84,6 +84,19 @@ public class LecturerQuestionController extends AbstractController { } } + @RequestMapping(value = "/{questionId}/startNewPiRound", method = RequestMethod.GET) + public void startSecondPiRound( + @PathVariable final String questionId, + @RequestParam(value = "time", defaultValue = "0", required = false) final int time + ) { + + if(time == 0) { + questionService.startNewPiRound(questionId, null); + } else { + questionService.startNewPiRoundDelayed(questionId, time); + } + } + @RequestMapping(value = "/{questionId}/publish", method = RequestMethod.POST) public void publishQuestion( @PathVariable final String questionId, diff --git a/src/main/java/de/thm/arsnova/dao/CacheBuster.java b/src/main/java/de/thm/arsnova/dao/CacheBuster.java index 5d39e3997..d96e40945 100644 --- a/src/main/java/de/thm/arsnova/dao/CacheBuster.java +++ b/src/main/java/de/thm/arsnova/dao/CacheBuster.java @@ -33,6 +33,8 @@ import de.thm.arsnova.events.NewFeedbackEvent; import de.thm.arsnova.events.NewInterposedQuestionEvent; import de.thm.arsnova.events.NewQuestionEvent; import de.thm.arsnova.events.NovaEventVisitor; +import de.thm.arsnova.events.PiRoundDelayedStartEvent; +import de.thm.arsnova.events.PiRoundEndEvent; import de.thm.arsnova.events.StatusSessionEvent; /** @@ -81,4 +83,10 @@ public class CacheBuster implements ICacheBuster, NovaEventVisitor { @Override public void visit(ChangeLearningProgress changeLearningProgress) {} + + @Override + public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) {} + + @Override + public void visit(PiRoundEndEvent piRoundEndEvent) {} } diff --git a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java index d2936f57a..23432273b 100644 --- a/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java +++ b/src/main/java/de/thm/arsnova/domain/LearningProgressFactory.java @@ -37,6 +37,8 @@ import de.thm.arsnova.events.NewFeedbackEvent; import de.thm.arsnova.events.NewInterposedQuestionEvent; import de.thm.arsnova.events.NewQuestionEvent; import de.thm.arsnova.events.NovaEventVisitor; +import de.thm.arsnova.events.PiRoundDelayedStartEvent; +import de.thm.arsnova.events.PiRoundEndEvent; import de.thm.arsnova.events.StatusSessionEvent; @Component @@ -121,4 +123,10 @@ public class LearningProgressFactory implements NovaEventVisitor, ILearningProgr this.publisher = publisher; } + @Override + public void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent) {} + + @Override + public void visit(PiRoundEndEvent piRoundEndEvent) {} + } diff --git a/src/main/java/de/thm/arsnova/entities/Question.java b/src/main/java/de/thm/arsnova/entities/Question.java index 956512a62..393769376 100644 --- a/src/main/java/de/thm/arsnova/entities/Question.java +++ b/src/main/java/de/thm/arsnova/entities/Question.java @@ -17,6 +17,7 @@ */ package de.thm.arsnova.entities; +import java.util.HashMap; import java.util.List; public class Question { @@ -38,6 +39,9 @@ public class Question { private int number; private int duration; private int piRound; + private long piRoundEndTime; + private long piRoundStartTime; + private boolean piRoundActive; private boolean showStatistic; // sic private boolean showAnswer; private boolean abstention; @@ -195,6 +199,30 @@ public class Question { this.piRound = piRound; } + public long getPiRoundEndTime() { + return piRoundEndTime; + } + + public void setPiRoundEndTime(long piRoundEndTime) { + this.piRoundEndTime = piRoundEndTime; + } + + public long getPiRoundStartTime() { + return piRoundStartTime; + } + + public void setPiRoundStartTime(long piRoundStartTime) { + this.piRoundStartTime = piRoundStartTime; + } + + public boolean isPiRoundActive() { + return piRoundActive; + } + + public void setPiRoundActive(boolean piRoundActive) { + this.piRoundActive = piRoundActive; + } + public boolean isShowStatistic() { return showStatistic; } diff --git a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java b/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java index c1eb3e68f..f8a2a76d4 100644 --- a/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java +++ b/src/main/java/de/thm/arsnova/events/NovaEventVisitor.java @@ -45,4 +45,8 @@ public interface NovaEventVisitor { void visit(ChangeLearningProgress changeLearningProgress); + void visit(PiRoundDelayedStartEvent piRoundDelayedStartEvent); + + void visit(PiRoundEndEvent piRoundEndEvent); + } diff --git a/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java new file mode 100644 index 000000000..9329cdfa7 --- /dev/null +++ b/src/main/java/de/thm/arsnova/events/PiRoundDelayedStartEvent.java @@ -0,0 +1,71 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ARSnova Backend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.events; + +import java.util.Date; +import java.util.HashMap; + +import de.thm.arsnova.entities.Question; +import de.thm.arsnova.entities.Session; + +public class PiRoundDelayedStartEvent extends SessionEvent { + + private static final long serialVersionUID = 1L; + + private final String questionId; + + private final Long startTime; + + private final Long endTime; + + public PiRoundDelayedStartEvent(Object source, Session session, Question question) { + super(source, session); + this.questionId = question.get_id(); + this.startTime = question.getPiRoundStartTime(); + this.endTime = question.getPiRoundEndTime(); + } + + @Override + public void accept(NovaEventVisitor visitor) { + visitor.visit(this); + } + + public String getQuestionId() { + return questionId; + } + + public Long getStartTime() { + return startTime; + } + + public Long getEndTime() { + return endTime; + } + + public HashMap<String, String> getPiRoundInformations() { + HashMap<String, String> map = new HashMap<String, String>(); + + map.put("id", getQuestionId()); + map.put("endTime", getEndTime().toString()); + map.put("startTime", getStartTime().toString()); + map.put("actualTime", String.valueOf(new Date().getTime())); + + return map; + } + +} diff --git a/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java new file mode 100644 index 000000000..3d064b092 --- /dev/null +++ b/src/main/java/de/thm/arsnova/events/PiRoundEndEvent.java @@ -0,0 +1,43 @@ +/* + * This file is part of ARSnova Backend. + * Copyright (C) 2012-2015 The ARSnova Team + * + * ARSnova Backend is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * ARSnova Backend is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ +package de.thm.arsnova.events; + +import de.thm.arsnova.entities.Question; +import de.thm.arsnova.entities.Session; + +public class PiRoundEndEvent extends SessionEvent { + + private static final long serialVersionUID = 1L; + + private final String questionId; + + public PiRoundEndEvent(Object source, Session session, Question question) { + super(source, session); + questionId = question.get_id(); + } + + @Override + public void accept(NovaEventVisitor visitor) { + visitor.visit(this); + } + + public String getQuestionId() { + return questionId; + } + +} diff --git a/src/main/java/de/thm/arsnova/services/IQuestionService.java b/src/main/java/de/thm/arsnova/services/IQuestionService.java index 457b9fc7b..8b440166a 100644 --- a/src/main/java/de/thm/arsnova/services/IQuestionService.java +++ b/src/main/java/de/thm/arsnova/services/IQuestionService.java @@ -41,6 +41,12 @@ public interface IQuestionService { void deleteAllQuestions(String sessionKeyword); + void startNewPiRound(String questionId, User user); + + void startNewPiRoundDelayed(String questionId, int time); + + public void cancelDelayedPiRoundChange(final String questionId); + List<String> getUnAnsweredQuestionIds(String sessionKey); Answer getMyAnswer(String questionId); @@ -69,6 +75,8 @@ public interface IQuestionService { Question update(Question question); + Question update(Question question, User user); + void deleteAnswers(String questionId); Answer saveAnswer(String questionId, de.thm.arsnova.entities.transport.Answer answer); diff --git a/src/main/java/de/thm/arsnova/services/QuestionService.java b/src/main/java/de/thm/arsnova/services/QuestionService.java index 9db7d05d5..535bd5a33 100644 --- a/src/main/java/de/thm/arsnova/services/QuestionService.java +++ b/src/main/java/de/thm/arsnova/services/QuestionService.java @@ -21,9 +21,12 @@ import java.util.AbstractMap; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Arrays; +import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import de.thm.arsnova.exceptions.ForbiddenException; @@ -53,6 +56,8 @@ import de.thm.arsnova.events.DeleteQuestionEvent; import de.thm.arsnova.events.NewAnswerEvent; import de.thm.arsnova.events.NewInterposedQuestionEvent; import de.thm.arsnova.events.NewQuestionEvent; +import de.thm.arsnova.events.PiRoundDelayedStartEvent; +import de.thm.arsnova.events.PiRoundEndEvent; import de.thm.arsnova.exceptions.BadRequestException; import de.thm.arsnova.exceptions.NotFoundException; import de.thm.arsnova.exceptions.UnauthorizedException; @@ -73,6 +78,8 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis public static final Logger LOGGER = LoggerFactory.getLogger(QuestionService.class); + private HashMap<String, Timer> timerList = new HashMap<String, Timer>(); + public void setDatabaseDao(final IDatabaseDao databaseDao) { this.databaseDao = databaseDao; } @@ -190,6 +197,61 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis this.publisher.publishEvent(event); } + public void startNewPiRound(final String questionId, User user) { + if(null == user) { + user = userService.getCurrentUser(); + } + + cancelDelayedPiRoundChange(questionId); + + final Question question = databaseDao.getQuestion(questionId); + final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword()); + + question.setPiRound(question.getPiRound() + 1); + question.setActive(false); + update(question, user); + + this.publisher.publishEvent(new PiRoundEndEvent(this, session, question)); + } + + @Override + @PreAuthorize("isAuthenticated() and hasPermission(#questionId, 'question', 'owner')") + public void startNewPiRoundDelayed(final String questionId, final int time) { + final Timer timer = new Timer(); + final Date date = new Date(); + final Date endDate = new Date(date.getTime() + (time * 1000)); + final IQuestionService questionService = this; + final User user = userService.getCurrentUser(); + final Question question = databaseDao.getQuestion(questionId); + final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword()); + + cancelDelayedPiRoundChange(questionId); + + timer.schedule(new TimerTask() { + public void run() { + questionService.startNewPiRound(questionId, user); + } + }, endDate); + + timerList.put(questionId, timer); + question.setPiRoundActive(true); + question.setPiRoundStartTime(date.getTime()); + question.setPiRoundEndTime(endDate.getTime()); + update(question); + + this.publisher.publishEvent(new PiRoundDelayedStartEvent(this, session, question)); + } + + public void cancelDelayedPiRoundChange(final String questionId) { + Timer timer = timerList.get(questionId); + + if(null != timer) { + timer.cancel(); + timerList.remove(questionId); + timer.purge(); + } + } + private Session getSessionWithAuthCheck(final String sessionKeyword) { final User user = userService.getCurrentUser(); final Session session = databaseDao.getSessionFromKeyword(sessionKeyword); @@ -425,12 +487,18 @@ public class QuestionService implements IQuestionService, ApplicationEventPublis @Override @PreAuthorize("isAuthenticated()") public Question update(final Question question) { + final User user = userService.getCurrentUser(); + return update(question, user); + } + + @Override + @PreAuthorize("isAuthenticated()") + public Question update(final Question question, User user) { final Question oldQuestion = databaseDao.getQuestion(question.get_id()); if (null == oldQuestion) { throw new NotFoundException(); } - final User user = userService.getCurrentUser(); final Session session = databaseDao.getSessionFromKeyword(question.getSessionKeyword()); if (user == null || session == null || !session.isCreator(user)) { throw new UnauthorizedException(); diff --git a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java index f8d7b6a58..1764f9f21 100644 --- a/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java +++ b/src/main/java/de/thm/arsnova/socket/ARSnovaSocketIOServer.java @@ -62,6 +62,8 @@ import de.thm.arsnova.events.NewFeedbackEvent; import de.thm.arsnova.events.NewInterposedQuestionEvent; import de.thm.arsnova.events.NewQuestionEvent; import de.thm.arsnova.events.NovaEventVisitor; +import de.thm.arsnova.events.PiRoundDelayedStartEvent; +import de.thm.arsnova.events.PiRoundEndEvent; import de.thm.arsnova.events.StatusSessionEvent; import de.thm.arsnova.exceptions.UnauthorizedException; import de.thm.arsnova.exceptions.NoContentException; @@ -474,6 +476,22 @@ public class ARSnovaSocketIOServer implements ARSnovaSocket, NovaEventVisitor { broadcastInSession(sessionKey, "countPreparationQuestionAnswers", questionService.countPreparationQuestionAnswersInternal(sessionKey)); } + @Async + @Override + public void visit(PiRoundDelayedStartEvent event) { + final String sessionKey = event.getSession().getKeyword(); + + broadcastInSession(sessionKey, "startDelayedPiRound", event.getPiRoundInformations()); + } + + @Async + @Override + public void visit(PiRoundEndEvent event) { + final String sessionKey = event.getSession().getKeyword(); + + broadcastInSession(sessionKey, "endPiRound", event.getQuestionId()); + } + @Override public void visit(DeleteQuestionEvent deleteQuestionEvent) { // TODO Auto-generated method stub -- GitLab