diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java index 22d7739..c5adf7d 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java @@ -27,11 +27,5 @@ public class TeachingAssistantAssistantApplication implements CommandLineRunner @Override public void run(String... args) throws Exception { System.out.println("Running startup..."); -// -// String exampleDate = "2019/04/15 4:13:41 PM GMT+2 "; -// // Parse the timestamp. -// DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd h:mm:ss a O "); -// ZonedDateTime dateTime = ZonedDateTime.parse(exampleDate, formatter); -// System.out.println("Read time: " + dateTime.toString()); } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java index 821ad41..a863b10 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java @@ -48,6 +48,6 @@ public class TeachingAssistants { course.addParticipant(teachingAssistant); this.courseRepository.save(course); }); - return "teaching_assistants/entity"; + return "redirect:/teaching_assistants"; } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/ImportStudents.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/ImportStudents.java index 65c5014..516efcc 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/ImportStudents.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/ImportStudents.java @@ -43,6 +43,12 @@ public class ImportStudents { return "courses/entity/import_students"; } + /** + * Performs the actual importing when this controller receives a post request. + * @param code The course code. + * @param file The file which the user has uploaded. + * @return Redirect to the course which will be shown. + */ @PostMapping( value = "/courses/{code}/import_students" ) diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/ExportStudentTeams.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/ExportStudentTeams.java new file mode 100644 index 0000000..c1ff9bd --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/ExportStudentTeams.java @@ -0,0 +1,59 @@ +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 org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.List; +import java.util.Optional; + +/** + * Controller for exporting team information into readable files. + */ +@Controller +public class ExportStudentTeams { + + private CourseRepository courseRepository; + + protected ExportStudentTeams(CourseRepository courseRepository) { + this.courseRepository = courseRepository; + } + + @GetMapping("/courses/{code}/student_teams/export") + public void export(@PathVariable String code, HttpServletResponse response) throws IOException { + Optional optionalCourse = this.courseRepository.findByCode(code); + if (!optionalCourse.isPresent()) { + response.sendError(404, "Course with code " + code + " not found"); + return; + } + + Course course = optionalCourse.get(); + + response.setContentType("text/*"); + + OutputStreamWriter writer = new OutputStreamWriter(response.getOutputStream()); + writer.write("Student Teams Export for course: " + course.getName() + "\n"); + + for (TeachingAssistantTeam teachingAssistantTeam : course.getTeachingAssistantTeams()) { + writer.write("Teaching Assistant Team " + teachingAssistantTeam.getId() + ", Github team: " + teachingAssistantTeam.getGithubTeamName() + "\n"); + List assignedTeams = teachingAssistantTeam.getAssignedStudentTeams(); + for (StudentTeam studentTeam : assignedTeams) { + writer.write("\tStudent Team " + studentTeam.getId() + ": "); + for (Student student : studentTeam.getStudents()) { + writer.write(student.getFullName() + " (S" + student.getStudentNumber() + "), "); + } + writer.write("\n"); + } + } + + response.flushBuffer(); + } +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/GenerateRepositories.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/GenerateRepositories.java index 07717c5..9d05182 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/GenerateRepositories.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/GenerateRepositories.java @@ -1,7 +1,9 @@ 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.util.github.GithubManager; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -16,9 +18,11 @@ import java.util.Optional; public class GenerateRepositories { private CourseRepository courseRepository; + private StudentTeamRepository studentTeamRepository; - protected GenerateRepositories(CourseRepository courseRepository) { + protected GenerateRepositories(CourseRepository courseRepository, StudentTeamRepository studentTeamRepository) { this.courseRepository = courseRepository; + this.studentTeamRepository = studentTeamRepository; } @GetMapping("/courses/{courseCode}/student_teams/generate_repositories") @@ -37,12 +41,25 @@ public class GenerateRepositories { System.out.println("Post received for " + courseCode); Optional optionalCourse = this.courseRepository.findByCode(courseCode); optionalCourse.ifPresent(course -> { - course.setGithubOrganizationName("InitializerTesting"); + GithubManager manager; + try { - GithubManager manager = new GithubManager(course.getApiKey()); - manager.generateStudentTeamRepository(course.getStudentTeams().get(0)); + manager = new GithubManager(course.getApiKey()); } catch (IOException e) { e.printStackTrace(); + return; + } + + for (StudentTeam studentTeam : course.getStudentTeams()) { + System.out.println("Generating repository for team " + studentTeam.getId()); + String repositoryName = manager.generateStudentTeamRepository(studentTeam); + if (repositoryName == null) { + System.err.println("An error occurred while generating a repository for student team " + studentTeam.getId()); + continue; + } + studentTeam.setGithubRepositoryName(repositoryName); + this.studentTeamRepository.save(studentTeam); + System.out.println("Done\n"); } }); 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 new file mode 100644 index 0000000..4a791d3 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java @@ -0,0 +1,112 @@ +package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.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.repositories.CourseRepository; +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.ArrayList; +import java.util.List; +import java.util.Optional; + +@Controller +public class InviteAllToRepository { + + private CourseRepository courseRepository; + + protected InviteAllToRepository(CourseRepository courseRepository) { + this.courseRepository = courseRepository; + } + + @GetMapping("/courses/{code}/students/invite_all") + public String get(@PathVariable String code, Model model) { + Optional optionalCourse =this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> model.addAttribute("course", course)); + + return "courses/entity/students/invite_all"; + } + + @PostMapping("/courses/{code}/students/invite_all") + public String post( + @PathVariable String code, + @RequestParam(value = "repository_name") String repositoryName, + @RequestParam(value = "api_keys") String apiKeys, + @RequestParam(value = "usernames", required = false) String usernames + ) { + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> { + + // Get a list of all the github usernames to invite. + List githubUsernames = new ArrayList<>(); + if (usernames != null && !usernames.isEmpty()) { + String[] usernamesRaw = usernames.split("\n"); + for (String username : usernamesRaw) { + githubUsernames.add(username.trim()); + } + } else { + for (Student student : course.getStudents()) { + githubUsernames.add(student.getGithubUsername()); + } + } + + // Get a list of all our available keys. + String[] rawKeys = apiKeys.split("\n"); + List keys = new ArrayList<>(); + for (String rawKey : rawKeys) { + keys.add(rawKey.trim()); + } + + String fullRepositoryName = course.getGithubOrganizationName() + '/' + repositoryName; + + int inviteCounter = 0; + GithubManager manager; + try { + manager = new GithubManager(keys.remove(0)); + } catch (IOException e) { + e.printStackTrace(); + return; + } + + List failedNames = new ArrayList<>(); + + while (!githubUsernames.isEmpty()) { + if (inviteCounter == 50) { + System.out.println("Used up 50 invites on key."); + try { + manager = new GithubManager(keys.remove(0)); + inviteCounter = 0; + } catch (IOException e) { + e.printStackTrace(); + failedNames.addAll(githubUsernames); + return; + } + } + + String username = githubUsernames.remove(0); + try { + manager.addCollaborator(fullRepositoryName, username, "pull"); + inviteCounter++; + System.out.println("\tInvited " + username); + } catch (IOException e) { + //e.printStackTrace(); + System.err.println("Could not add " + username + " to repository " + fullRepositoryName); + failedNames.add(username); + } + } + + System.err.println("The following github usernames have not been added."); + for (String username : failedNames) { + System.out.println("\t" + username); + } + }); + + return "redirect:/courses/{code}/students"; + } +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/AssignToStudentTeams.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/AssignToStudentTeams.java new file mode 100644 index 0000000..e1e23f2 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/AssignToStudentTeams.java @@ -0,0 +1,70 @@ +package nl.andrewlalis.teaching_assistant_assistant.controllers.courses.entity.teaching_assistant_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.people.teams.TeachingAssistantTeam; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; +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.util.*; + +@Controller +public class AssignToStudentTeams { + + private CourseRepository courseRepository; + + protected AssignToStudentTeams(CourseRepository courseRepository) { + this.courseRepository = courseRepository; + } + + @GetMapping("/courses/{code}/teaching_assistant_teams/assign_to_student_teams") + public String get(@PathVariable String code, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> model.addAttribute("course", course)); + + return "courses/entity/teaching_assistant_teams/assign_to_student_teams"; + } + + /** + * Randomly assigns a teaching assistant team to each student team in such a way that all teaching assistant teams + * should receive an equal number of student teams. + * @param code The code for the course in which to perform this action. + * @param seed A seed to use to determine randomness. + * @return The view for the list of student teams in this course, to see the results of the action. + */ + @PostMapping("/courses/{code}/teaching_assistant_teams/assign_to_student_teams") + public String post(@PathVariable String code, @RequestParam(value = "seed") int seed) { + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> { + List studentTeams = course.getStudentTeams(); + + LinkedList studentTeamQueue = new LinkedList<>(); + for (int i = 0; i < studentTeams.size(); i++) { + studentTeamQueue.add(i); + } + Collections.shuffle(studentTeamQueue, new Random(seed)); + + while (!studentTeamQueue.isEmpty()) { + for (TeachingAssistantTeam taTeam : course.getTeachingAssistantTeams()) { + if (studentTeamQueue.isEmpty()) { + break; + } + + StudentTeam studentTeam = studentTeams.get(studentTeamQueue.removeFirst()); + studentTeam.setAssignedTeachingAssistantTeam(taTeam); + taTeam.addAssignedStudentTeam(studentTeam); + } + } + + this.courseRepository.save(course); + }); + + return "redirect:/courses/{code}/student_teams"; + } + +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java index 542528d..fef4843 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java @@ -40,7 +40,6 @@ public class CreateTeachingAssistantTeam { @PathVariable String code, @RequestParam(value = "github_team_name") String githubTeamName, @RequestParam(value = "id_1") long id1, - @RequestParam(value = "id_2") long id2, Model model ) { TeachingAssistantTeam team = new TeachingAssistantTeam(); @@ -48,17 +47,12 @@ public class CreateTeachingAssistantTeam { Optional optionalCourse = this.courseRepository.findByCode(code); Optional optionalTeachingAssistant1 = this.teachingAssistantRepository.findById(id1); - Optional optionalTeachingAssistant2 = this.teachingAssistantRepository.findById(id2); - System.out.println("Course code: " + code + ", Team name: " + githubTeamName + ", TA 1: " + id1 + ", TA 2: " + id2); - - if (optionalCourse.isPresent() && optionalTeachingAssistant1.isPresent() && optionalTeachingAssistant2.isPresent()) { - System.out.println("All data available."); + if (optionalCourse.isPresent() && optionalTeachingAssistant1.isPresent()) { Course course = optionalCourse.get(); team.setCourse(course); team.addMember(optionalTeachingAssistant1.get()); - team.addMember(optionalTeachingAssistant2.get()); course.addTeachingAssistantTeam(team); this.courseRepository.save(course); diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/TeachingAssistantTeam.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/TeachingAssistantTeam.java index 85fb7a5..6cdf318 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/TeachingAssistantTeam.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/teams/TeachingAssistantTeam.java @@ -61,4 +61,16 @@ public class TeachingAssistantTeam extends Team { public void setGithubTeamName(String name) { this.githubTeamName = name; } + + public List getAssignedStudentTeams() { + return this.assignedStudentTeams; + } + + public void addAssignedStudentTeam(StudentTeam studentTeam) { + this.assignedStudentTeams.add(studentTeam); + } + + public void removeAssignedStudentTeam(StudentTeam studentTeam) { + this.assignedStudentTeams.remove(studentTeam); + } } 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 c314d8f..98ab14a 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 @@ -1,14 +1,23 @@ package nl.andrewlalis.teaching_assistant_assistant.util.github; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +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.HttpPut; +import org.apache.http.entity.StringEntity; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; import org.kohsuke.github.*; import java.io.File; import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URL; import java.nio.file.Files; -import java.util.ArrayList; import java.util.Calendar; -import java.util.List; /** * Encapsulates much of the github functionality that is needed. @@ -16,70 +25,178 @@ import java.util.List; public class GithubManager { private GitHub github; + private String apiKey; + /** + * Constructs this manager with an API key. + * @param apiKey The api key to use. + * @throws IOException if the key is invalid. + */ public GithubManager(String apiKey) throws IOException { this.github = GitHub.connectUsingOAuth(apiKey); + this.apiKey = apiKey; } + /** + * Constructs this manager with a username/password login combination. + * @param username The username to use. + * @param password The password for the above username. + * @throws IOException if the username or password are invalid. + */ public GithubManager(String username, String password) throws IOException { this.github = GitHub.connectUsingPassword(username, password); } - public void generateStudentTeamRepository(StudentTeam team) throws IOException { - GHOrganization organization = this.github.getOrganization(team.getCourse().getGithubOrganizationName()); + /** + * Generates a new Github Repository for the given student team. + * @param team The team to create a repository for. + * @return The name of the created repository. + */ + public String generateStudentTeamRepository(StudentTeam team) { + 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()); + return null; + } + Calendar today = Calendar.getInstance(); int year = today.get(Calendar.YEAR); String repositoryName = year + "_Team_" + team.getId(); // Get the TA team which manages this repository. - GHTeam teachingAssistantGithubTeam = organization.getTeamByName(team.getAssignedTeachingAssistantTeam().getGithubTeamName()); + 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()); + return null; + } // Create the repo GHCreateRepositoryBuilder repositoryBuilder = organization.createRepository(repositoryName); repositoryBuilder.wiki(false); - repositoryBuilder.issues(false); + repositoryBuilder.issues(true); repositoryBuilder.description("University of Groningen OOP Student Repository"); - repositoryBuilder.private_(false); + repositoryBuilder.private_(true); repositoryBuilder.autoInit(false); - repositoryBuilder.team(teachingAssistantGithubTeam); - GHRepository repository = repositoryBuilder.create(); + GHRepository repository; + try { + repository = repositoryBuilder.create(); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not create repository: " + repositoryName); + return null; + } - // Add getting started file. + try { + this.addRepositoryToTeam(teachingAssistantGithubTeam, repository); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not add repository " + repositoryName + " to team " + teachingAssistantGithubTeam.getName()); + return null; + } + + try { + 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); + return null; + } + + try { + this.createDevelopmentBranch(repository); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not create development branch for repository: " + repositoryName); + return null; + } + + try { + this.protectMasterBranch(repository, teachingAssistantGithubTeam); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not add protections to the master branch of " + repositoryName); + return null; + } + + try { + this.addStudentsAsCollaborators(repository, team); + } catch (IOException e) { + e.printStackTrace(); + System.err.println("Could not add students as collaborators to " + repositoryName); + return null; + } + + + return repositoryName; + } + + private void addStarterFile(GHRepository repository, String filePath, String fileName) throws IOException { GHContentBuilder contentBuilder = repository.createContent(); contentBuilder.branch("master"); contentBuilder.message("Initial Commit"); - File f = new File(getClass().getClassLoader().getResource("program_resources/getting_started.md").getFile()); - contentBuilder.content(Files.readAllBytes(f.toPath())); - contentBuilder.path("getting_started.md"); - contentBuilder.commit(); - // Create the development branch. - try { - String sha1 = repository.getBranch(repository.getDefaultBranch()).getSHA1(); - repository.createRef("refs/heads/development", sha1); - } catch (IOException e) { - e.printStackTrace(); + URL resource = getClass().getClassLoader().getResource(filePath); + if (resource == null) { + throw new IOException("Could not get resource identified by " + filePath); } + File f = new File(resource.getFile()); - // Protect master branch. + contentBuilder.content(Files.readAllBytes(f.toPath())); + contentBuilder.path(fileName); + contentBuilder.commit(); + } + + private void protectMasterBranch(GHRepository repository, GHTeam adminTeam) throws IOException { GHBranchProtectionBuilder protectionBuilder = repository.getBranch("master").enableProtection(); protectionBuilder.includeAdmins(false); protectionBuilder.restrictPushAccess(); - protectionBuilder.teamPushAccess(teachingAssistantGithubTeam); + protectionBuilder.teamPushAccess(adminTeam); protectionBuilder.addRequiredChecks("ci/circleci"); protectionBuilder.enable(); + } - List studentUsers = new ArrayList<>(team.getMembers().size()); - team.getStudents().forEach(student -> { - try { - studentUsers.add(this.github.getUser(student.getGithubUsername())); - } catch (IOException e) { - e.printStackTrace(); - } - }); - - //repository.addCollaborators(studentUsers); + private void addStudentsAsCollaborators(GHRepository repository, StudentTeam studentTeam) throws IOException { + for (Student student : studentTeam.getStudents()) { + this.addCollaborator(repository.getFullName(), student.getGithubUsername(), "push"); + } } + private void createDevelopmentBranch(GHRepository repository) throws IOException { + String sha1 = repository.getBranch(repository.getDefaultBranch()).getSHA1(); + repository.createRef("refs/heads/development", sha1); + } + + public void addCollaborator(String repositoryName, String githubUsername, String permission) throws IOException { + try { + String url = "https://api.github.com/repos/" + repositoryName + "/collaborators/" + githubUsername + "?access_token=" + this.apiKey; + HttpPut put = new HttpPut(url); + CloseableHttpClient client = HttpClientBuilder.create().build(); + ObjectMapper mapper = new ObjectMapper(); + ObjectNode root = mapper.createObjectNode(); + root.put("permission", permission); + String json = mapper.writeValueAsString(root); + put.setEntity(new StringEntity(json)); + HttpResponse response = client.execute(put); + + if (response.getStatusLine().getStatusCode() != 201) { + throw new IOException("Error adding collaborator via url " + url + " : " + response.getStatusLine().getStatusCode() + " " + response.getStatusLine().getReasonPhrase()); + } + } catch (JsonProcessingException | UnsupportedEncodingException e) { + e.printStackTrace(); + } + } + + private void addRepositoryToTeam(GHTeam team, GHRepository repository) throws IOException { + team.add(repository, GHOrganization.Permission.ADMIN); + } + } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 37dd396..2541540 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -4,7 +4,7 @@ spring.datasource.username=root spring.datasource.password=root spring.datasource.url=jdbc:mysql://localhost:3306/teaching_assistant_assistant?serverTimezone=UTC # -#spring.jpa.properties.hibernate.show_sql=false +spring.jpa.properties.hibernate.show_sql=false #spring.jpa.properties.hibernate.use_sql_comments=true #spring.jpa.properties.hibernate.format_sql=true #spring.jpa.properties.hibernate.type=trace @@ -12,4 +12,3 @@ spring.datasource.url=jdbc:mysql://localhost:3306/teaching_assistant_assistant?s spring.security.user.name=tester spring.security.user.password=tester -spring.data.rest.e diff --git a/src/main/resources/program_resources/getting_started.md b/src/main/resources/program_resources/getting_started.md index c4db326..13dd306 100644 --- a/src/main/resources/program_resources/getting_started.md +++ b/src/main/resources/program_resources/getting_started.md @@ -3,11 +3,17 @@ Welcome to the University of Groningen's Object Oriented Programming course! We' ## Setting Up To begin, you'll need to download this repository you're looking at right now. To do that, run the following command (where `repository_url` is the URL of this repository): + ``` git clone ``` -Now you should have a local directory with the same name as this repository. Checkout your `development` branch, since you do not have permission as a student to make changes to the master branch. From here, we need to copy the files from the assignments repository into ours. To do this, we first add a new 'remote' to our git repository, and then pull from that repository's `master` branch. +Now you should have a local directory with the same name as this repository. Checkout your `development` branch, since you do not have permission as a student to make changes to the master branch. + +From here, we need to copy the files from the assignments repository into ours. To do this, we first add a new 'remote' to our local git repository, and then pull from that repository's `master` branch. + +> Note that if you have not received an invitation to the 2019_assignments repository yet, you'll have to wait until you can complete this part. Thank you for your patience. + ``` git checkout development @@ -19,4 +25,38 @@ git pull assignments master --allow-unrelated-histories Now you should see all the files just as they appear in the assignments repository. ## Developing Your Code and Submitting -While previous knowledge of git may be useful, it should not really be needed for you to do well in this course. \ No newline at end of file +While previous knowledge of git may be useful, it should not really be needed for you to do well in this course, as long as you know how to submit each assignment. + +When you have finished working on your code, you should first add any new files which you have created (`-A` means "all files". Be careful, since you should not upload compiled class files). + +``` +git add -A +``` + +Now, commit all changes that have been made (`-a` means "all changed files", and `-m` allows you to supply a commit message. If you do not add `-m`, git will try to open a text editor for you to write a message in). + +``` +git commit -a -m "This is a short message about what I did." +``` + +To make these committed changes public, you need to upload them to the remote repository. This is done by 'pushing'. + +``` +git push +``` + +If you refresh the page for your repository on github.com, you should now see changes in whatever branch you pushed to (most likely `development`). + +### Submission +Once you've committed and push a few times, and you feel that your code is ready to be submitted, create a new pull request from your `development` branch onto the `master` branch. When you do this, a continuous integration service will compile your code and run `mvn integration-test` to see that all tests (if any) pass. If your code does not compile or pass all tests, don't bother making a submission; we won't grade it. Fix it and submit a working version. + +If you have made a pull request, but then would like to make some last-minute changes, you can do this by simply adding more commits to the `development` branch. Any pushed commits will simply be added to an existing pull request. + +## Questions, Comments, Concerns +Should you have any questions about Github, the above workflow, or other technical questions, please first see if Google can help, and then if you still cannot figure out what to do, make an issue in your repository that adheres to the following guidelines: + +* What is the problem? +* What have you already tried to do to fix it? +* How can my teaching assistant replicate the problem? + +By giving us this information, you make it much easier for us to give meaningful feedback as quickly as possible. diff --git a/src/main/resources/templates/courses/entity/student_teams.html b/src/main/resources/templates/courses/entity/student_teams.html index eefcc85..4772726 100644 --- a/src/main/resources/templates/courses/entity/student_teams.html +++ b/src/main/resources/templates/courses/entity/student_teams.html @@ -32,8 +32,16 @@
- + None + + + + None Generate Repositories + 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 42dd6d4..dc1af36 100644 --- a/src/main/resources/templates/courses/entity/student_teams/entity.html +++ b/src/main/resources/templates/courses/entity/student_teams/entity.html @@ -16,6 +16,14 @@ +
  • + Assigned Teaching Assistant Team: + +
  • Members:
      @@ -31,7 +39,18 @@ diff --git a/src/main/resources/templates/courses/entity/student_teams/generate_repositories.html b/src/main/resources/templates/courses/entity/student_teams/generate_repositories.html index 6cf565a..e0532e0 100644 --- a/src/main/resources/templates/courses/entity/student_teams/generate_repositories.html +++ b/src/main/resources/templates/courses/entity/student_teams/generate_repositories.html @@ -9,6 +9,10 @@

      Generate Repositories for all Student Teams in

      +

      + Be careful, this may take a little while... +

      +
      diff --git a/src/main/resources/templates/courses/entity/students/invite_all.html b/src/main/resources/templates/courses/entity/students/invite_all.html new file mode 100644 index 0000000..e0c4bf6 --- /dev/null +++ b/src/main/resources/templates/courses/entity/students/invite_all.html @@ -0,0 +1,40 @@ + + + + + Generate Repositories + + + +
      +

      Invite All Students in to Repository

      + +

      + Be careful, this may take a little while... +

      + + + + + + + + + + + + + +
      + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/courses/entity/teaching_assistant_teams.html b/src/main/resources/templates/courses/entity/teaching_assistant_teams.html index b1eb8b7..b8fbabf 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams.html @@ -39,6 +39,9 @@ +
      diff --git a/src/main/resources/templates/courses/entity/teaching_assistant_teams/assign_to_student_teams.html b/src/main/resources/templates/courses/entity/teaching_assistant_teams/assign_to_student_teams.html new file mode 100644 index 0000000..fd3b330 --- /dev/null +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams/assign_to_student_teams.html @@ -0,0 +1,21 @@ + + + + Assign To Student Teams + + + +
      +
      + + + +
      +
      + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html b/src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html index b37e0d9..29af127 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html @@ -6,9 +6,6 @@
      -

      - Create your course here -

      @@ -18,19 +15,12 @@ - - -
      diff --git a/src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html b/src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html index 24c3041..4f5153c 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html @@ -22,6 +22,16 @@
  • +
  • + Assigned Student Teams: + +
  • diff --git a/src/main/resources/templates/students.html b/src/main/resources/templates/students.html index 223384c..0dc3c9a 100644 --- a/src/main/resources/templates/students.html +++ b/src/main/resources/templates/students.html @@ -16,6 +16,7 @@ +