Skip to content
Snippets Groups Projects
Commit ff1e8369 authored by Otto Visser's avatar Otto Visser
Browse files

Merge branch 'import-assistants' into 'development'

Import assistants

Closes #202

See merge request !135
parents cb281341 04d807fe
Branches
Tags
1 merge request!154Development
buildscript { buildscript {
ext { ext {
springBootVersion = '1.5.22.RELEASE' springBootVersion = '1.5.15.RELEASE'
//springBootVersion = '2.0.9.RELEASE' //springBootVersion = '2.0.9.RELEASE'
queryDslVersion = '4.2.1' queryDslVersion = '4.2.1'
} }
......
...@@ -28,7 +28,32 @@ import javax.validation.Valid; ...@@ -28,7 +28,32 @@ import javax.validation.Valid;
import nl.tudelft.ewi.queue.annotation.AuthenticatedUser; import nl.tudelft.ewi.queue.annotation.AuthenticatedUser;
import nl.tudelft.ewi.queue.forms.AssignmentListWrapperForm; import nl.tudelft.ewi.queue.forms.AssignmentListWrapperForm;
import nl.tudelft.ewi.queue.forms.ParticipantForm; import nl.tudelft.ewi.queue.forms.ParticipantForm;
import nl.tudelft.ewi.queue.model.Assignment;
import nl.tudelft.ewi.queue.model.Assistant;
import nl.tudelft.ewi.queue.model.Course;
import nl.tudelft.ewi.queue.model.Manager;
import nl.tudelft.ewi.queue.model.QCourse;
import nl.tudelft.ewi.queue.model.Request;
import nl.tudelft.ewi.queue.model.Role;
import nl.tudelft.ewi.queue.model.Student;
import nl.tudelft.ewi.queue.model.Teacher;
import nl.tudelft.ewi.queue.model.User;
import nl.tudelft.ewi.queue.model.*; import nl.tudelft.ewi.queue.model.*;
import nl.tudelft.ewi.queue.model.Assignment;
import nl.tudelft.ewi.queue.model.Assistant;
import nl.tudelft.ewi.queue.model.Course;
import nl.tudelft.ewi.queue.model.Manager;
import nl.tudelft.ewi.queue.model.QCourse;
import nl.tudelft.ewi.queue.model.Request;
import nl.tudelft.ewi.queue.model.Role;
import nl.tudelft.ewi.queue.model.Student;
import nl.tudelft.ewi.queue.model.Teacher;
import nl.tudelft.ewi.queue.model.User;
import nl.tudelft.ewi.queue.repository.AssignmentRepository;
import nl.tudelft.ewi.queue.repository.CourseRepository;
import nl.tudelft.ewi.queue.repository.FirstYearStudentRepository;
import nl.tudelft.ewi.queue.repository.RoleRepository;
import nl.tudelft.ewi.queue.repository.UserRepository;
import nl.tudelft.ewi.queue.repository.AssignmentRepository; import nl.tudelft.ewi.queue.repository.AssignmentRepository;
import nl.tudelft.ewi.queue.repository.CourseRepository; import nl.tudelft.ewi.queue.repository.CourseRepository;
import nl.tudelft.ewi.queue.repository.RoleRepository; import nl.tudelft.ewi.queue.repository.RoleRepository;
...@@ -39,7 +64,6 @@ import nl.tudelft.ewi.queue.service.LabService; ...@@ -39,7 +64,6 @@ import nl.tudelft.ewi.queue.service.LabService;
import nl.tudelft.ewi.queue.service.RequestService; import nl.tudelft.ewi.queue.service.RequestService;
import nl.tudelft.ewi.queue.validator.ParticipantValidator; import nl.tudelft.ewi.queue.validator.ParticipantValidator;
import nl.tudelft.ewi.queue.viewmodel.AssignmentViewModel; import nl.tudelft.ewi.queue.viewmodel.AssignmentViewModel;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
...@@ -60,8 +84,22 @@ import org.springframework.web.bind.annotation.RequestMapping; ...@@ -60,8 +84,22 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.persistence.EntityNotFoundException;
import javax.validation.Valid;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
@Controller @Controller
...@@ -103,7 +141,6 @@ public class CourseController { ...@@ -103,7 +141,6 @@ public class CourseController {
public static String page() { public static String page() {
return "courses"; return "courses";
} }
@InitBinder("participantForm") @InitBinder("participantForm")
protected void initBinder(WebDataBinder binder) { protected void initBinder(WebDataBinder binder) {
binder.addValidators(participantValidator); binder.addValidators(participantValidator);
...@@ -131,17 +168,6 @@ public class CourseController { ...@@ -131,17 +168,6 @@ public class CourseController {
return "course/view/info"; return "course/view/info";
} }
@RequestMapping(value = "/course/{id}/leave", method = RequestMethod.GET)
@PreAuthorize("@permissionService.canLeaveCourse(principal, #id)")
public String leave(@AuthenticatedUser User user, @PathVariable("id") Long id, Model model) {
Course course = getCourse(id);
model.addAttribute("course", course);
return "course/view/leave";
}
@RequestMapping(value = "/course/{id}/leave", method = RequestMethod.POST) @RequestMapping(value = "/course/{id}/leave", method = RequestMethod.POST)
@PreAuthorize("@permissionService.canLeaveCourse(principal, #id)") @PreAuthorize("@permissionService.canLeaveCourse(principal, #id)")
public String leave(@AuthenticatedUser User user, @PathVariable("id") Long id) { public String leave(@AuthenticatedUser User user, @PathVariable("id") Long id) {
...@@ -175,8 +201,7 @@ public class CourseController { ...@@ -175,8 +201,7 @@ public class CourseController {
List<Role> students = course.getStudents(); List<Role> students = course.getStudents();
if (filter.isPresent()) { if (filter.isPresent()) {
students = students.stream() students = students.stream()
.filter(student -> student.getUser().getUsername().toLowerCase() .filter(student -> student.getUser().getUsername().toLowerCase().contains(filter.get().toLowerCase()))
.contains(filter.get().toLowerCase()))
.collect(Collectors.toList()); .collect(Collectors.toList());
} }
students.sort(CourseController::compareLexicographically); students.sort(CourseController::compareLexicographically);
...@@ -244,8 +269,7 @@ public class CourseController { ...@@ -244,8 +269,7 @@ public class CourseController {
@RequestMapping(value = "/course/{courseId}/participants/{id}/remove", method = RequestMethod.GET) @RequestMapping(value = "/course/{courseId}/participants/{id}/remove", method = RequestMethod.GET)
@PreAuthorize("@permissionService.canUpdateParticipant(principal, #courseId, #id)") @PreAuthorize("@permissionService.canUpdateParticipant(principal, #courseId, #id)")
public String deleteParticipant(@PathVariable("courseId") Long courseId, @PathVariable("id") Long id, public String deleteParticipant(@PathVariable("courseId") Long courseId, @PathVariable("id") Long id, Model model) {
Model model) {
Role role = getRole(id); Role role = getRole(id);
model.addAttribute("course", getCourse(courseId)); model.addAttribute("course", getCourse(courseId));
...@@ -264,6 +288,16 @@ public class CourseController { ...@@ -264,6 +288,16 @@ public class CourseController {
return "redirect:/course/" + courseId + "/participants"; return "redirect:/course/" + courseId + "/participants";
} }
@RequestMapping(value = "/course/{courseId}/participants/import", method =
RequestMethod.POST)
@PreAuthorize("@permissionService.canManageTeachers(principal, #courseId)")
public String importParticipants(@PathVariable("courseId") long courseId,
@RequestParam("file") MultipartFile csv) throws IOException {
Course course = courseRepository.findOne(courseId);
courseService.addCourseStaff(csv, course);
return "redirect:/course/" + courseId + "/participants";
}
@RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.GET) @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.GET)
@PreAuthorize("@permissionService.canManageAssignments(principal, #id)") @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
public String viewAssignments(@PathVariable("id") Long id, public String viewAssignments(@PathVariable("id") Long id,
...@@ -281,8 +315,7 @@ public class CourseController { ...@@ -281,8 +315,7 @@ public class CourseController {
return "course/view/assignments"; return "course/view/assignments";
} }
@RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = { @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"addAssignment"})
"addAssignment" })
@PreAuthorize("@permissionService.canManageAssignments(principal, #id)") @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
public String addAssignment(@PathVariable("id") Long id, public String addAssignment(@PathVariable("id") Long id,
@ModelAttribute("addAssignment") AssignmentListWrapperForm assignmentList, @ModelAttribute("addAssignment") AssignmentListWrapperForm assignmentList,
...@@ -301,8 +334,7 @@ public class CourseController { ...@@ -301,8 +334,7 @@ public class CourseController {
return "course/view/assignments"; return "course/view/assignments";
} }
@RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = { @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"removeAssignment"})
"removeAssignment" })
@PreAuthorize("@permissionService.canManageAssignments(principal, #id)") @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
public String removeAssignment(@PathVariable("id") Long id, public String removeAssignment(@PathVariable("id") Long id,
@RequestParam("removeAssignment") int assignmentID, @RequestParam("removeAssignment") int assignmentID,
...@@ -320,8 +352,7 @@ public class CourseController { ...@@ -320,8 +352,7 @@ public class CourseController {
return "redirect:/course/" + id + "/assignments"; return "redirect:/course/" + id + "/assignments";
} }
@RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = { @RequestMapping(value = "/course/{id}/assignments", method = RequestMethod.POST, params = {"storeAssignments"})
"storeAssignments" })
@PreAuthorize("@permissionService.canManageAssignments(principal, #id)") @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
public String storeAssignments(@PathVariable("id") Long id, public String storeAssignments(@PathVariable("id") Long id,
@ModelAttribute("storeAssignments") AssignmentListWrapperForm assignmentList, @ModelAttribute("storeAssignments") AssignmentListWrapperForm assignmentList,
...@@ -354,8 +385,7 @@ public class CourseController { ...@@ -354,8 +385,7 @@ public class CourseController {
@PreAuthorize("@permissionService.canManageAssignments(principal, #id)") @PreAuthorize("@permissionService.canManageAssignments(principal, #id)")
public String uploadEnqueue(@PathVariable("id") Long id, public String uploadEnqueue(@PathVariable("id") Long id,
@RequestParam("csv") MultipartFile csv, @RequestParam("csv") MultipartFile csv,
@RequestParam("uploadEnqueue") Long assignmentId) @RequestParam("uploadEnqueue") Long assignmentId) throws IOException {
throws IOException {
Course course = getCourse(id); Course course = getCourse(id);
groupService.importCsv(course, csv); groupService.importCsv(course, csv);
......
/*
* Queue - A Queueing system that can be used to handle labs in higher education Copyright (C) 2016-2019 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.ewi.queue.helper;
import nl.tudelft.ewi.queue.model.DefaultRole;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Scanner;
public class CsvUserHelper {
private String netid;
private DefaultRole role;
public CsvUserHelper(String netid, DefaultRole role) {
this.netid = netid;
this.role = role;
}
public String getNetId() {
return netid;
}
public DefaultRole getRole() {
return role;
}
public static ArrayList<CsvUserHelper> readCsv(MultipartFile csvFile) throws IOException {
InputStream content = csvFile.getInputStream();
Scanner scanner = new Scanner(content).useDelimiter("\\A");
ArrayList<CsvUserHelper> csvUserHelpersSet = new ArrayList<>();
String csv = "";
if (scanner.hasNext()) {
csv = scanner.next();
}
String[] lines = csv.split("\n");
for (String line : lines) {
String[] currLine = line.split(",");
if (currLine[1].contains("\r")) {
currLine[1] = currLine[1].substring(0, currLine[1].length()-1);
}
csvUserHelpersSet.add(new CsvUserHelper(currLine[0],
DefaultRole.valueOfExt(currLine[1])));
}
content.close();
return csvUserHelpersSet;
}
}
...@@ -26,6 +26,12 @@ public enum DefaultRole implements GrantedAuthority { ...@@ -26,6 +26,12 @@ public enum DefaultRole implements GrantedAuthority {
), ),
ROLE_ADMIN( ROLE_ADMIN(
"Admin" "Admin"
),
ROLE_ASSISTANT(
"Assistant"
),
ROLE_MANAGER(
"Manager"
); );
private final String displayName; private final String displayName;
...@@ -42,4 +48,16 @@ public enum DefaultRole implements GrantedAuthority { ...@@ -42,4 +48,16 @@ public enum DefaultRole implements GrantedAuthority {
public String getAuthority() { public String getAuthority() {
return name(); return name();
} }
public static DefaultRole valueOfExt(String role) {
switch (role.toLowerCase()) {
case "ta": return DefaultRole.ROLE_ASSISTANT;
case "assistant": return DefaultRole.ROLE_ASSISTANT;
case "manager": return DefaultRole.ROLE_MANAGER;
case "teacher": return DefaultRole.ROLE_TEACHER;
case "admin": return DefaultRole.ROLE_ADMIN;
default: return DefaultRole.ROLE_STUDENT;
}
}
} }
...@@ -100,6 +100,10 @@ public class User extends RequestEntity { ...@@ -100,6 +100,10 @@ public class User extends RequestEntity {
this.subscription = subscription; this.subscription = subscription;
} }
public void setStudentNumber(int number) {
this.studentNumber = number;
}
public NotificationList getNotifications() { public NotificationList getNotifications() {
return new NotificationList(notifications); return new NotificationList(notifications);
} }
......
...@@ -15,29 +15,71 @@ ...@@ -15,29 +15,71 @@
*/ */
package nl.tudelft.ewi.queue.service; package nl.tudelft.ewi.queue.service;
import com.google.common.collect.Lists;
import com.querydsl.core.types.dsl.BooleanExpression;
import nl.tudelft.ewi.queue.helper.CsvUserHelper;
import nl.tudelft.ewi.queue.repository.CourseRepository;
import nl.tudelft.ewi.queue.model.Assistant;
import nl.tudelft.ewi.queue.model.Course;
import nl.tudelft.ewi.queue.model.DefaultRole;
import nl.tudelft.ewi.queue.model.Manager;
import nl.tudelft.ewi.queue.model.QRequest;
import nl.tudelft.ewi.queue.model.Request;
import nl.tudelft.ewi.queue.model.Student;
import nl.tudelft.ewi.queue.model.Teacher;
import nl.tudelft.ewi.queue.model.User;
import com.google.common.collect.Lists;
import com.querydsl.core.types.dsl.BooleanExpression;
import nl.tudelft.ewi.queue.helper.CsvUserHelper;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import nl.tudelft.ewi.queue.model.*; import nl.tudelft.ewi.queue.model.*;
import nl.tudelft.ewi.queue.repository.CourseRepository;
import nl.tudelft.ewi.queue.repository.RequestRepository; import nl.tudelft.ewi.queue.repository.RequestRepository;
import nl.tudelft.ewi.queue.repository.RoleRepository;
import nl.tudelft.ewi.queue.repository.UserRepository; import nl.tudelft.ewi.queue.repository.UserRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.BooleanExpression;
import java.io.IOException;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Optional;
@Service @Service
public class CourseService { public class CourseService {
private static final Logger logger = LoggerFactory.getLogger(CourseService.class);
@Autowired @Autowired
private UserRepository userRepository; private UserRepository userRepository;
@Autowired @Autowired
private RequestRepository requestRepository; private RequestRepository requestRepository;
@Autowired
private CourseRepository courseRepository;
@Autowired
private RoleRepository roleRepository;
/** /**
* Enroll user for course * Enroll user for course
* *
...@@ -67,4 +109,42 @@ public class CourseService { ...@@ -67,4 +109,42 @@ public class CourseService {
return feedbackCount; return feedbackCount;
} }
public void addCourseStaff(MultipartFile csv, Course course) throws IOException {
ArrayList<CsvUserHelper> users = CsvUserHelper.readCsv(csv);
for (CsvUserHelper csvUser : users) {
User staff = findUser(csvUser.getNetId());
Optional<Role> oldRole = staff.getRole(course);
oldRole.ifPresent(r -> {
staff.getRoles().remove(r);
userRepository.save(staff);
roleRepository.delete(r);
});
Role role = null;
switch (csvUser.getRole()) {
case ROLE_ASSISTANT: role = new Assistant(staff, course);
break;
case ROLE_TEACHER: role = new Teacher(staff, course);
break;
case ROLE_MANAGER: role = new Manager(staff, course);
}
staff.addRole(role);
course.addRole(role);
userRepository.save(staff);
courseRepository.save(course);
}
}
private User findUser(String netid) {
User user = userRepository.findByUsername(netid+"@tudelft.nl");
if (user == null) {
user = new User(netid+"@tudelft.nl","", netid, "",
DefaultRole.ROLE_STUDENT, 0);
userRepository.save(user);
}
return user;
}
} }
...@@ -97,6 +97,14 @@ public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService { ...@@ -97,6 +97,14 @@ public class SAMLUserDetailsServiceImpl implements SAMLUserDetailsService {
user.setDisplayName(displayName); user.setDisplayName(displayName);
saveUserWithCheck(user); saveUserWithCheck(user);
} }
if (user.getDefaultRole() == DefaultRole.ROLE_STUDENT) {
int studentNumber =
Integer.parseUnsignedInt(credential.getAttributeAsString(TUD_STUDENT_NUMBER));
if (studentNumber != user.getStudentNumber()) {
user.setStudentNumber(studentNumber);
saveUserWithCheck(user);
}
}
AttachUserToFirstYearStudent(user); AttachUserToFirstYearStudent(user);
return new UserPrincipal(user); return new UserPrincipal(user);
......
...@@ -59,6 +59,39 @@ ...@@ -59,6 +59,39 @@
</div> </div>
</div> </div>
</form> </form>
<div class="row">
<div class="col-sm-12">
<h4>Import TA's</h4>
<p class="lead">You can upload a CSV with TA netid's</p>
<th:block th:if="${@permissionService.canManageTeachers(#authentication.principal, course.id)}">
<form action="#"
th:action="@{/course/{id}/participants/import(id=${course.id})}"
enctype="multipart/form-data" class="form-horizontal" method="post">
<div class="form-group">
<div class="col-sm-offset-2 col-sm-8">
<label class="custom-file">
<input type="file" id="csv" name="file" class="csvfile" />
<span class="csvfile"></span>
</label>
</div>
</div>
<div class="form-group">
<div class="col-sm-offset-2 col-sm-8">
<button type="submit" class="btn btn-primary"
name="storeTA">Import
Assistants/Managers/Teachers</button>
</div>
</div>
<div class="col-sm-8">
The csv should look like: <br>
"netid1","role1" <br>
"netid2",role2" <br>
The following roles can be applied: <i>ta, assistant, manager, teacher</i>
</div>
</form>
</th:block>
</div>
</section> </section>
</body> </body>
</html> </html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please to comment