Added a StudentTeamService and cleaned up controllers which can now make

use of it.
This commit is contained in:
Andrew Lalis 2019-05-10 12:29:01 +02:00 committed by andrewlalis
parent 6653047263
commit ae2886fe63
6 changed files with 346 additions and 119 deletions

View File

@ -13,6 +13,9 @@ import org.springframework.web.bind.annotation.RequestParam;
import java.util.Optional;
/**
* Controller for operations dealing with the global collection of students, not particular to one course.
*/
@Controller
public class Students {

View File

@ -0,0 +1,65 @@
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.teams.StudentTeam;
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository;
import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentTeamRepository;
import nl.andrewlalis.teaching_assistant_assistant.services.StudentTeamService;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Controller
public class MergeSingleTeams {
private Logger logger = LogManager.getLogger(MergeSingleTeams.class);
private CourseRepository courseRepository;
private StudentTeamRepository studentTeamRepository;
private StudentTeamService studentTeamService;
protected MergeSingleTeams(CourseRepository courseRepository, StudentTeamRepository studentTeamRepository, StudentTeamService studentTeamService) {
this.courseRepository = courseRepository;
this.studentTeamRepository = studentTeamRepository;
this.studentTeamService = studentTeamService;
}
@GetMapping("/courses/{code}/student_teams/merge_single_teams")
public String get(@PathVariable String code) {
Optional<Course> optionalCourse = this.courseRepository.findByCode(code);
if (optionalCourse.isPresent()) {
Course course = optionalCourse.get();
List<StudentTeam> singleTeams = this.getAllSingleTeams(course);
singleTeams.forEach(team -> logger.info("Team " + team.getId() + " is a single team."));
// while (singleTeams.size() > 1) {
// StudentTeam single1 = singleTeams.remove(0);
// StudentTeam single2 = singleTeams.remove(0);
//
// // Todo: use a service here and when removing a team in another location to avoid duplication.
// StudentTeam newTeam = this.studentTeamService.createNewStudentTeam(course);
// }
}
return "redirect:/courses/{code}/student_teams";
}
private List<StudentTeam> getAllSingleTeams(Course course) {
List<StudentTeam> allTeams = course.getStudentTeams();
List<StudentTeam> singleTeams = new ArrayList<>();
for (StudentTeam team : allTeams) {
if (team.getMembers().size() == 1) {
singleTeams.add(team);
}
}
return singleTeams;
}
}

View File

@ -8,7 +8,7 @@ import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepo
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 nl.andrewlalis.teaching_assistant_assistant.services.StudentTeamService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
@ -16,7 +16,6 @@ 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.List;
import java.util.Optional;
@ -27,17 +26,20 @@ public class StudentTeamEntity {
private CourseRepository courseRepository;
private StudentRepository studentRepository;
private TeachingAssistantTeamRepository teachingAssistantTeamRepository;
private StudentTeamService studentTeamService;
protected StudentTeamEntity(
StudentTeamRepository studentTeamRepository,
CourseRepository courseRepository,
StudentRepository studentRepository,
TeachingAssistantTeamRepository teachingAssistantTeamRepository
TeachingAssistantTeamRepository teachingAssistantTeamRepository,
StudentTeamService studentTeamService
) {
this.studentTeamRepository = studentTeamRepository;
this.courseRepository = courseRepository;
this.studentRepository = studentRepository;
this.teachingAssistantTeamRepository = teachingAssistantTeamRepository;
this.studentTeamService = studentTeamService;
}
/**
@ -63,14 +65,16 @@ public class StudentTeamEntity {
return "courses/entity/student_teams/create";
}
/**
* Mapping for creating a new student team.
* @param courseCode The course code.
* @param model The view model.
* @return A redirect to the list of student teams.
*/
@PostMapping("/courses/{courseCode}/student_teams/create")
public String postCreate(@PathVariable String courseCode, Model model) {
Optional<Course> optionalCourse = this.courseRepository.findByCode(courseCode);
optionalCourse.ifPresent(course -> {
StudentTeam team = new StudentTeam(course);
course.addStudentTeam(team);
this.courseRepository.save(course);
});
optionalCourse.ifPresent(course -> this.studentTeamService.createNewStudentTeam(course));
return "redirect:/courses/{courseCode}/student_teams";
}
@ -91,6 +95,13 @@ public class StudentTeamEntity {
return "courses/entity/student_teams/entity/add_student";
}
/**
* Mapping for adding a new student to this team.
* @param courseCode The course code.
* @param teamId The id of the team to add the student to.
* @param studentId The id of an existing student to add to this team.
* @return A redirect to the list of student teams.
*/
@PostMapping("/courses/{courseCode}/student_teams/{teamId}/add_student")
public String postAddStudent(
@PathVariable String courseCode,
@ -102,16 +113,10 @@ public class StudentTeamEntity {
Optional<Student> 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);
this.studentTeamService.addStudent(optionalStudentTeam.get(), optionalStudent.get());
}
return "redirect:/courses/{courseCode}/student_teams";
return "redirect:/courses/{courseCode}/student_teams/{teamId}";
}
@GetMapping("/courses/{courseCode}/student_teams/{teamId}/assign_teaching_assistant_team")
@ -131,6 +136,13 @@ public class StudentTeamEntity {
return "courses/entity/student_teams/entity/assign_teaching_assistant_team";
}
/**
* Endpoint for assigning a teaching assistant team to this student team.
* @param courseCode The course code.
* @param teamId The id of the student team.
* @param teachingAssistantTeamId The id of the teaching assistant team.
* @return A redirect to the team responsible.
*/
@PostMapping("/courses/{courseCode}/student_teams/{teamId}/assign_teaching_assistant_team")
public String postAssignTeachingAssistantTeam(
@PathVariable String courseCode,
@ -147,29 +159,19 @@ public class StudentTeamEntity {
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);
}
this.studentTeamService.assignTeachingAssistantTeam(optionalStudentTeam.get(), teachingAssistantTeam);
}
return "redirect:/courses/{courseCode}/student_teams";
return "redirect:/courses/{courseCode}/student_teams/{teamId}";
}
/**
* Endpoint for removing a student from the student team.
* @param courseCode The code for the course.
* @param teamId The id of the team.
* @param studentId The student's id.
* @return A redirect to the team after the student is removed.
*/
@GetMapping("/courses/{courseCode}/student_teams/{teamId}/remove_student/{studentId}")
public String getRemoveStudent(
@PathVariable String courseCode,
@ -181,26 +183,7 @@ public class StudentTeamEntity {
Optional<Student> 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);
this.studentTeamService.removeStudent(optionalStudentTeam.get(), optionalStudent.get());
}
return "redirect:/courses/{courseCode}/student_teams/{teamId}";
@ -215,26 +198,7 @@ public class StudentTeamEntity {
Optional<StudentTeam> 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.");
this.studentTeamService.generateRepository(optionalStudentTeam.get());
}
return "redirect:/courses/{courseCode}/student_teams/{teamId}";
@ -246,38 +210,7 @@ public class StudentTeamEntity {
Optional<StudentTeam> 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);
this.studentTeamService.removeTeam(optionalStudentTeam.get());
}
return "redirect:/courses/{courseCode}/student_teams";

View File

@ -0,0 +1,213 @@
package nl.andrewlalis.teaching_assistant_assistant.services;
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.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.List;
/**
* This service is used to control the manipulation, creation and deletion of student teams.
*/
@Service
public class StudentTeamService {
private Logger logger = LogManager.getLogger(StudentTeamService.class);
private StudentTeamRepository studentTeamRepository;
private StudentRepository studentRepository;
private CourseRepository courseRepository;
private TeachingAssistantTeamRepository teachingAssistantTeamRepository;
public StudentTeamService(
StudentTeamRepository studentTeamRepository,
StudentRepository studentRepository,
CourseRepository courseRepository,
TeachingAssistantTeamRepository teachingAssistantTeamRepository
) {
this.studentTeamRepository = studentTeamRepository;
this.studentRepository = studentRepository;
this.courseRepository = courseRepository;
this.teachingAssistantTeamRepository = teachingAssistantTeamRepository;
}
/**
* Creates a new empty student team.
* @param course The course that the student team is in.
* @return The newly created student team.
*/
public StudentTeam createNewStudentTeam(Course course) {
StudentTeam newTeam = new StudentTeam(course);
course.addStudentTeam(newTeam);
this.courseRepository.save(course);
logger.info("Created new team: " + newTeam.getId());
return newTeam;
}
/**
* Creates a new student team, and populates it with some students and automatically assigns a teaching assistant
* team.
* @param course The course to which the team will belong.
* @param students The list of students to add to the team.
* @param teachingAssistantTeam The teaching assistant team responsible for this new team.
* @return The newly created student team.
*/
public StudentTeam createNewStudentTeam(Course course, List<Student> students, TeachingAssistantTeam teachingAssistantTeam) {
StudentTeam emptyTeam = this.createNewStudentTeam(course);
emptyTeam.setAssignedTeachingAssistantTeam(teachingAssistantTeam);
teachingAssistantTeam.addAssignedStudentTeam(emptyTeam);
this.teachingAssistantTeamRepository.save(teachingAssistantTeam);
for (Student student : students) {
this.addStudent(emptyTeam, student);
}
return emptyTeam;
}
/**
* Adds a new student to this team.
* @param team The team to add the student to.
* @param student The student to add.
*/
public void addStudent(StudentTeam team, Student student) {
team.addMember(student);
student.assignToTeam(team);
this.studentTeamRepository.save(team);
this.studentRepository.save(student);
}
/**
* Removes a single student from a team, and if that team has a github repository, tries to remove the student from
* that as well.
* @param studentTeam The student team to remove the student from.
* @param student The student to remove.
*/
public void removeStudent(StudentTeam studentTeam, Student student) {
studentTeam.removeMember(student);
student.removeFromAssignedTeam(studentTeam);
this.studentTeamRepository.save(studentTeam);
this.studentRepository.save(student);
if (studentTeam.getGithubRepositoryName() != null) {
try {
logger.debug("Removing " + student.getGithubUsername() + " from repository " + studentTeam.getGithubRepositoryName());
GithubManager manager = new GithubManager(studentTeam.getCourse().getApiKey());
manager.removeCollaborator(studentTeam, student);
} catch (IOException e) {
logger.catching(e);
logger.error("Could not remove student from repository: " + studentTeam.getGithubRepositoryName());
}
}
logger.info("Removed student " + student.getFullName() + " from team " + studentTeam.getId());
}
/**
* Assigns a new teaching assistant team to a student team.
* @param studentTeam The student team.
* @param teachingAssistantTeam The teaching assistant team to assign the student team to.
* This may be null.
*/
public void assignTeachingAssistantTeam(StudentTeam studentTeam, TeachingAssistantTeam teachingAssistantTeam) {
TeachingAssistantTeam oldTeachingAssistantTeam = studentTeam.getAssignedTeachingAssistantTeam();
if (oldTeachingAssistantTeam != null) {
oldTeachingAssistantTeam.removeAssignedStudentTeam(studentTeam);
studentTeam.setAssignedTeachingAssistantTeam(null);
this.teachingAssistantTeamRepository.save(oldTeachingAssistantTeam);
}
if (teachingAssistantTeam != null) {
studentTeam.setAssignedTeachingAssistantTeam(teachingAssistantTeam);
teachingAssistantTeam.addAssignedStudentTeam(studentTeam);
this.teachingAssistantTeamRepository.save(teachingAssistantTeam);
}
this.studentTeamRepository.save(studentTeam);
logger.info("Assigned teaching assistant team " + teachingAssistantTeam + " to student team " + studentTeam.getId());
}
/**
* Uses a {@link GithubManager} to generate a repository for this student team.
* @param team The team to generate a repository for.
*/
public void generateRepository(StudentTeam team) {
if (team.getGithubRepositoryName() == null) {
try {
GithubManager manager = new GithubManager(team.getCourse().getApiKey());
String name = manager.generateStudentTeamRepository(team);
team.setGithubRepositoryName(name);
this.studentTeamRepository.save(team);
} catch (IOException e) {
logger.error("Could not generate repository.");
}
} else {
logger.warn("Repository " + team.getGithubRepositoryName() + " already exists.");
}
}
/**
* Removes the given team, archives the repository if one exists.
* @param team The team to remove.
*/
public void removeTeam(StudentTeam team) {
Course course = team.getCourse();
// 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();
logger.error("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);
logger.info("Removed team " + team.getId());
}
/**
* Merges all teams consisting of a single student so that new teams are generated.
*
* TODO: Make this team size independent.
*/
public void mergeSingleTeams() {
}
}

View File

@ -11,6 +11,8 @@ import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.kohsuke.github.*;
import java.io.*;
@ -26,6 +28,8 @@ import java.util.stream.Collectors;
*/
public class GithubManager {
Logger logger = LogManager.getLogger(GithubManager.class);
private GitHub github;
private String apiKey;
@ -55,13 +59,14 @@ public class GithubManager {
* @return The name of the created repository.
*/
public String generateStudentTeamRepository(StudentTeam team) {
GHOrganization organization;
logger.info("Generating repository for student team " + team.getId());
GHOrganization organization;
try {
organization = this.github.getOrganization(team.getCourse().getGithubOrganizationName());
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not get Github organization with name: " + team.getCourse().getGithubOrganizationName());
logger.error("Could not get Github organization with name: " + team.getCourse().getGithubOrganizationName());
return null;
}
@ -71,12 +76,11 @@ public class GithubManager {
// Get the TA team which manages this repository.
GHTeam teachingAssistantGithubTeam;
try {
teachingAssistantGithubTeam = organization.getTeamByName(team.getAssignedTeachingAssistantTeam().getGithubTeamName());
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not get team by name: " + team.getAssignedTeachingAssistantTeam().getGithubTeamName());
logger.error("Could not get team by name: " + team.getAssignedTeachingAssistantTeam().getGithubTeamName());
return null;
}
@ -89,50 +93,56 @@ public class GithubManager {
repositoryBuilder.autoInit(false);
GHRepository repository;
try {
repository = repositoryBuilder.create();
logger.debug("Creating empty repository " + repositoryName);
repository = repositoryBuilder.create();
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not create repository: " + repositoryName);
logger.error("Could not create repository: " + repositoryName);
return null;
}
try {
logger.debug("Assigning teaching assistant team " + teachingAssistantGithubTeam.getName() + " to repository.");
this.addRepositoryToTeam(teachingAssistantGithubTeam, repository);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not add repository " + repositoryName + " to team " + teachingAssistantGithubTeam.getName());
logger.error("Could not add repository " + repositoryName + " to team " + teachingAssistantGithubTeam.getName());
return null;
}
try {
logger.debug("Adding starting file to the repository.");
this.addStarterFile(repository, "program_resources/getting_started.md", "getting_started.md");
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not add the starter file to the repository: " + repositoryName);
logger.error("Could not add the starter file to the repository: " + repositoryName);
return null;
}
try {
logger.debug("Creating development branch.");
this.createDevelopmentBranch(repository);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not create development branch for repository: " + repositoryName);
logger.error("Could not create development branch for repository: " + repositoryName);
return null;
}
try {
logger.debug("Adding protections to the master branch.");
this.protectMasterBranch(repository, teachingAssistantGithubTeam);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not add protections to the master branch of " + repositoryName);
logger.error("Could not add protections to the master branch of " + repositoryName);
return null;
}
try {
logger.debug("Adding students as collaborators.");
this.addStudentsAsCollaborators(repository, team);
} catch (IOException e) {
e.printStackTrace();
System.err.println("Could not add students as collaborators to " + repositoryName);
logger.error("Could not add students as collaborators to " + repositoryName);
return null;
}
@ -211,7 +221,7 @@ public class GithubManager {
* 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
* @throws IOException If an io exception occurred, duh!
*/
public void deactivateRepository(StudentTeam studentTeam) throws IOException {
GHOrganization organization = this.github.getOrganization(studentTeam.getCourse().getGithubOrganizationName());

View File

@ -69,6 +69,9 @@
<div class="sidebar_block">
<a th:href="@{/courses/{code}/student_teams/create(code=${course.getCode()})}">Create New Student Team</a>
</div>
<div class="sidebar_block">
<a th:href="@{/courses/{code}/student_teams/merge_single_teams(code=${course.getCode()})}">Merge Single Teams</a>
</div>
</div>
</body>