diff --git a/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java b/src/main/java/de/thm/arsnova/controller/LecturerQuestionController.java index 07f17995a9d96dc555b0a8c19554097ace260f32..cf15ea4701086bac55eca615846c7deaf94ab186 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 5d39e3997558d105ffe1c1205495867a4f99733a..d96e40945f57a66b328b94b32995443b345fc787 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 d2936f57a0aa975df3fa09a353a23631982c2584..23432273b8a577397e7fdf4e8fddf3a5df046fd2 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 956512a627dfbbf38b1ceac3b98144815d83bca8..3937693761928a6f90f0dd4e22d2ce953b0cd9e9 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 c1eb3e68f7988f399457211ca87727073dff2ff7..f8a2a76d4436c61614e5b2b25d209df2e4775e91 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 0000000000000000000000000000000000000000..9329cdfa7e25ba1826cd3ecefb4d45de6d49ab9d --- /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 0000000000000000000000000000000000000000..3d064b092d7f3d8e5cdd9c144e68fdf83231031a --- /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 457b9fc7b2d9e9af938e5ee3011bb4b583181e2e..8b440166ac43494b358b4073d411b61f9343c8bc 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 9db7d05d5fb508420e6d74f257a954a6e54720c3..535bd5a339388395bc1e44ffdd6ac7f1776d7a09 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 f8d7b6a580d879189a833945f56fc0176f37a99e..1764f9f21f796b811fbd783ef6ac2f02dd6215e0 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