From 64f75ea21d1948f3c86509007c78cefd48497490 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Fri, 26 Apr 2019 20:59:05 +0200 Subject: [PATCH] Did more than can be explained in a commit message. --- .../controllers/Students.java | 47 +++- .../student_teams/StudentTeamEntity.java | 249 +++++++++++++++++- .../students/InviteAllToRepository.java | 14 +- .../controllers/students/StudentEntity.java | 33 ++- .../model/Course.java | 4 + .../model/people/Person.java | 8 + .../model/people/Student.java | 12 +- .../model/people/teams/StudentTeam.java | 5 + .../model/people/teams/Team.java | 9 + .../util/github/GithubManager.java | 58 +++- .../resources/templates/courses/create.html | 3 - .../courses/entity/student_teams.html | 3 + .../courses/entity/student_teams/create.html | 28 ++ .../courses/entity/student_teams/entity.html | 36 ++- .../student_teams/entity/add_student.html | 35 +++ .../assign_teaching_assistant_team.html | 39 +++ .../resources/templates/fragments/footer.html | 20 +- src/main/resources/templates/students.html | 7 +- .../resources/templates/students/create.html | 60 +++++ 19 files changed, 637 insertions(+), 33 deletions(-) create mode 100644 src/main/resources/templates/courses/entity/student_teams/create.html create mode 100644 src/main/resources/templates/courses/entity/student_teams/entity/add_student.html create mode 100644 src/main/resources/templates/courses/entity/student_teams/entity/assign_teaching_assistant_team.html create mode 100644 src/main/resources/templates/students/create.html diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Students.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Students.java index 4086ce3..0406fe3 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Students.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Students.java @@ -1,17 +1,29 @@ package nl.andrewlalis.teaching_assistant_assistant.controllers; +import nl.andrewlalis.teaching_assistant_assistant.model.Course; +import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.ModelAttribute; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.Optional; @Controller public class Students { - private StudentRepository studentRepository; + private static final String NO_COURSE = "NO_COURSE_SELECTED"; - protected Students(StudentRepository studentRepository) { + private StudentRepository studentRepository; + private CourseRepository courseRepository; + + protected Students(StudentRepository studentRepository, CourseRepository courseRepository) { this.studentRepository = studentRepository; + this.courseRepository = courseRepository; } @GetMapping("/students") @@ -19,4 +31,35 @@ public class Students { model.addAttribute("students", this.studentRepository.findAll()); return "students"; } + + @GetMapping("/students/create") + public String getCreate(Model model) { + model.addAttribute("student", new Student("First Name", "Last Name", "Email Address", "Github Username", 1234567)); + model.addAttribute("courses", this.courseRepository.findAll()); + + return "students/create"; + } + + @PostMapping( + value = "/students/create", + consumes = "application/x-www-form-urlencoded" + ) + public String postCreate( + @ModelAttribute Student newStudent, + @RequestParam(value = "course_code", required = false) String courseCode + ) { + this.studentRepository.save(newStudent); + + if (courseCode != null && !courseCode.equals(NO_COURSE)) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + optionalCourse.ifPresent(course -> { + course.addParticipant(newStudent); + newStudent.assignToCourse(course); + this.courseRepository.save(course); + this.studentRepository.save(newStudent); + }); + } + + return "redirect:/students"; + } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/StudentTeamEntity.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/StudentTeamEntity.java index aeae752..4897e2e 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/StudentTeamEntity.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/StudentTeamEntity.java @@ -1,21 +1,42 @@ package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.student_teams; +import nl.andrewlalis.teaching_assistant_assistant.model.Course; +import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam; +import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.TeachingAssistantTeam; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository; import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentTeamRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeachingAssistantTeamRepository; +import nl.andrewlalis.teaching_assistant_assistant.util.github.GithubManager; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestParam; +import java.io.IOException; import java.util.Optional; @Controller public class StudentTeamEntity { private StudentTeamRepository studentTeamRepository; + private CourseRepository courseRepository; + private StudentRepository studentRepository; + private TeachingAssistantTeamRepository teachingAssistantTeamRepository; - protected StudentTeamEntity(StudentTeamRepository studentTeamRepository) { + protected StudentTeamEntity( + StudentTeamRepository studentTeamRepository, + CourseRepository courseRepository, + StudentRepository studentRepository, + TeachingAssistantTeamRepository teachingAssistantTeamRepository + ) { this.studentTeamRepository = studentTeamRepository; + this.courseRepository = courseRepository; + this.studentRepository = studentRepository; + this.teachingAssistantTeamRepository = teachingAssistantTeamRepository; } /** @@ -32,4 +53,230 @@ public class StudentTeamEntity { return "courses/entity/student_teams/entity"; } + + @GetMapping("/courses/{courseCode}/student_teams/create") + public String getCreate(@PathVariable String courseCode, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + optionalCourse.ifPresent(course -> model.addAttribute("course", course)); + + return "courses/entity/student_teams/create"; + } + + @PostMapping("/courses/{courseCode}/student_teams/create") + public String postCreate(@PathVariable String courseCode, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + optionalCourse.ifPresent(course -> { + StudentTeam team = new StudentTeam(course); + course.addStudentTeam(team); + this.courseRepository.save(course); + }); + + return "redirect:/courses/{courseCode}/student_teams"; + } + + @GetMapping("/courses/{courseCode}/student_teams/{teamId}/add_student") + public String getAddStudent(@PathVariable String courseCode, @PathVariable long teamId, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent()) { + model.addAttribute("course", optionalCourse.get()); + model.addAttribute("student_team", optionalStudentTeam.get()); + model.addAttribute("eligible_students", optionalCourse.get().getStudents()); + } + + return "courses/entity/student_teams/entity/add_student"; + } + + @PostMapping("/courses/{courseCode}/student_teams/{teamId}/add_student") + public String postAddStudent( + @PathVariable String courseCode, + @PathVariable long teamId, + @RequestParam(value = "student_id") long studentId + ) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + Optional optionalStudent = this.studentRepository.findById(studentId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent() && optionalStudent.isPresent()) { + StudentTeam team = optionalStudentTeam.get(); + Student student = optionalStudent.get(); + + team.addMember(student); + student.assignToTeam(team); + this.studentTeamRepository.save(team); + this.studentRepository.save(student); + } + + return "redirect:/courses/{courseCode}/student_teams"; + } + + @GetMapping("/courses/{courseCode}/student_teams/{teamId}/assign_teaching_assistant_team") + public String getAssignTeachingAssistantTeam( + @PathVariable String courseCode, + @PathVariable long teamId, + Model model + ) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent()) { + model.addAttribute("course", optionalCourse.get()); + model.addAttribute("student_team", optionalStudentTeam.get()); + } + + return "courses/entity/student_teams/entity/assign_teaching_assistant_team"; + } + + @PostMapping("/courses/{courseCode}/student_teams/{teamId}/assign_teaching_assistant_team") + public String postAssignTeachingAssistantTeam( + @PathVariable String courseCode, + @PathVariable long teamId, + @RequestParam(value = "teaching_assistant_team_id") long teachingAssistantTeamId + ) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + Optional optionalTeachingAssistantTeam = this.teachingAssistantTeamRepository.findById(teachingAssistantTeamId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent()) { + TeachingAssistantTeam teachingAssistantTeam = null; + if (optionalTeachingAssistantTeam.isPresent()) { + teachingAssistantTeam = optionalTeachingAssistantTeam.get(); + } + + StudentTeam studentTeam = optionalStudentTeam.get(); + TeachingAssistantTeam oldTeam = studentTeam.getAssignedTeachingAssistantTeam(); + + // Unset old TA team if it exists. + if (oldTeam != null) { + oldTeam.removeAssignedStudentTeam(studentTeam); + studentTeam.setAssignedTeachingAssistantTeam(null); + this.teachingAssistantTeamRepository.save(oldTeam); + this.studentTeamRepository.save(studentTeam); + } + + // Set new TA team if it exists. + if (teachingAssistantTeam != null) { + studentTeam.setAssignedTeachingAssistantTeam(teachingAssistantTeam); + teachingAssistantTeam.addAssignedStudentTeam(studentTeam); + this.teachingAssistantTeamRepository.save(teachingAssistantTeam); + this.studentTeamRepository.save(studentTeam); + } + } + + return "redirect:/courses/{courseCode}/student_teams"; + } + + @GetMapping("/courses/{courseCode}/student_teams/{teamId}/remove_student/{studentId}") + public String getRemoveStudent( + @PathVariable String courseCode, + @PathVariable long teamId, + @PathVariable long studentId + ) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + Optional optionalStudent = this.studentRepository.findById(studentId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent() && optionalStudent.isPresent()) { + Student student = optionalStudent.get(); + StudentTeam team = optionalStudentTeam.get(); + Course course = optionalCourse.get(); + + // If the team has a github repository, remove this student as a collaborator. + if (team.getGithubRepositoryName() != null) { + try { + GithubManager manager = new GithubManager(course.getApiKey()); + manager.removeCollaborator(team, student); + System.out.println("Removed " + student.getGithubUsername() + " from " + team.getGithubRepositoryName()); + } catch (IOException e) { + System.err.println("Could not remove student from repository: " + team.getGithubRepositoryName()); + } + } + + team.removeMember(student); + student.removeFromAssignedTeam(team); + + this.studentTeamRepository.save(team); + this.studentRepository.save(student); + } + + return "redirect:/courses/{courseCode}/student_teams/{teamId}"; + } + + @GetMapping("/courses/{courseCode}/student_teams/{teamId}/generate_repository") + public String getGenerateRepository( + @PathVariable String courseCode, + @PathVariable long teamId + ) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent()) { + StudentTeam team = optionalStudentTeam.get(); + Course course = optionalCourse.get(); + + if (team.getGithubRepositoryName() == null) { + System.out.println("Generating repository."); + try { + GithubManager manager = new GithubManager(course.getApiKey()); + String name = manager.generateStudentTeamRepository(team); + team.setGithubRepositoryName(name); + this.studentTeamRepository.save(team); + + } catch (IOException e) { + System.err.println("Could not generate repository."); + } + } else { + System.err.println("Repository already exists."); + } + + } else { + System.err.println("Could not find all objects."); + } + + return "redirect:/courses/{courseCode}/student_teams/{teamId}"; + } + + @GetMapping("/courses/{courseCode}/student_teams/{teamId}/remove") + public String remove(@PathVariable String courseCode, @PathVariable long teamId) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalStudentTeam = this.studentTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalStudentTeam.isPresent()) { + StudentTeam team = optionalStudentTeam.get(); + Course course = optionalCourse.get(); + + // Remove the student team at all costs! + if (team.getGithubRepositoryName() != null) { + // First remove all student collaborators. + try { + GithubManager manager = new GithubManager(course.getApiKey()); + manager.deactivateRepository(team); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not deactivate repository."); + } + } + + // Remove all students from this team. + for (Student s : team.getStudents()) { + s.removeFromAssignedTeam(team); + team.removeMember(s); + this.studentRepository.save(s); + } + + // Remove the TA team assignment. + TeachingAssistantTeam teachingAssistantTeam = team.getAssignedTeachingAssistantTeam(); + teachingAssistantTeam.removeAssignedStudentTeam(team); + team.setAssignedTeachingAssistantTeam(null); + this.teachingAssistantTeamRepository.save(teachingAssistantTeam); + + // Remove the repository from the course and delete it. + course.removeStudentTeam(team); + this.studentTeamRepository.delete(team); + this.courseRepository.save(course); + } + + return "redirect:/courses/{courseCode}/student_teams"; + } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java index bfa2703..1803284 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java @@ -63,8 +63,6 @@ public class InviteAllToRepository { keys.add(rawKey.trim()); } - String fullRepositoryName = course.getGithubOrganizationName() + '/' + repositoryName; - int inviteCounter = 0; GithubManager manager; try { @@ -80,24 +78,30 @@ public class InviteAllToRepository { if (inviteCounter == 50) { System.out.println("Used up 50 invites on key."); try { + if (keys.isEmpty()) { + System.err.println("No more keys."); + failedNames.addAll(githubUsernames); + break; + } manager = new GithubManager(keys.remove(0)); inviteCounter = 0; } catch (IOException e) { e.printStackTrace(); failedNames.addAll(githubUsernames); - return; + break; } } String username = githubUsernames.remove(0); try { - manager.addCollaborator(fullRepositoryName, username, "pull"); + manager.addCollaborator(course.getGithubOrganizationName(), repositoryName, username, "pull"); inviteCounter++; System.out.println("\tInvited " + username); } catch (IOException e) { //e.printStackTrace(); - System.err.println("Could not add " + username + " to repository " + fullRepositoryName + ": " + e.getMessage()); + System.err.println("Could not add " + username + " to repository " + repositoryName + ": " + e.getMessage()); failedNames.add(username); + inviteCounter = 50; // Try to use a different key if possible. } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/students/StudentEntity.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/students/StudentEntity.java index b0768c9..9fd8547 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/students/StudentEntity.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/students/StudentEntity.java @@ -1,7 +1,11 @@ package nl.andrewlalis.teaching_assistant_assistant.controllers.students; +import nl.andrewlalis.teaching_assistant_assistant.model.Course; import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; +import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.Team; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeamRepository; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @@ -15,9 +19,13 @@ import java.util.Optional; public class StudentEntity { private StudentRepository studentRepository; + private TeamRepository teamRepository; + private CourseRepository courseRepository; - protected StudentEntity(StudentRepository studentRepository) { + protected StudentEntity(StudentRepository studentRepository, TeamRepository teamRepository, CourseRepository courseRepository) { this.studentRepository = studentRepository; + this.teamRepository = teamRepository; + this.courseRepository = courseRepository; } @GetMapping("/students/{id}") @@ -51,4 +59,27 @@ public class StudentEntity { return "redirect:/students/{id}"; } + + @GetMapping("/students/{id}/remove") + public String getRemove(@PathVariable long id) { + Optional optionalStudent = this.studentRepository.findById(id); + optionalStudent.ifPresent(student -> { + + for (Team team : student.getTeams()) { + team.removeMember(student); + student.removeFromAssignedTeam(team); + this.teamRepository.save(team); + } + + for (Course course : student.getCourses()) { + course.removeParticipant(student); + student.removeFromAssignedCourse(course); + this.courseRepository.save(course); + } + + this.studentRepository.delete(student); + }); + + return "redirect:/students"; + } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/Course.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/Course.java index 8c33111..e22cf53 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/Course.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/Course.java @@ -110,6 +110,10 @@ public class Course extends BasicEntity { this.studentTeams.add(team); } + public void removeStudentTeam(StudentTeam team) { + this.studentTeams.remove(team); + } + public void addTeachingAssistantTeam(TeachingAssistantTeam team) { this.teachingAssistantTeams.add(team); } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Person.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Person.java index c4fea05..5d7446c 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Person.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Person.java @@ -80,12 +80,20 @@ public abstract class Person extends BasicEntity { } } + public void removeFromAssignedTeam(Team team) { + this.teams.remove(team); + } + public void assignToCourse(Course course) { if (!this.courses.contains(course)) { this.courses.add(course); } } + public void removeFromAssignedCourse(Course course) { + this.courses.remove(course); + } + /* Getters and Setters */ diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Student.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Student.java index 26963bf..74856c4 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Student.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/Student.java @@ -50,7 +50,17 @@ public class Student extends Person { */ @Override public boolean equals(Object o) { - return super.equals(o) || this.getStudentNumber() == ((Student) o).getStudentNumber(); + if (super.equals(o)) { + return true; + } + + if (!(o instanceof Student)) { + return false; + } + + Student s = (Student) o; + + return this.getStudentNumber() == s.getStudentNumber(); } @Override diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/StudentTeam.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/StudentTeam.java index dad4d47..a201008 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/StudentTeam.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/StudentTeam.java @@ -1,5 +1,6 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people.teams; +import nl.andrewlalis.teaching_assistant_assistant.model.Course; import nl.andrewlalis.teaching_assistant_assistant.model.assignments.grades.AssignmentGrade; import nl.andrewlalis.teaching_assistant_assistant.model.people.Person; import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; @@ -41,6 +42,10 @@ public class StudentTeam extends Team { */ public StudentTeam() {} + public StudentTeam(Course course) { + super(course); + } + public List getStudents() { List people = super.getMembers(); List students = new ArrayList<>(); diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/Team.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/Team.java index 0e81635..c121694 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/Team.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/Team.java @@ -47,6 +47,15 @@ public abstract class Team extends BasicEntity { this.members = new ArrayList<>(); } + /** + * Publicly available constructor in which a course is required. + * @param course The course that this team is in. + */ + public Team(Course course) { + this(); + this.setCourse(course); + } + public void addMember(Person person) { if (!this.containsMember(person)) { this.members.add(person); diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/github/GithubManager.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/github/GithubManager.java index 0d84d88..df986b5 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/github/GithubManager.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/github/GithubManager.java @@ -6,6 +6,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode; import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam; import org.apache.http.HttpResponse; +import org.apache.http.client.methods.HttpPatch; import org.apache.http.client.methods.HttpPut; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; @@ -15,7 +16,9 @@ import org.kohsuke.github.*; import java.io.*; import java.net.URL; import java.nio.file.Files; +import java.util.ArrayList; import java.util.Calendar; +import java.util.List; import java.util.stream.Collectors; /** @@ -164,7 +167,7 @@ public class GithubManager { private void addStudentsAsCollaborators(GHRepository repository, StudentTeam studentTeam) throws IOException { for (Student student : studentTeam.getStudents()) { - this.addCollaborator(repository.getFullName(), student.getGithubUsername(), "push"); + this.addCollaborator(repository.getOwnerName(), repository.getName(), student.getGithubUsername(), "push"); } } @@ -174,9 +177,9 @@ public class GithubManager { repository.createRef("refs/heads/development", sha1); } - public void addCollaborator(String repositoryName, String githubUsername, String permission) throws IOException { + public void addCollaborator(String organizationName, String repositoryName, String githubUsername, String permission) throws IOException { try { - String url = "https://api.github.com/repos/" + repositoryName + "/collaborators/" + githubUsername + "?access_token=" + this.apiKey; + String url = "https://api.github.com/repos/" + organizationName + '/' + repositoryName + "/collaborators/" + githubUsername + "?access_token=" + this.apiKey; HttpPut put = new HttpPut(url); CloseableHttpClient client = HttpClientBuilder.create().build(); ObjectMapper mapper = new ObjectMapper(); @@ -196,6 +199,36 @@ public class GithubManager { } } + public void removeCollaborator(StudentTeam studentTeam, Student student) throws IOException { + GHOrganization organization = this.github.getOrganization(studentTeam.getCourse().getGithubOrganizationName()); + GHRepository repository = organization.getRepository(studentTeam.getGithubRepositoryName()); + GHUser user = this.github.getUser(student.getGithubUsername()); + + repository.removeCollaborators(user); + } + + /** + * Deactivates a repository by removing all collaborator students, unassigning the repository from the TA team that + * was responsible for it, and archiving it. + * @param studentTeam The student team for which to archive. + * @throws IOException + */ + public void deactivateRepository(StudentTeam studentTeam) throws IOException { + GHOrganization organization = this.github.getOrganization(studentTeam.getCourse().getGithubOrganizationName()); + GHRepository repository = organization.getRepository(studentTeam.getGithubRepositoryName()); + List users = new ArrayList<>(); + for (Student s : studentTeam.getStudents()) { + users.add(this.github.getUser(s.getGithubUsername())); + } + + repository.removeCollaborators(users); + + GHTeam taTeam = organization.getTeamByName(studentTeam.getAssignedTeachingAssistantTeam().getGithubTeamName()); + taTeam.remove(repository); + + this.archiveRepository(repository); + } + private void addRepositoryToTeam(GHTeam team, GHRepository repository) throws IOException { team.add(repository, GHOrganization.Permission.ADMIN); } @@ -222,4 +255,23 @@ public class GithubManager { builder.enable(); } + /** + * Archives a repository so that it can no longer be manipulated. + * TODO: Change to using Github API instead of Apache HttpUtils. + * @param repo The repository to archive. + */ + private void archiveRepository(GHRepository repo) throws IOException { + HttpPatch patch = new HttpPatch("https://api.github.com/repos/" + repo.getFullName() + "?access_token=" + this.apiKey); + CloseableHttpClient client = HttpClientBuilder.create().build(); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + root.put("archived", true); + String json = mapper.writeValueAsString(root); + patch.setEntity(new StringEntity(json)); + HttpResponse response = client.execute(patch); + if (response.getStatusLine().getStatusCode() != 200) { + throw new IOException("Could not archive repository: " + repo.getName() + ". Code: " + response.getStatusLine().getStatusCode()); + } + } + } diff --git a/src/main/resources/templates/courses/create.html b/src/main/resources/templates/courses/create.html index 425655a..35bc337 100644 --- a/src/main/resources/templates/courses/create.html +++ b/src/main/resources/templates/courses/create.html @@ -37,9 +37,6 @@ diff --git a/src/main/resources/templates/courses/entity/student_teams.html b/src/main/resources/templates/courses/entity/student_teams.html index 1f0c2b3..6bb8dc3 100644 --- a/src/main/resources/templates/courses/entity/student_teams.html +++ b/src/main/resources/templates/courses/entity/student_teams.html @@ -66,6 +66,9 @@ + diff --git a/src/main/resources/templates/courses/entity/student_teams/create.html b/src/main/resources/templates/courses/entity/student_teams/create.html new file mode 100644 index 0000000..6aa2f77 --- /dev/null +++ b/src/main/resources/templates/courses/entity/student_teams/create.html @@ -0,0 +1,28 @@ + + + + Create Student Team + + + +
+

Create a New Student Team

+ +

+ Creates a new student team for the course, without any assigned TA team or student members. +

+ +
+ +
+ +
+
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/courses/entity/student_teams/entity.html b/src/main/resources/templates/courses/entity/student_teams/entity.html index dc1af36..fa04e6f 100644 --- a/src/main/resources/templates/courses/entity/student_teams/entity.html +++ b/src/main/resources/templates/courses/entity/student_teams/entity.html @@ -16,7 +16,7 @@ -
  • +
  • Assigned Teaching Assistant Team: + (Remove From This Team)
  • @@ -39,17 +43,37 @@ + + Remove + diff --git a/src/main/resources/templates/students/create.html b/src/main/resources/templates/students/create.html new file mode 100644 index 0000000..3d5a5d2 --- /dev/null +++ b/src/main/resources/templates/students/create.html @@ -0,0 +1,60 @@ + + + + Create a Student + + + +
    +

    Create New Student

    + +
    +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + +
    + +
    +
    + + + + + \ No newline at end of file