SEBSERV-204 added collecting room restrictions (only one open at a time)
This commit is contained in:
parent
e88f5146ab
commit
181fb6d95e
10 changed files with 131 additions and 30 deletions
|
@ -22,8 +22,10 @@ import ch.ethz.seb.sebserver.gbl.util.Utils;
|
|||
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||
public class RemoteProctoringRoom {
|
||||
|
||||
public static final String ATTR_IS_OPEN = "isOpen";
|
||||
|
||||
public static final RemoteProctoringRoom NULL_ROOM = new RemoteProctoringRoom(
|
||||
null, null, null, null, null, false, null, null, null);
|
||||
null, null, null, null, null, false, null, null, null, false);
|
||||
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID)
|
||||
public final Long id;
|
||||
|
@ -52,6 +54,9 @@ public class RemoteProctoringRoom {
|
|||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ROOM_DATA)
|
||||
public final String additionalRoomData;
|
||||
|
||||
@JsonProperty(ATTR_IS_OPEN)
|
||||
public final Boolean isOpen;
|
||||
|
||||
@JsonCreator
|
||||
public RemoteProctoringRoom(
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ID) final Long id,
|
||||
|
@ -62,7 +67,8 @@ public class RemoteProctoringRoom {
|
|||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_TOWNHALL_ROOM) final Boolean townhallRoom,
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_BREAK_OUT_CONNECTIONS) final Collection<String> breakOutConnections,
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_JOIN_KEY) final CharSequence joinKey,
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ROOM_DATA) final String additionalRoomData) {
|
||||
@JsonProperty(Domain.REMOTE_PROCTORING_ROOM.ATTR_ROOM_DATA) final String additionalRoomData,
|
||||
@JsonProperty(ATTR_IS_OPEN) final Boolean isOpen) {
|
||||
|
||||
this.id = id;
|
||||
this.examId = examId;
|
||||
|
@ -73,6 +79,7 @@ public class RemoteProctoringRoom {
|
|||
this.breakOutConnections = Utils.immutableCollectionOf(breakOutConnections);
|
||||
this.joinKey = joinKey;
|
||||
this.additionalRoomData = additionalRoomData;
|
||||
this.isOpen = isOpen;
|
||||
}
|
||||
|
||||
public Long getId() {
|
||||
|
@ -111,6 +118,10 @@ public class RemoteProctoringRoom {
|
|||
return this.additionalRoomData;
|
||||
}
|
||||
|
||||
public Boolean getIsOpen() {
|
||||
return this.isOpen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
final StringBuilder builder = new StringBuilder();
|
||||
|
@ -128,6 +139,12 @@ public class RemoteProctoringRoom {
|
|||
builder.append(this.townhallRoom);
|
||||
builder.append(", breakOutConnections=");
|
||||
builder.append(this.breakOutConnections);
|
||||
builder.append(", joinKey=");
|
||||
builder.append(this.joinKey);
|
||||
builder.append(", additionalRoomData=");
|
||||
builder.append(this.additionalRoomData);
|
||||
builder.append(", isOpen=");
|
||||
builder.append(this.isOpen);
|
||||
builder.append("]");
|
||||
return builder.toString();
|
||||
}
|
||||
|
|
|
@ -354,7 +354,7 @@ public final class ClientConnectionTable {
|
|||
data.getConnectionId(),
|
||||
UpdatableTableItem::new);
|
||||
tableItem.push(data);
|
||||
if (this.statusFilterChanged || this.forceUpdateAll) {
|
||||
if (this.statusFilterChanged || this.forceUpdateAll || needsSync) {
|
||||
this.toDelete.remove(data.getConnectionId());
|
||||
}
|
||||
});
|
||||
|
|
|
@ -201,14 +201,11 @@ public class MonitoringProctoringService {
|
|||
final PageAction action =
|
||||
actionBuilder.newAction(ActionDefinition.MONITOR_EXAM_VIEW_PROCTOR_ROOM)
|
||||
.withEntityKey(entityKey)
|
||||
.withExec(_action -> {
|
||||
final int actualRoomSize = proctoringGUIService
|
||||
.getActualCollectingRoomSize(room.name);
|
||||
if (actualRoomSize <= 0) {
|
||||
return _action;
|
||||
}
|
||||
return openExamProctoringRoom(proctoringSettings, room, _action);
|
||||
})
|
||||
.withExec(_action -> openExamProctoringRoom(
|
||||
proctoringGUIService,
|
||||
proctoringSettings,
|
||||
room,
|
||||
_action))
|
||||
.withNameAttributes(
|
||||
room.subject,
|
||||
room.roomSize,
|
||||
|
@ -229,6 +226,7 @@ public class MonitoringProctoringService {
|
|||
.withParentEntityKey(entityKey);
|
||||
this.proctorRoomConnectionsPopup.show(pc, collectingRoom.subject);
|
||||
}));
|
||||
|
||||
processProctorRoomActionActivation(
|
||||
proctoringGUIService.getCollectingRoomActionItem(room.name),
|
||||
room, pageContext);
|
||||
|
@ -239,10 +237,15 @@ public class MonitoringProctoringService {
|
|||
}
|
||||
|
||||
private PageAction openExamProctoringRoom(
|
||||
final ProctoringGUIService proctoringGUIService,
|
||||
final ProctoringServiceSettings proctoringSettings,
|
||||
final RemoteProctoringRoom room,
|
||||
final PageAction action) {
|
||||
|
||||
if (!proctoringGUIService.isCollectingRoomEnabled(room.name)) {
|
||||
return action;
|
||||
}
|
||||
|
||||
final ProctoringRoomConnection proctoringConnectionData = this.pageService
|
||||
.getRestService()
|
||||
.getBuilder(GetProctorRoomConnection.class)
|
||||
|
@ -437,13 +440,16 @@ public class MonitoringProctoringService {
|
|||
final PageContext pageContext) {
|
||||
|
||||
try {
|
||||
|
||||
final boolean active = room.roomSize > 0 && !room.isOpen;
|
||||
final Display display = pageContext.getRoot().getDisplay();
|
||||
final PageAction action = (PageAction) treeItem.getData(ActionPane.ACTION_EVENT_CALL_KEY);
|
||||
final Image image = room.roomSize > 0
|
||||
final Image image = active
|
||||
? action.definition.icon.getImage(display)
|
||||
: action.definition.icon.getGreyedImage(display);
|
||||
treeItem.setImage(image);
|
||||
treeItem.setForeground(room.roomSize > 0 ? null : new Color(display, Constants.GREY_DISABLED));
|
||||
treeItem.setForeground(active ? null : new Color(display, Constants.GREY_DISABLED));
|
||||
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to set Proctor-Room-Activation: ", e.getMessage());
|
||||
}
|
||||
|
|
|
@ -78,7 +78,6 @@ public class ProctoringGUIService {
|
|||
final RemoteProctoringRoom remoteProctoringRoom = getRemoteProctoringRoom(item);
|
||||
if (remoteProctoringRoom != null && remoteProctoringRoom.roomSize > 0) {
|
||||
showConnectionsPopup.accept(remoteProctoringRoom);
|
||||
//this.proctorRoomConnectionsPopup.show(pc, remoteProctoringRoom.subject);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -99,12 +98,13 @@ public class ProctoringGUIService {
|
|||
.orElse(null);
|
||||
}
|
||||
|
||||
public int getActualCollectingRoomSize(final String roomName) {
|
||||
public boolean isCollectingRoomEnabled(final String roomName) {
|
||||
try {
|
||||
return this.collectingRoomsActionState.get(roomName).a.roomSize;
|
||||
final Pair<RemoteProctoringRoom, TreeItem> pair = this.collectingRoomsActionState.get(roomName);
|
||||
return pair.a.roomSize > 0 && !pair.a.isOpen;
|
||||
} catch (final Exception e) {
|
||||
log.error("Failed to get actual collecting room size for room: {} cause: ", roomName, e.getMessage());
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,7 +69,8 @@ public interface AdditionalAttributesDAO {
|
|||
|
||||
/** Use this to delete all additional attributes for a given entity.
|
||||
*
|
||||
* @param type the entity type
|
||||
* @param entityId the entity identifier (primary-key) */
|
||||
void deleteAll(Long entityId);
|
||||
void deleteAll(EntityType type, Long entityId);
|
||||
|
||||
}
|
||||
|
|
|
@ -132,4 +132,6 @@ public interface RemoteProctoringRoomDAO {
|
|||
* @return Result refer to active break-out rooms or to an error when happened */
|
||||
Result<Collection<String>> getConnectionsInBreakoutRooms(Long examId);
|
||||
|
||||
void setCollectingRoomOpenFlag(Long roomId, boolean isOpen);
|
||||
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ import java.util.Collection;
|
|||
import java.util.Optional;
|
||||
|
||||
import org.mybatis.dynamic.sql.SqlBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
@ -29,6 +31,8 @@ import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO
|
|||
@WebServiceProfile
|
||||
public class AdditionalAttributesDAOImpl implements AdditionalAttributesDAO {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AdditionalAttributesDAOImpl.class);
|
||||
|
||||
private final AdditionalAttributeRecordMapper additionalAttributeRecordMapper;
|
||||
|
||||
protected AdditionalAttributesDAOImpl(final AdditionalAttributeRecordMapper additionalAttributeRecordMapper) {
|
||||
|
@ -164,14 +168,21 @@ public class AdditionalAttributesDAOImpl implements AdditionalAttributesDAO {
|
|||
|
||||
@Override
|
||||
@Transactional
|
||||
public void deleteAll(final Long entityId) {
|
||||
this.additionalAttributeRecordMapper
|
||||
.deleteByExample()
|
||||
.where(
|
||||
AdditionalAttributeRecordDynamicSqlSupport.entityId,
|
||||
SqlBuilder.isEqualTo(entityId))
|
||||
.build()
|
||||
.execute();
|
||||
public void deleteAll(final EntityType type, final Long entityId) {
|
||||
try {
|
||||
this.additionalAttributeRecordMapper
|
||||
.deleteByExample()
|
||||
.where(
|
||||
AdditionalAttributeRecordDynamicSqlSupport.entityType,
|
||||
SqlBuilder.isEqualTo(type.name()))
|
||||
.and(
|
||||
AdditionalAttributeRecordDynamicSqlSupport.entityId,
|
||||
SqlBuilder.isEqualTo(entityId))
|
||||
.build()
|
||||
.execute();
|
||||
} catch (final Exception e) {
|
||||
log.warn("Failed to delete all additional attributes for: {} cause: {}", entityId, e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -635,7 +635,8 @@ public class ExamDAOImpl implements ExamDAO {
|
|||
.execute();
|
||||
|
||||
// delete all additional attributes
|
||||
this.additionalAttributeRecordMapper.deleteByExample()
|
||||
this.additionalAttributeRecordMapper
|
||||
.deleteByExample()
|
||||
.where(AdditionalAttributeRecordDynamicSqlSupport.entityType, isEqualTo(EntityType.EXAM.name()))
|
||||
.and(AdditionalAttributeRecordDynamicSqlSupport.entityId, isIn(ids))
|
||||
.build()
|
||||
|
|
|
@ -35,6 +35,7 @@ import ch.ethz.seb.sebserver.gbl.util.Result;
|
|||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RemoteProctoringRoomRecordDynamicSqlSupport;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.mapper.RemoteProctoringRoomRecordMapper;
|
||||
import ch.ethz.seb.sebserver.webservice.datalayer.batis.model.RemoteProctoringRoomRecord;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.AdditionalAttributesDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.RemoteProctoringRoomDAO;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.dao.TransactionHandler;
|
||||
import ch.ethz.seb.sebserver.webservice.servicelayer.session.impl.proctoring.NewRoom;
|
||||
|
@ -49,11 +50,14 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
private static final Object RESERVE_ROOM_LOCK = new Object();
|
||||
|
||||
private final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper;
|
||||
private final AdditionalAttributesDAO additionalAttributesDAO;
|
||||
|
||||
protected RemoteProctoringRoomDAOImpl(
|
||||
final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper) {
|
||||
final RemoteProctoringRoomRecordMapper remoteProctoringRoomRecordMapper,
|
||||
final AdditionalAttributesDAO additionalAttributesDAO) {
|
||||
|
||||
this.remoteProctoringRoomRecordMapper = remoteProctoringRoomRecordMapper;
|
||||
this.additionalAttributesDAO = additionalAttributesDAO;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -227,6 +231,10 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
@Transactional
|
||||
public Result<EntityKey> deleteRoom(final Long roomId) {
|
||||
return Result.tryCatch(() -> {
|
||||
|
||||
this.additionalAttributesDAO
|
||||
.deleteAll(EntityType.REMOTE_PROCTORING_ROOM, roomId);
|
||||
|
||||
this.remoteProctoringRoomRecordMapper
|
||||
.deleteByPrimaryKey(roomId);
|
||||
|
||||
|
@ -249,7 +257,15 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
.execute();
|
||||
|
||||
return ids.stream()
|
||||
.map(id -> new EntityKey(String.valueOf(id), EntityType.REMOTE_PROCTORING_ROOM))
|
||||
.map(roomId -> {
|
||||
this.additionalAttributesDAO.deleteAll(
|
||||
EntityType.REMOTE_PROCTORING_ROOM,
|
||||
roomId);
|
||||
return roomId;
|
||||
})
|
||||
.map(roomId -> new EntityKey(
|
||||
String.valueOf(roomId),
|
||||
EntityType.REMOTE_PROCTORING_ROOM))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
});
|
||||
|
@ -340,6 +356,19 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional
|
||||
public void setCollectingRoomOpenFlag(final Long roomId, final boolean isOpen) {
|
||||
this.additionalAttributesDAO.saveAdditionalAttribute(
|
||||
EntityType.REMOTE_PROCTORING_ROOM,
|
||||
roomId,
|
||||
RemoteProctoringRoom.ATTR_IS_OPEN,
|
||||
BooleanUtils.toStringTrueFalse(isOpen))
|
||||
.onError(error -> log.error("Failed to set open flag for proctoring room: {} : {}",
|
||||
roomId,
|
||||
error.getMessage()));
|
||||
}
|
||||
|
||||
private RemoteProctoringRoom toDomainModel(final RemoteProctoringRoomRecord record) {
|
||||
final String breakOutConnections = record.getBreakOutConnections();
|
||||
final Collection<String> connections = StringUtils.isNotBlank(breakOutConnections)
|
||||
|
@ -355,7 +384,25 @@ public class RemoteProctoringRoomDAOImpl implements RemoteProctoringRoomDAO {
|
|||
BooleanUtils.toBooleanObject(record.getTownhallRoom()),
|
||||
connections,
|
||||
record.getJoinKey(),
|
||||
record.getRoomData());
|
||||
record.getRoomData(),
|
||||
isOpen(record));
|
||||
}
|
||||
|
||||
private boolean isOpen(final RemoteProctoringRoomRecord record) {
|
||||
if (record.getTownhallRoom() != 0 || !StringUtils.isBlank(record.getBreakOutConnections())) {
|
||||
return false;
|
||||
} else {
|
||||
return BooleanUtils.toBoolean(this.additionalAttributesDAO
|
||||
.getAdditionalAttribute(
|
||||
EntityType.REMOTE_PROCTORING_ROOM,
|
||||
record.getId(),
|
||||
RemoteProctoringRoom.ATTR_IS_OPEN)
|
||||
.map(rec -> rec.getValue())
|
||||
.onError(error -> log.error("Failed to get open flag for proctoring room: {} : {}",
|
||||
record.getName(),
|
||||
error.getMessage()))
|
||||
.getOrElse(() -> Constants.FALSE_STRING));
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteProctoringRoomRecord createNewCollectingRoom(
|
||||
|
|
|
@ -394,6 +394,9 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
examProctoringService
|
||||
.notifyCollectingRoomOpened(proctoringSettings, room, clientConnections)
|
||||
.getOrThrow();
|
||||
|
||||
this.remoteProctoringRoomDAO
|
||||
.setCollectingRoomOpenFlag(room.id, true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -446,6 +449,19 @@ public class ExamProctoringRoomServiceImpl implements ExamProctoringRoomService
|
|||
.map(cc -> cc.connectionToken)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
final RemoteProctoringRoom room = this.remoteProctoringRoomDAO
|
||||
.getRoom(examId, roomName)
|
||||
.onError(error -> log.error("Failed to get room for setting closed: {} {} {}",
|
||||
examId,
|
||||
roomName,
|
||||
error.getMessage()))
|
||||
.getOr(null);
|
||||
|
||||
if (room != null) {
|
||||
this.remoteProctoringRoomDAO
|
||||
.setCollectingRoomOpenFlag(room.id, false);
|
||||
}
|
||||
|
||||
// Send default settings to clients if feature is enabled
|
||||
if (this.sendBroadcastReset) {
|
||||
this.sendReconfigurationInstructions(
|
||||
|
|
Loading…
Reference in a new issue