Skip to content
Snippets Groups Projects
Commit 1cfda893 authored by Ruben Backx's avatar Ruben Backx :coffee:
Browse files

Merge branch 'development' into 'master'

Version 2.2.1

See merge request !711
parents c1fd5c9a 13f3dad2
No related branches found
No related tags found
No related merge requests found
Showing
with 364 additions and 43 deletions
......@@ -9,13 +9,24 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased]
## [2.2.1]
### Added
- Requests in slotted labs can now be distributed to assistants in advanced. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
### Changed
- Teachers can only see assistant feedback for courses that they manage. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
- Slotted requests no longer show up 15 minutes in advance on the requests page. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
### Fixed
- Teachers can no longer see feedback of other teachers. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
- Screens should no longer sleep when presenting (not supported on Firefox). [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- Lab filter for shared labs now shows the correct course codes in front of assignments. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
- Chips are no longer cut off on smaller devices. [@hpage](https://gitlab.ewi.tudelft.nl/hpage)
- Presentation text was invisible on dark mode. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- Forwarded requests did not always show up or showed up for the wrong people in the requests table. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- All requests would be added to the request table regardless of language preference. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- Slotted lab page did not display requests. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
## [2.2.0]
......@@ -41,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Revoked requests could be finished (approved, rejected, etc.) by teachers. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- Room maps did not work. [@rwbackx](https://gitlab.ewi.tudelft.nl/rwbackx)
- Archive button appears again. [@mmadara](https://gitlab.ewi.tudelft.nl/mmadara)
### Deprecated
### Removed
......
......@@ -6,7 +6,7 @@ import org.springframework.boot.gradle.tasks.run.BootRun
import java.nio.file.Files
group = "nl.tudelft.ewi.queue"
version = "2.2.0"
version = "2.2.1"
val javaVersion = JavaVersion.VERSION_17
......@@ -395,6 +395,8 @@ dependencies {
implementation("org.webjars:momentjs:2.24.0")
implementation("org.webjars:fullcalendar:5.9.0")
implementation("org.webjars:codemirror:5.62.2")
implementation("org.webjars.npm:simplemde:1.11.2")
// Library for converting markdown to html
implementation("org.commonmark:commonmark:0.18.1")
......
......@@ -30,8 +30,10 @@ import nl.tudelft.labracore.api.PersonControllerApi;
import nl.tudelft.labracore.api.SessionControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.labracore.lib.security.user.AuthenticatedPerson;
import nl.tudelft.labracore.lib.security.user.DefaultRole;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.librador.dto.view.View;
import nl.tudelft.queue.PageUtil;
import nl.tudelft.queue.cache.EditionCacheManager;
import nl.tudelft.queue.cache.EditionCollectionCacheManager;
import nl.tudelft.queue.cache.PersonCacheManager;
......@@ -50,6 +52,7 @@ import nl.tudelft.queue.service.PermissionService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
......@@ -252,13 +255,14 @@ public class HomeController {
@PreAuthorize("@permissionService.canViewOwnFeedback()")
public String ownFeedback(@AuthenticatedPerson Person person,
Model model, Pageable pageable) {
fillInFeedbackModel(person.getId(), person, model, pageable);
fillInFeedbackModel(person.getId(), person, model, pageable, false);
return "home/feedback";
}
/**
* Maps the feedback url to a page. The Feedback page displays feedback for a user with the given ID.
* Maps the feedback url to a page. The Feedback page displays feedback for a user with the given ID. This
* endpoint is also used by TAM.
*
* @param person The person that is currently authenticated.
* @param id The id of the person for which feedback will be shown.
......@@ -270,7 +274,17 @@ public class HomeController {
@PreAuthorize("@permissionService.canViewFeedback(#id)")
public String feedback(@AuthenticatedPerson Person person, @PathVariable("id") Long id,
Model model, Pageable pageable) {
fillInFeedbackModel(id, person, model, pageable);
fillInFeedbackModel(id, person, model, pageable, false);
return "home/feedback";
}
@GetMapping("/feedback/{id}/manager")
@PreAuthorize("@permissionService.canViewFeedback(#id)")
public String feedbackManager(@AuthenticatedPerson Person person, @PathVariable("id") Long id,
Model model, Pageable pageable) {
fillInFeedbackModel(id, person, model, pageable, true);
return "home/feedback";
}
......@@ -278,17 +292,32 @@ public class HomeController {
/**
* Fills in the model for a page where feedback is shown to the user.
*
* @param assistantId The id of the user to find assistant for (this could be the current user).
*
* @param assistantId The id of the user to find assistant for (this could be the current
* user).
* @param person The person that is currently authenticated.
* @param model The model that is to be filled.
* @param pageable The pageable containing information on how much feedback needs to be shown.
* @param pageable The pageable containing information on how much feedback needs to be
* shown.
* @param restrictToCourseManager Used to restrict feedback to the courses a manager teaches.
* @throws AccessDeniedException In the case that a teacher purposefully adds a teacher as a TA to view
* their feedback.
*/
private void fillInFeedbackModel(Long assistantId, Person person, Model model, Pageable pageable) {
private void fillInFeedbackModel(Long assistantId, Person person, Model model, Pageable pageable,
Boolean restrictToCourseManager) {
var assistant = pCache.getRequired(assistantId);
if (assistant.getDefaultRole() == PersonSummaryDTO.DefaultRoleEnum.TEACHER
&& person.getDefaultRole() != DefaultRole.ADMIN) {
throw new AccessDeniedException(
"Teachers are not permitted to view the feedback of other teachers.");
}
Page<Feedback> feedback = assistantId.equals(person.getId())
? fr.findByAssistantAnonymised(assistantId, pageable)
: fr.findByAssistant(assistantId, pageable);
: restrictToCourseManager
? fs.filterFeedbackForManagerCourses(fr.findByAssistant(assistantId), pageable)
: PageUtil.toPage(pageable, fr.findByAssistant(assistantId));
model.addAttribute("assistant", assistant);
model.addAttribute("feedback",
......
......@@ -54,6 +54,7 @@ import nl.tudelft.queue.dto.patch.labs.CapacitySessionPatchDTO;
import nl.tudelft.queue.dto.patch.labs.ExamLabPatchDTO;
import nl.tudelft.queue.dto.patch.labs.RegularLabPatchDTO;
import nl.tudelft.queue.dto.patch.labs.SlottedLabPatchDTO;
import nl.tudelft.queue.dto.util.DistributeRequestsDTO;
import nl.tudelft.queue.dto.util.RequestTableFilterDTO;
import nl.tudelft.queue.dto.view.RequestViewDTO;
import nl.tudelft.queue.model.QueueSession;
......@@ -144,6 +145,9 @@ public class LabController {
@Autowired
private RoleDTOService roleService;
@Autowired
private RequestService requestService;
@Autowired
private StudentGroupControllerApi sgApi;
......@@ -251,6 +255,26 @@ public class LabController {
return "redirect:/lab/" + lab.getId();
}
/**
*
* @param lab The lab that we will distribute requests for
* @param person The distributor of the requests
* @param dto The DTO containing the assignments and assistants to distribute
* @return A redirect to the lab overview page.
*/
@Transactional
@PostMapping("/lab/{lab}/distribute")
@PreAuthorize("@permissionService.canManageSession(#lab)")
public String distributeRequests(@PathEntity Lab lab, @AuthenticatedPerson Person person,
DistributeRequestsDTO dto) {
dto.getEditionSelections()
.forEach(edition -> requestService.distributeRequests(edition.getSelectedAssignments(),
edition.getSelectedAssistants(), person, lab));
return "redirect:/lab/" + lab.getId();
}
/**
* Gets the student enqueue view. This page displays the form that needs to be filled out by the student
* to successfully enrol into the given session.
......@@ -765,6 +789,9 @@ public class LabController {
return isStaffInAll || roleService.rolesForPersonInEdition(edition, person).stream()
.noneMatch(roleService::isStaff);
}).toList();
Map<Long, CourseSummaryDTO> editionsToCourses = session.getEditions().stream()
.collect(Collectors.toMap(EditionSummaryDTO::getId,
edition -> eCache.getRequired(edition.getId()).getCourse()));
Map<Long, String> assignments = allowedAssignments.stream()
.collect(Collectors.toMap(AssignmentDetailsDTO::getId, a -> {
var edition = mCache.getRequired(a.getModule().getId()).getEdition();
......@@ -773,6 +800,7 @@ public class LabController {
: eCache.getRequired(edition.getId()).getCourse().getName() + " - "
+ a.getName();
}));
Map<Long, Long> notEnqueueAble = allowedAssignments.stream().filter(a -> {
var edition = mCache.getRequired(a.getModule().getId()).getEdition();
return roleService.rolesForPersonInEdition(edition, person).isEmpty();
......@@ -781,6 +809,11 @@ public class LabController {
return edition.getId();
}));
if (qSession instanceof AbstractSlottedLab<?>) {
model.addAttribute("distributeRequestsDto",
new DistributeRequestsDTO(editionsToCourses.size()));
}
Set<Long> alreadyInGroup = sgCache.getByPerson(person.getId()).stream()
.map(g -> g.getModule().getId()).collect(Collectors.toSet());
Set<Long> hasEmptyGroups = qSession.getModules().stream()
......@@ -793,6 +826,7 @@ public class LabController {
allowedAssignments.stream().filter(a -> hasEmptyGroups.contains(a.getModule().getId()))
.map(AssignmentDetailsDTO::getId).collect(Collectors.toSet()));
model.addAttribute("assignments", assignments);
model.addAttribute("editionsToCourses", editionsToCourses);
model.addAttribute("notEnqueueAble", notEnqueueAble);
model.addAttribute("types", lab.getAllowedRequests().stream()
.collect(Collectors.groupingBy(AllowedRequest::getAssignment,
......
......@@ -20,6 +20,7 @@ package nl.tudelft.queue.controller;
import static nl.tudelft.labracore.api.dto.RolePersonDetailsDTO.TypeEnum.*;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
......@@ -43,6 +44,7 @@ import nl.tudelft.queue.model.LabRequest;
import nl.tudelft.queue.model.QueueSession;
import nl.tudelft.queue.model.Request;
import nl.tudelft.queue.model.SelectionRequest;
import nl.tudelft.queue.model.enums.RequestStatus;
import nl.tudelft.queue.model.labs.Lab;
import nl.tudelft.queue.repository.LabRequestRepository;
import nl.tudelft.queue.repository.ProfileRepository;
......@@ -148,7 +150,13 @@ public class RequestController {
model.addAttribute("showName", false);
model.addAttribute("showOnlyRelevant", true);
setRequestTableAttributes(model, pageable, assistant,
"/requests", r -> r.getEventInfo().getStatus().isPending(), false, true);
"/requests",
r -> r.getEventInfo().getStatus() == RequestStatus.PENDING ||
(r.getEventInfo().getStatus() == RequestStatus.FORWARDED &&
!Objects.equals(r.getEventInfo().getForwardedBy(), assistant.getId()) &&
(r.getEventInfo().getForwardedTo() == null ||
r.getEventInfo().getForwardedTo().equals(assistant.getId()))),
false, true);
return "request/list";
}
......@@ -186,7 +194,7 @@ public class RequestController {
.collect(Collectors.toList());
List<LabRequest> filteredRequests = rs
.filterRequestsSharedEditionCheck(lrr.findAllByFilter(labs, filter, language))
.filterRequestsSharedEditionCheck(lrr.findAllByFilterUpcoming(labs, filter, language))
.stream().filter(requestFilter)
.toList();
......
/*
* Queue - A Queueing system that can be used to handle labs in higher education
* Copyright (C) 2016-2021 Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.queue.dto.util;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class DistributeRequestsDTO {
@Builder.Default
private List<EditionRequestDistributionDTO> editionSelections = new ArrayList<>();
public DistributeRequestsDTO(int numEditions) {
this.editionSelections = new ArrayList<>();
for (int i = 0; i < numEditions; i++) {
this.editionSelections.add(new EditionRequestDistributionDTO());
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public static class EditionRequestDistributionDTO {
private List<Long> selectedAssignments = new ArrayList<>();
private List<Long> selectedAssistants = new ArrayList<>();
}
}
......@@ -51,6 +51,8 @@ public class RequestCreatedMessage extends View<LabRequest> implements Message {
private Long buildingId;
private String buildingName;
private String language;
private OnlineMode onlineMode;
private String onlineModeDisplayName;
......
......@@ -23,6 +23,7 @@ import lombok.*;
import nl.tudelft.queue.dto.view.requests.LabRequestViewDTO;
import nl.tudelft.queue.model.enums.OnlineMode;
import nl.tudelft.queue.model.enums.RequestStatus;
import nl.tudelft.queue.model.enums.RequestType;
import nl.tudelft.queue.model.events.RequestForwardedToAnyEvent;
@Data
......@@ -53,6 +54,7 @@ public class RequestForwardedToAnyMessage extends RequestStatusUpdateMessage<Req
private String moduleName;
private Long labId;
private RequestType requestType;
private String requestTypeDisplayName;
@Override
......@@ -94,6 +96,7 @@ public class RequestForwardedToAnyMessage extends RequestStatusUpdateMessage<Req
labId = view.getQSession().getId();
requestType = view.getRequestType();
requestTypeDisplayName = view.getRequestType().displayName();
}
}
......@@ -21,6 +21,7 @@ import lombok.*;
import nl.tudelft.queue.dto.view.requests.LabRequestViewDTO;
import nl.tudelft.queue.model.enums.OnlineMode;
import nl.tudelft.queue.model.enums.RequestStatus;
import nl.tudelft.queue.model.enums.RequestType;
import nl.tudelft.queue.model.events.RequestForwardedToPersonEvent;
@Data
......@@ -38,15 +39,20 @@ public class RequestForwardedToPersonMessage
private String organizationName;
private Long roomId;
private String roomName;
private Long buildingId;
private String buildingName;
private OnlineMode onlineMode;
private String onlineModeDisplayName;
private Long assignmentId;
private String assignmentName;
private Long moduleId;
private String moduleName;
private RequestType requestType;
private String requestTypeDisplayName;
@Override
......@@ -72,16 +78,21 @@ public class RequestForwardedToPersonMessage
requestedBy = view.requesterEntityName();
if (view.getRoom() != null) {
roomId = view.getRoom().getId();
roomName = view.getRoom().getName();
buildingId = view.getRoom().getBuilding().getId();
buildingName = view.getRoom().getBuilding().getName();
} else if (view.getOnlineMode() != null) {
onlineMode = view.getOnlineMode();
onlineModeDisplayName = view.getOnlineMode().getDisplayName();
}
assignmentId = view.getAssignment().getId();
assignmentName = view.getAssignment().getName();
moduleId = view.getAssignment().getModule().getId();
moduleName = view.getAssignment().getModule().getName();
requestType = view.getRequestType();
requestTypeDisplayName = view.getRequestType().displayName();
}
}
......@@ -71,12 +71,11 @@ public interface FeedbackRepository
/**
* @param assistantId The assistant to find Feedback for.
* @param pageable The pageable object to page feedback with.
* @return The page of feedback for the given Assistant.
*/
default Page<Feedback> findByAssistant(Long assistantId, Pageable pageable) {
default List<Feedback> findByAssistant(Long assistantId) {
return findAll(qf.id.assistantId.eq(assistantId)
.and(qf.feedback.isNotNull()).and(qf.feedback.isNotEmpty()), pageable);
.and(qf.feedback.isNotNull()).and(qf.feedback.isNotEmpty()));
}
/**
......
......@@ -355,21 +355,40 @@ public interface LabRequestRepository
/**
* Finds all requests that pass the given filter. This filter is a DTO transferred from the page
* requesting to see these requests. The filter contains all labs, rooms, assignments, etc. that need to
* be displayed. If a field in the filter is left as an empty set, the filter ignores it.
* be displayed. If a field in the filter is left as an empty set, the filter ignores it. Additionally,
* returns only past and upcoming requests.
*
* @param labs The labs that the should also be kept in the filter.
* @param filter The filter to apply to the boolean expression.
* @param language The assistant's language choice
* @return The filtered list of requests.
*/
default List<LabRequest> findAllByFilter(List<Lab> labs, RequestTableFilterDTO filter,
default List<LabRequest> findAllByFilterUpcoming(List<Lab> labs, RequestTableFilterDTO filter,
Language language) {
return findAll(qlr.in(select(qlr).from(qlr)
.leftJoin(qlr.timeSlot, QTimeSlot.timeSlot).on(qlr.timeSlot.id.eq(QTimeSlot.timeSlot.id))
.where(createFilterBooleanExpression(filter, qlr.session.in(labs).and(
qlr.session.type.eq(QueueSessionType.REGULAR)
.or(qlr.session.type.in(QueueSessionType.SLOTTED, QueueSessionType.EXAM)
.and(qlr.timeSlot.slot.opensAt.before(now().plusMinutes(10))))))
.and(qlr.timeSlot.slot.opensAt.before(now())))))
.and(matchesLanguagePreference(language)))));
}
/**
* Finds all requests that pass the given filter. This filter is a DTO transferred from the page
* requesting to see these requests. The filter contains all labs, rooms, assignments, etc. that need to
* be displayed. If a field in the filter is left as an empty set, the filter ignores it.
*
* @param labs The labs that the should also be kept in the filter.
* @param filter The filter to apply to the boolean expression.
* @param language The assistant's language choice
* @return The filtered list of requests.
*/
default List<LabRequest> findAllByFilter(List<Lab> labs, RequestTableFilterDTO filter,
Language language) {
return findAll(qlr.in(select(qlr).from(qlr)
.leftJoin(qlr.timeSlot, QTimeSlot.timeSlot).on(qlr.timeSlot.id.eq(QTimeSlot.timeSlot.id))
.where(createFilterBooleanExpression(filter, qlr.session.in(labs))
.and(matchesLanguagePreference(language)))));
}
......
......@@ -21,12 +21,17 @@ import static java.time.LocalDateTime.now;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import javax.transaction.Transactional;
import javax.validation.ValidationException;
import nl.tudelft.labracore.api.dto.PersonSummaryDTO;
import nl.tudelft.labracore.api.dto.SessionDetailsDTO;
import nl.tudelft.queue.PageUtil;
import nl.tudelft.queue.cache.PersonCacheManager;
import nl.tudelft.queue.cache.SessionCacheManager;
import nl.tudelft.queue.dto.patch.FeedbackPatchDTO;
import nl.tudelft.queue.model.Feedback;
import nl.tudelft.queue.model.LabRequest;
......@@ -34,6 +39,9 @@ import nl.tudelft.queue.repository.FeedbackRepository;
import nl.tudelft.queue.repository.LabRequestRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
......@@ -48,6 +56,16 @@ public class FeedbackService {
@Autowired
private LabRequestRepository rr;
@Autowired
private PermissionService ps;
@Autowired
@Lazy
private LabService ls;
@Autowired
private SessionCacheManager sessionCacheManager;
/**
* Finds all assistant that are involved in the given request. This list of people is generated for
* students to be able to give feedback on the one TA they want to give feedback on. This list is ordered
......@@ -109,4 +127,32 @@ public class FeedbackService {
});
}
/**
* Filters feedback for a manager, such that they can only see feedback for TAs in courses that they
* managed.
*
* @param feedback The list of feedback to be filtered.
* @param pageable The pageable used for paging.
* @return A Page of feedback that the manager can see.
*/
public Page<Feedback> filterFeedbackForManagerCourses(List<Feedback> feedback, Pageable pageable) {
List<Long> sessionIds = feedback.stream()
.map(fb -> fb.getRequest().getSession().getSession())
.distinct()
.toList();
Map<Long, Boolean> canManage = sessionCacheManager
.getAndHandleAll(sessionIds, ls.deleteSessionsByIds()).stream()
.collect(Collectors.toMap(
SessionDetailsDTO::getId,
session -> ps
.canManageInAnyEdition(new ArrayList<>(session.getEditions()))));
List<Feedback> filteredFeedback = feedback.stream()
.filter(fb -> canManage.getOrDefault(fb.getRequest().getSession().getSession(), false))
.toList();
return PageUtil.toPage(pageable, filteredFeedback);
}
}
......@@ -18,6 +18,7 @@
package nl.tudelft.queue.service;
import static java.time.LocalDateTime.now;
import static nl.tudelft.labracore.api.dto.RolePersonDetailsDTO.TypeEnum.*;
import static nl.tudelft.queue.misc.QueueSessionStatus.*;
import java.io.IOException;
......
/*
* Queue - A Queueing system that can be used to handle labs in higher education
* Copyright (C) 2016-2021 Delft University of Technology
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package nl.tudelft.queue.service;
import java.util.List;
import nl.tudelft.labracore.api.dto.AssignmentSummaryDTO;
import nl.tudelft.labracore.api.dto.ModuleDetailsDTO;
import org.springframework.stereotype.Service;
@Service
public class ModuleDTOService {
/**
* Gets assignments with respect to a certain edition within several modules.
*
* @param dto The list of modules to consider.
* @param editionId The edition to consider.
* @return The assignments that belong to that edition within the modules.
*/
public List<AssignmentSummaryDTO> getAssignmentsInEdition(List<ModuleDetailsDTO> dto, Long editionId) {
return dto.stream().filter(module -> module.getEdition().getId().equals(editionId))
.flatMap(module -> module.getAssignments().stream())
.collect(java.util.stream.Collectors.toList());
}
}
......@@ -759,7 +759,7 @@ public class PermissionService {
* course and the request is not yet assigned to anybody.
*/
public boolean canPickRequest(Long requestId) {
return withRequest(requestId,
return isAdmin() || withRequest(requestId,
request -> withEdition(request.toViewDTO().getEdition().getId(),
edition -> withRole(edition.getId(),
(person, role) -> MANAGER_ROLES.contains(role)))
......
......@@ -39,6 +39,7 @@ import nl.tudelft.queue.model.LabRequest;
import nl.tudelft.queue.model.Request;
import nl.tudelft.queue.model.SelectionRequest;
import nl.tudelft.queue.model.enums.OnlineMode;
import nl.tudelft.queue.model.enums.RequestStatus;
import nl.tudelft.queue.model.enums.SelectionProcedure;
import nl.tudelft.queue.model.events.*;
import nl.tudelft.queue.model.labs.AbstractSlottedLab;
......@@ -143,11 +144,6 @@ public class RequestService {
if (!currentlyEnqueuing.add(personId))
return;
if (request instanceof LabRequest labRequest) {
/*
* TODO in the future, delegate to another service layer component,
* and let it generate a link for the respective online mode.
* This is for the far future, when more online modes need to be supported.
*/
if (labRequest.getTimeSlot() != null && !labRequest.getTimeSlot().canTakeSlot()) {
throw new AccessDeniedException("Time slot is not available");
......@@ -589,4 +585,34 @@ public class RequestService {
.flatMap(id -> sgApi.getStudentGroupsById(List.of(id)).collectList().map(l -> l.get(0)))
.block();
}
/**
* Distributes requests by abusing the forwarding mechanism. Assigns requests in a round-robin fashion.
*
* @param selectedAssignments Assignments that should be distributed
* @param selectedAssistants Assistants that should receieve the distributed requests
* @param distributor Person who is responsible for distributing the requests
* @param lab The lab session that the requests belong to.
*/
@Transactional
public void distributeRequests(List<Long> selectedAssignments, List<Long> selectedAssistants,
Person distributor, Lab lab) {
var assistants = pCache
.getAndIgnoreMissing(selectedAssistants);
if (assistants.isEmpty())
return;
var requests = lab.getRequests().stream()
.filter(rq -> selectedAssignments.contains(rq.getAssignment())
&& rq.getEventInfo().getStatus() == RequestStatus.PENDING)
.sorted(Comparator.comparing(Request::getCreatedAt))
.toList();
// round robin request assignment
for (int i = 0; i < requests.size(); i++) {
forwardRequestToPerson(requests.get(i), distributor, assistants.get(i % assistants.size()),
"Distributed by " + distributor.getDisplayName());
}
}
}
......@@ -22,7 +22,6 @@ import static nl.tudelft.labracore.api.dto.RolePersonDetailsDTO.TypeEnum.*;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import javax.servlet.http.HttpSession;
......@@ -34,6 +33,7 @@ import nl.tudelft.labracore.api.SessionControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.librador.dto.view.View;
import nl.tudelft.queue.PageUtil;
import nl.tudelft.queue.cache.*;
import nl.tudelft.queue.dto.util.RequestTableFilterDTO;
import nl.tudelft.queue.dto.view.QueueSessionSummaryDTO;
......@@ -97,6 +97,9 @@ public class RequestTableService {
@Autowired
private SessionCacheManager sCache;
@Autowired
private ModuleCacheManager mCache;
@Autowired
private StudentGroupCacheManager sgCache;
......@@ -273,10 +276,7 @@ public class RequestTableService {
.filter(r -> r.getEventInfo().getStatus() != RequestStatus.FORWARDED))
.toList();
}
final int start = (int) pageable.getOffset();
final int end = (int) (Math.min((start + pageable.getPageSize()), requestList.size()));
return new PageImpl<>(convertRequestsToView(sortedRequestList.subList(start, end)), pageable,
requestList.size());
return PageUtil.toPage(pageable, convertRequestsToView(sortedRequestList));
}
/**
......@@ -375,18 +375,11 @@ public class RequestTableService {
if (assignments.isEmpty())
return Collections.emptyMap();
List<Long> assignmentIds = assignments.stream().map(AssignmentDetailsDTO::getId).toList();
var assignmentModules = Objects
.requireNonNull(aApi.getAssignmentsWithModules(assignmentIds).collectList().block());
List<String> courseCodes = eCache
.getAndIgnoreMissing(assignmentModules.stream()
.map(assignment -> assignment.getModule().getEdition().getId()))
.stream().map(edition -> edition.getCourse().getCode()).toList();
return IntStream.range(0, assignments.size()).boxed()
.collect(Collectors.toMap(assignmentIds::get, courseCodes::get));
return assignments.stream().collect(Collectors.toMap(
AssignmentDetailsDTO::getId,
assignment -> eCache
.getRequired(mCache.getRequired(assignment.getModule().getId()).getEdition().getId())
.getCourse().getCode()));
}
......
......@@ -27,7 +27,9 @@ import java.util.stream.Collectors;
import nl.tudelft.labracore.api.RoleControllerApi;
import nl.tudelft.labracore.api.dto.*;
import nl.tudelft.labracore.lib.security.user.Person;
import nl.tudelft.queue.cache.EditionCacheManager;
import nl.tudelft.queue.cache.EditionRolesCacheManager;
import nl.tudelft.queue.cache.SessionCacheManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
......@@ -40,6 +42,12 @@ public class RoleDTOService {
@Autowired
private RoleControllerApi rApi;
@Autowired
private EditionCacheManager eCache;
@Autowired
private SessionCacheManager sessionCacheManager;
public List<String> names(List<PersonSummaryDTO> people) {
return people.stream().map(PersonSummaryDTO::getDisplayName).collect(Collectors.toList());
}
......@@ -77,6 +85,10 @@ public class RoleDTOService {
return roles(erCache.getRequired(eDto.getId()).getRoles(), types);
}
public List<PersonSummaryDTO> roles(EditionSummaryDTO eDto, Set<RolePersonDetailsDTO.TypeEnum> types) {
return roles(eCache.getRequired(eDto.getId()), types);
}
/**
* Gets the display name of the role type.
*
......@@ -131,6 +143,10 @@ public class RoleDTOService {
return roles(eDto, Set.of(TA));
}
public List<PersonSummaryDTO> staff(EditionSummaryDTO eDto) {
return roles(eDto, Set.of(TA, HEAD_TA, TEACHER, TEACHER_RO));
}
public List<PersonSummaryDTO> headTAs(EditionDetailsDTO eDto) {
return roles(eDto, Set.of(HEAD_TA));
}
......
......@@ -8,6 +8,7 @@
}
background-color: white;
color: black;
display: flex;
flex-direction: column;
height: 100cqh;
......
......@@ -77,6 +77,24 @@ const inFilter = (() => {
};
}).apply();
/**
* Checks if the event matches the user's language preference.
*
* @param event The event to check
*/
function matchesLanguage(event) {
switch (event.language) {
case "ANY":
return true;
case "DUTCH_ONLY":
return userLanguage === "ANY" || userLanguage === "DUTCH_ONLY";
case "ENGLISH_ONLY":
return userLanguage === "ANY" || userLanguage === "ENGLISH_ONLY";
default:
return false;
}
}
/**
* Handles the creation of a websocket connection by subscribing to the relevant topics.
* @param client The STOMP Client object to connect with.
......@@ -88,7 +106,7 @@ function handleSocketCreation(client) {
if (page && page > 0) return;
const event = JSON.parse(msg.body);
if (event.type === "request-created" && inFilter(event)) {
if (event.type === "request-created" && inFilter(event) && matchesLanguage(event)) {
appendToRequestTable(event);
increaseGetNextCounter(event["labId"]);
} else if (event.type !== "request-created") {
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment