From 7dff57ac98c1ec8b6f481c08b0afd6c0b9fa4595 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Wed, 17 Apr 2019 22:16:05 +0200 Subject: [PATCH 01/12] Got CSV reading to work properly. --- pom.xml | 6 + ...TeachingAssistantAssistantApplication.java | 7 +- .../controllers/Courses.java | 1 - .../controllers/courses/ImportStudents.java | 61 ++++++++ .../model/Course.java | 8 +- .../model/people/Person.java | 41 +++++- .../model/people/Student.java | 44 +++++- .../model/people/TeachingAssistant.java | 4 +- .../util/sample_data/CourseGenerator.java | 4 +- .../util/sample_data/StudentGenerator.java | 2 +- .../TeachingAssistantGenerator.java | 2 +- .../team_importing/StudentRecordEntry.java | 44 ++++++ .../team_importing/StudentTeamImporter.java | 136 ++++++++++++++++++ .../resources/templates/courses/entity.html | 2 +- .../templates/courses/import_students.html | 26 ++++ .../courses/import_teaching_assistants.html | 10 ++ 16 files changed, 381 insertions(+), 17 deletions(-) create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentRecordEntry.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java create mode 100644 src/main/resources/templates/courses/import_students.html create mode 100644 src/main/resources/templates/courses/import_teaching_assistants.html diff --git a/pom.xml b/pom.xml index 086d14c..640ee77 100644 --- a/pom.xml +++ b/pom.xml @@ -79,6 +79,12 @@ mysql-connector-java 8.0.15 + + + org.apache.commons + commons-csv + 1.5 + 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 984e6f9..22d7739 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/TeachingAssistantAssistantApplication.java @@ -27,6 +27,11 @@ 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/Courses.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Courses.java index 6935735..ea5df0e 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Courses.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/Courses.java @@ -41,7 +41,6 @@ public class Courses { consumes = "application/x-www-form-urlencoded" ) public String post(@ModelAttribute Course course) { - System.out.println("Object submitted: " + course); this.courseRepository.save(course); return "courses/entity"; } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java new file mode 100644 index 0000000..02819db --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java @@ -0,0 +1,61 @@ +package nl.andrewlalis.teaching_assistant_assistant.controllers.courses; + +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.TeamRepository; +import nl.andrewlalis.teaching_assistant_assistant.util.team_importing.StudentTeamImporter; +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 org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.util.List; +import java.util.Optional; + +/** + * Controller for importing students from a CSV sheet. + */ +@Controller +public class ImportStudents { + + private CourseRepository courseRepository; + + private TeamRepository teamRepository; + + protected ImportStudents(CourseRepository courseRepository, TeamRepository teamRepository) { + this.courseRepository = courseRepository; + this.teamRepository = teamRepository; + } + + @GetMapping("/courses/{code}/import_students") + public String get(@PathVariable String code, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> model.addAttribute("course", course)); + return "courses/import_students"; + } + + @PostMapping( + value = "/courses/{code}/import_students" + ) + public String post(@PathVariable String code, @RequestParam MultipartFile file) { + Optional optionalCourse = this.courseRepository.findByCode(code); + + if (!optionalCourse.isPresent()) { + System.out.println("No course found."); + return "redirect:/courses"; + } + + try { + List studentTeams = StudentTeamImporter.importFromCSV(file.getInputStream(), optionalCourse.get()); + } catch (IOException e) { + e.printStackTrace(); + } + + return "redirect:/courses/{code}"; + } +} 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 25d41db..483b294 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 @@ -77,12 +77,12 @@ public class Course extends BasicEntity { this.code = code; } - public void addStudentGroup(StudentTeam group) { - this.studentTeams.add(group); + public void addStudentTeam(StudentTeam team) { + this.studentTeams.add(team); } - public void addTeachingAssistantGroup(TeachingAssistantTeam group) { - this.teachingAssistantTeams.add(group); + 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 47cef1d..1029403 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 @@ -26,6 +26,9 @@ public abstract class Person extends BasicEntity { @Column private String emailAddress; + @Column + private String githubUsername; + /** * The list of teams that this person belongs to. Because a person can belong to more than one team, it is implied * that each person exists in only one location in the database. Therefore, if one person is enrolled in two courses @@ -50,12 +53,14 @@ public abstract class Person extends BasicEntity { * @param firstName The person's first name. * @param lastName The person's last name. * @param emailAddress The person's email address. + * @param githubUsername The person's github username; */ - public Person(String firstName, String lastName, String emailAddress) { + public Person(String firstName, String lastName, String emailAddress, String githubUsername) { this(); this.firstName = firstName; this.lastName = lastName; this.emailAddress = emailAddress; + this.githubUsername = githubUsername; } public void assignToTeam(Team team) { @@ -82,8 +87,40 @@ public abstract class Person extends BasicEntity { return this.emailAddress; } + public String getGithubUsername() { + return this.githubUsername; + } + + /** + * Determines if two Persons are equal. They are considered equal when all of the basic identifying information + * about the person is the same, regardless of case. + * @param o The other object. + * @return True if the other object is the same person, or false if not. + */ + @Override + public boolean equals(Object o) { + if (o == null) { + return false; + } + + if (o instanceof Person) { + Person p = (Person) o; + return ( + this.getFirstName().equalsIgnoreCase(p.getFirstName()) + && this.getLastName().equalsIgnoreCase(p.getLastName()) + && this.getEmailAddress().equalsIgnoreCase(p.getEmailAddress()) + && this.getGithubUsername().equalsIgnoreCase(p.getGithubUsername()) + ); + } + + return false; + } + @Override public String toString() { - return this.getFirstName() + ' ' + this.getLastName() + '[' + this.getId() + ']'; + return "First Name: " + this.getFirstName() + + ", Last Name: " + this.getLastName() + + ", Email: " + this.getEmailAddress() + + ", Github Username: " + this.getGithubUsername(); } } 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 e61216f..26963bf 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 @@ -1,5 +1,6 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people; +import javax.persistence.Column; import javax.persistence.Entity; /** @@ -8,13 +9,52 @@ import javax.persistence.Entity; @Entity public class Student extends Person { + /** + * The student's unique student number, as given by the university. + */ + @Column(unique = true, nullable = false) + private int studentNumber; + /** * Default constructor for JPA. */ protected Student() {} - public Student(String firstName, String lastName, String emailAddress) { - super(firstName, lastName, emailAddress); + /** + * Constructs a new student with all the properties of a Person, and any extra properties. + * @param firstName The student's first name. + * @param lastName The student's last name. + * @param emailAddress The student's email address. + * @param githubUsername The student's Github username. + * @param studentNumber The student's unique student number. + */ + public Student(String firstName, String lastName, String emailAddress, String githubUsername, int studentNumber) { + super(firstName, lastName, emailAddress, githubUsername); + + this.studentNumber = studentNumber; } + public int getStudentNumber() { + return studentNumber; + } + + public void setStudentNumber(int studentNumber) { + this.studentNumber = studentNumber; + } + + /** + * Determines if two students are equal. They are considered equal if their person attributes are the same, or + * their student-specific attributes are equal. + * @param o The other object. + * @return True if the other object is the same student. + */ + @Override + public boolean equals(Object o) { + return super.equals(o) || this.getStudentNumber() == ((Student) o).getStudentNumber(); + } + + @Override + public String toString() { + return super.toString() + ", Student Number: " + this.getStudentNumber(); + } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/TeachingAssistant.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/TeachingAssistant.java index 19dfcf8..dee492b 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/TeachingAssistant.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/people/TeachingAssistant.java @@ -15,7 +15,7 @@ public class TeachingAssistant extends Person { } - public TeachingAssistant(String firstName, String lastName, String emailAddress) { - super(firstName, lastName, emailAddress); + public TeachingAssistant(String firstName, String lastName, String githubUsername, String emailAddress) { + super(firstName, lastName, emailAddress, githubUsername); } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/CourseGenerator.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/CourseGenerator.java index eecf759..0273c0d 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/CourseGenerator.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/CourseGenerator.java @@ -56,11 +56,11 @@ public class CourseGenerator extends TestDataGenerator { List studentTeams = this.generateStudentTeams(); List teachingAssistantTeams = this.generateTeachingAssistantTeams(); for (StudentTeam team : studentTeams) { - course.addStudentGroup(team); + course.addStudentTeam(team); team.setCourse(course); } for (TeachingAssistantTeam team : teachingAssistantTeams) { - course.addTeachingAssistantGroup(team); + course.addTeachingAssistantTeam(team); team.setCourse(course); } return course; diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/StudentGenerator.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/StudentGenerator.java index 23240c4..01945b5 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/StudentGenerator.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/StudentGenerator.java @@ -12,6 +12,6 @@ public class StudentGenerator extends PersonGenerator { public Student generate() { String firstName = this.getRandomFirstName(); String lastName = this.getRandomLastName(); - return new Student(firstName, lastName, this.getRandomEmailAddress(firstName, lastName)); + return new Student(firstName, lastName, this.getRandomEmailAddress(firstName, lastName), null, this.getRandomInteger(0, 100000000)); } } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/TeachingAssistantGenerator.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/TeachingAssistantGenerator.java index e4c9a89..8b2c8d4 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/TeachingAssistantGenerator.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/sample_data/TeachingAssistantGenerator.java @@ -12,6 +12,6 @@ public class TeachingAssistantGenerator extends PersonGenerator importFromCSV(InputStream fileInputStream, Course course) throws IOException { + Iterable records = CSVFormat.DEFAULT + .withSkipHeaderRecord() + .withHeader("timestamp", "email", "name", "number", "github_username", "has_partner", "partner_name", "partner_number", "partner_email", "partner_github_username") + .parse(new InputStreamReader(fileInputStream)); + + List studentEntries = extractStudentsFromRecords(records); + + for (StudentRecordEntry entry : studentEntries) { + System.out.println(entry.toString()); + } + + return new ArrayList<>(); + + } + + /** + * Extracts all student data from a list of records, and automatically discards outdated responses (those where the + * same student submitted more than once). + * @param records The list of records in the CSV file. + * @return A mapping for each timestamp to + */ + private static List extractStudentsFromRecords(Iterable records) { + List studentEntries = new ArrayList<>(); + + for (CSVRecord record : records) { + // Parse the actual student. + Student s = parseStudentRecordData(record.get("name"), record.get("email"), record.get("github_username"), record.get("number")); + + // Parse the student's preferred partner, if they exist. + Student preferredPartner = null; + if (record.get("has_partner").equalsIgnoreCase("yes")) { + preferredPartner = parseStudentRecordData(record.get("partner_name"), record.get("partner_email"), record.get("partner_github_username"), record.get("partner_number")); + } + + // Parse the timestamp. + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd h:mm:ss a O "); + ZonedDateTime dateTime = ZonedDateTime.parse(record.get("timestamp") + ' ', formatter); + // A space is added because of a java bug: https://stackoverflow.com/questions/37287103/why-does-gmt8-fail-to-parse-with-pattern-o-despite-being-copied-straight-ou + + studentEntries.add(new StudentRecordEntry(dateTime, s, preferredPartner)); + } + + studentEntries = removeDuplicateEntries(studentEntries); + + return studentEntries; + } + + + private static List removeDuplicateEntries(List studentEntries) { + List uniqueStudentEntries = new ArrayList<>(); + + for (StudentRecordEntry entry : studentEntries) { + // Check for if the current entry's student already exists. + boolean duplicateFound = false; + for (StudentRecordEntry existingEntry : uniqueStudentEntries) { + if (entry.getStudent().equals(existingEntry.getStudent())) { + duplicateFound = true; + // Check if the existing entry is older than the new one; it should be overwritten. + if (existingEntry.getDateTime().isBefore(entry.getDateTime())) { + uniqueStudentEntries.remove(existingEntry); + uniqueStudentEntries.add(entry); + break; + } + } + } + + if (!duplicateFound) { + uniqueStudentEntries.add(entry); + } + } + + return uniqueStudentEntries; + } + + /** + * Creates a student object from given entries in a record obtained from a CSV file. + * @param name The name value. + * @param email The email value. + * @param githubUsername The github_username value. + * @param studentNumber The number value. + * @return A student object constructed from the given data. + */ + private static Student parseStudentRecordData(String name, String email, String githubUsername, String studentNumber) { + // Extract a sensible first and last name. + String[] nameSegments = name.split(" "); + String firstName = "No First Name Given"; + String lastName = "No Last Name Given"; + + if (nameSegments.length > 0) { + firstName = nameSegments[0]; + } + + if (nameSegments.length > 1) { + lastName = String.join(" ", Arrays.copyOfRange(nameSegments, 1, nameSegments.length)); + } + + // Extract a sensible github username. + String githubURL = "https://github.com/"; + + if (githubUsername.startsWith(githubURL)) { + githubUsername = githubUsername.substring(githubURL.length()); + } + + // Create the student. + return new Student(firstName, lastName, email, githubUsername, Integer.parseInt(studentNumber)); + } + +} diff --git a/src/main/resources/templates/courses/entity.html b/src/main/resources/templates/courses/entity.html index 7355aa8..511b5b4 100644 --- a/src/main/resources/templates/courses/entity.html +++ b/src/main/resources/templates/courses/entity.html @@ -48,7 +48,7 @@ Import students from CSV diff --git a/src/main/resources/templates/courses/import_students.html b/src/main/resources/templates/courses/import_students.html new file mode 100644 index 0000000..f825b6b --- /dev/null +++ b/src/main/resources/templates/courses/import_students.html @@ -0,0 +1,26 @@ + + + + Import Students via CSV + + + +
+

+ Please select a CSV file to import. +

+ +
+ + + +
+ +
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/courses/import_teaching_assistants.html b/src/main/resources/templates/courses/import_teaching_assistants.html new file mode 100644 index 0000000..d538fea --- /dev/null +++ b/src/main/resources/templates/courses/import_teaching_assistants.html @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file From df92a2c2d50fb20da732ed20ad3bf9365774e099 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Wed, 17 Apr 2019 23:42:37 +0200 Subject: [PATCH 02/12] Added correct algorithm! Yay! --- .../model/people/teams/Team.java | 2 +- .../team_importing/StudentTeamImporter.java | 103 +++++++++++++++++- 2 files changed, 101 insertions(+), 4 deletions(-) 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 255883f..82a43f7 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 @@ -91,7 +91,7 @@ public abstract class Team

extends BasicEntity { public String toString() { StringBuilder sb = new StringBuilder(); for (P p : this.getMembers()) { - sb.append(p.toString()).append(", "); + sb.append(p.getFullName()).append(", "); } return sb.toString(); } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java index 0723148..774991d 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java @@ -11,7 +11,10 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; /** * Provides some methods to streamline the process of transforming a CSV file of student data into a list of valid teams @@ -33,19 +36,27 @@ public class StudentTeamImporter { List studentEntries = extractStudentsFromRecords(records); + System.out.println("<<< Entries (" + studentEntries.size() + ") >>>"); for (StudentRecordEntry entry : studentEntries) { System.out.println(entry.toString()); } - return new ArrayList<>(); + List studentTeams = generateTeamsFromStudentEntries(studentEntries, 0); + System.out.println("<<< Teams (" + studentTeams.size() + ") >>>"); + for (StudentTeam team : studentTeams) { + System.out.println("Team: " + team); + } + + return studentTeams; } /** * Extracts all student data from a list of records, and automatically discards outdated responses (those where the * same student submitted more than once). * @param records The list of records in the CSV file. - * @return A mapping for each timestamp to + * @return A list of all student entries. A student entry is an intermediate object used to organize the parsed data + * from each record in the CSV file. */ private static List extractStudentsFromRecords(Iterable records) { List studentEntries = new ArrayList<>(); @@ -73,7 +84,93 @@ public class StudentTeamImporter { return studentEntries; } + /** + * Generates a list of teams from a list of unique student entries. The algorithm is described as follows: + * + * 1. Select the next student from the list of entries. + * 2. If the student (A) has specified a partner (B), search for that partner (B) in the list of entries. + * 3. If the partner (B) also specified the student (A) as their preferred partner, a team is created from the two. + * a. Pop both student entries from the list of entries. + * 4. Else, place student (A) in a queue of single students. + * a. Pop student (A) from the list of entries. + * 5. If the list of entries is not empty, go to 1. + * 6. The list of entries is now empty, and zero or more entries exist in a queue of single students. + * 7. Pop two (or as many as possible) students randomly from the queue, and form a team from them. + * 8. Repeat until the queue of single students is empty. + * 9. There is now a list of student teams and no student should remain unprocessed. + * + * @param entries A list of record entries. + * @param seed A seed to use for randomization of single student teams. + * @return A list of student teams. + */ + private static List generateTeamsFromStudentEntries(List entries, long seed) { + List teams = new ArrayList<>(); + List singleStudentsEntryQueue = new ArrayList<>(); + Random random = new Random(seed); + // Realize step 5. Loop until there are no more processed entries. + while (!entries.isEmpty()) { + // Step 1. Select the next student from the list of entries. + StudentRecordEntry entry = entries.remove(0); + + // Step 2. If the student has a partner, search for that partner. + if (entry.hasPartner()) { + boolean partnerFound = false; // Use this to keep track of if a partner is actually found. + for (int i = 0; i < entries.size(); i++) { + StudentRecordEntry secondEntry = entries.get(i); + // Step 3. If the partner specifies their partner as the student (both want each other), then create a team. + if ( + entry.getPartnerStudent().equals(secondEntry.getStudent()) + && secondEntry.hasPartner() + && secondEntry.getPartnerStudent().equals(entry.getStudent()) + ) { + partnerFound = true; + + entries.remove(i); // Step 3a. The first student has been popped, so pop this one. + + StudentTeam team = new StudentTeam(); + team.addMember(entry.getStudent()); + team.addMember(secondEntry.getStudent()); + teams.add(team); + + break; + } + } + + // Step 4. If no partner was found, then add this entry to the queue of single students. + if (!partnerFound) { + singleStudentsEntryQueue.add(entry); + } + } else { + // Also Step 4. The student chose to have no partner, so add this entry to the queue of single students. + singleStudentsEntryQueue.add(entry); + } + } + + // We are now at step 6. + while (!singleStudentsEntryQueue.isEmpty()) { + StudentTeam team = new StudentTeam(); + StudentRecordEntry firstRandomEntry = singleStudentsEntryQueue.remove(random.nextInt(singleStudentsEntryQueue.size())); + team.addMember(firstRandomEntry.getStudent()); + + // Check if there's another student in the queue. + if (!singleStudentsEntryQueue.isEmpty()) { + StudentRecordEntry secondRandomEntry = singleStudentsEntryQueue.remove(random.nextInt(singleStudentsEntryQueue.size())); + team.addMember(secondRandomEntry.getStudent()); + } + + teams.add(team); + } + + return teams; + } + + /** + * Scans a list of student entries and removes entries which are outdated by newer entries by the same student, such + * that the resulting list contains only one unique entry per student. + * @param studentEntries The list of student entries, possibly containing duplicates. + * @return A list of student entries in which all outdated duplicates have been removed. + */ private static List removeDuplicateEntries(List studentEntries) { List uniqueStudentEntries = new ArrayList<>(); From d9d9a51707cc7e2ca1364dad14fad053322d0744 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Fri, 19 Apr 2019 16:17:09 +0200 Subject: [PATCH 03/12] Switched to mysql implementation and added persistence for students. --- .../config/DataSourceConfig.java | 34 ----------------- .../controllers/courses/ImportStudents.java | 37 +++++++++++++++++-- .../model/Course.java | 32 ++++++++++++++++ .../model/people/Person.java | 25 ++++++++++++- .../model/people/teams/StudentTeam.java | 15 ++++++-- .../people/teams/TeachingAssistantTeam.java | 12 ++++-- .../model/people/teams/Team.java | 18 ++++----- .../model/repositories/StudentRepository.java | 17 +++++++++ .../repositories/StudentTeamRepository.java | 7 ++++ src/main/resources/application.properties | 11 ++++-- src/main/resources/templates/courses.html | 2 + .../resources/templates/courses/entity.html | 16 ++++++++ 12 files changed, 166 insertions(+), 60 deletions(-) delete mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/config/DataSourceConfig.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentRepository.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentTeamRepository.java diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/config/DataSourceConfig.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/config/DataSourceConfig.java deleted file mode 100644 index f6b0a94..0000000 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/config/DataSourceConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package nl.andrewlalis.teaching_assistant_assistant.config; - -import org.springframework.boot.context.properties.ConfigurationProperties; -import org.springframework.boot.jdbc.DataSourceBuilder; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.Primary; - -import javax.sql.DataSource; - -/** - * Configures the data source for this application. - */ -@Configuration -public class DataSourceConfig { - - private static final String USERNAME = "root"; - private static final String PASSWORD = "root"; - private static final String DB_HOST = "localhost"; - private static final String DB_NAME = "teaching_assistant_assistant"; - - @ConfigurationProperties(prefix = "spring.datasource") - @Bean - @Primary - public DataSource getDataSource() { - return DataSourceBuilder - .create() - .url("jdbc:h2:~/" + DB_NAME) - .username(USERNAME) - .password(PASSWORD) - .build(); - } - -} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java index 02819db..b858d4c 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/ImportStudents.java @@ -3,7 +3,8 @@ package nl.andrewlalis.teaching_assistant_assistant.controllers.courses; 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.TeamRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.StudentTeamRepository; import nl.andrewlalis.teaching_assistant_assistant.util.team_importing.StudentTeamImporter; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; @@ -25,11 +26,14 @@ public class ImportStudents { private CourseRepository courseRepository; - private TeamRepository teamRepository; + private StudentTeamRepository studentTeamRepository; - protected ImportStudents(CourseRepository courseRepository, TeamRepository teamRepository) { + private StudentRepository studentRepository; + + protected ImportStudents(CourseRepository courseRepository, StudentTeamRepository studentTeamRepository, StudentRepository studentRepository) { this.courseRepository = courseRepository; - this.teamRepository = teamRepository; + this.studentTeamRepository = studentTeamRepository; + this.studentRepository = studentRepository; } @GetMapping("/courses/{code}/import_students") @@ -50,8 +54,33 @@ public class ImportStudents { return "redirect:/courses"; } + Course course = optionalCourse.get(); + try { List studentTeams = StudentTeamImporter.importFromCSV(file.getInputStream(), optionalCourse.get()); + + // Save all the new students first, then save all the teams they belong to. + for (StudentTeam team : studentTeams) { + team.getMembers().forEach(student -> student.assignToCourse(course)); + this.studentRepository.saveAll(team.getStudents()); + team.setCourse(course); + } + + studentTeams.forEach(team -> { + team.getMembers().forEach(student -> { + student.assignToCourse(course); + course.addParticipant(student); + }); + team.setCourse(course); + course.addStudentTeam(team); + + //this.studentRepository.saveAll((team.getMembers())); + }); + + //this.studentTeamRepository.saveAll(studentTeams); + + this.courseRepository.save(course); + } catch (IOException e) { e.printStackTrace(); } 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 483b294..fd1879d 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 @@ -1,6 +1,8 @@ package nl.andrewlalis.teaching_assistant_assistant.model; import nl.andrewlalis.teaching_assistant_assistant.model.assignments.Assignment; +import nl.andrewlalis.teaching_assistant_assistant.model.people.Person; +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; @@ -57,6 +59,19 @@ public class Course extends BasicEntity { ) private List teachingAssistantTeams; + /** + * The list of all participants in this course, both teaching assistants and students. + */ + @ManyToMany( + cascade = CascadeType.ALL + ) + @JoinTable( + name = "course_participants", + joinColumns = @JoinColumn(name = "course_id"), + inverseJoinColumns = @JoinColumn(name = "person_id") + ) + private List participants; + /** * Default constructor for JPA. */ @@ -64,6 +79,7 @@ public class Course extends BasicEntity { this.assignments = new ArrayList<>(); this.studentTeams = new ArrayList<>(); this.teachingAssistantTeams = new ArrayList<>(); + this.participants = new ArrayList<>(); } /** @@ -85,6 +101,12 @@ public class Course extends BasicEntity { this.teachingAssistantTeams.add(team); } + public void addParticipant(Person person) { + if (!this.participants.contains(person)) { + this.participants.add(person); + } + } + /* Getters and Setters */ @@ -109,6 +131,16 @@ public class Course extends BasicEntity { return teachingAssistantTeams; } + public List getStudents() { + List students = new ArrayList<>(); + this.participants.forEach(participant -> { + if (participant instanceof Student) { + students.add((Student) participant); + } + }); + return students; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(this.getName()).append('\n'); 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 1029403..7909a7f 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 @@ -1,6 +1,7 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people; import nl.andrewlalis.teaching_assistant_assistant.model.BasicEntity; +import nl.andrewlalis.teaching_assistant_assistant.model.Course; import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.Team; import javax.persistence.*; @@ -41,11 +42,21 @@ public abstract class Person extends BasicEntity { ) private List teams; + /** + * The list of courses that this person belongs to. + */ + @ManyToMany( + fetch = FetchType.LAZY, + mappedBy = "participants" + ) + private List courses; + /** * Default constructor for JPA. */ protected Person () { this.teams = new ArrayList<>(); + this.courses = new ArrayList<>(); } /** @@ -64,7 +75,15 @@ public abstract class Person extends BasicEntity { } public void assignToTeam(Team team) { - this.teams.add(team); + if (!this.teams.contains(team)) { + this.teams.add(team); + } + } + + public void assignToCourse(Course course) { + if (!this.courses.contains(course)) { + this.courses.add(course); + } } /* @@ -91,6 +110,10 @@ public abstract class Person extends BasicEntity { return this.githubUsername; } + public List getCourses() { + return this.courses; + } + /** * Determines if two Persons are equal. They are considered equal when all of the basic identifying information * about the person is the same, regardless of case. 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 70fddce..f4be0fc 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,18 +1,20 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people.teams; 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; import javax.persistence.Entity; import javax.persistence.FetchType; import javax.persistence.OneToMany; +import java.util.ArrayList; import java.util.List; /** * A group of students. */ @Entity -public class StudentTeam extends Team { +public class StudentTeam extends Team { /** * The list of assignment grades which this student group has received. @@ -27,8 +29,13 @@ public class StudentTeam extends Team { */ public StudentTeam() {} - @Override - public void addMember(Student person) { - this.getMembers().add(person); + public List getStudents() { + List people = super.getMembers(); + List students = new ArrayList<>(); + people.forEach(person -> { + students.add((Student) person); + }); + return students; } + } 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 5361bc1..830a89f 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 @@ -1,6 +1,7 @@ package nl.andrewlalis.teaching_assistant_assistant.model.people.teams; import nl.andrewlalis.teaching_assistant_assistant.model.assignments.grades.SectionGrade; +import nl.andrewlalis.teaching_assistant_assistant.model.people.Person; import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant; import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistantRole; @@ -8,13 +9,14 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; +import java.util.ArrayList; import java.util.List; /** * A group of teaching assistants. */ @Entity -public class TeachingAssistantTeam extends Team { +public class TeachingAssistantTeam extends Team { /** * The role that this teaching assistant team plays. @@ -33,8 +35,10 @@ public class TeachingAssistantTeam extends Team { */ public TeachingAssistantTeam() {} - @Override - public void addMember(TeachingAssistant person) { - this.getMembers().add(person); + public List getTeachingAssistants() { + List people = super.getMembers(); + List teachingAssistants = new ArrayList<>(people.size()); + people.forEach(person -> teachingAssistants.add((TeachingAssistant) person)); + return teachingAssistants; } } 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 82a43f7..0e81635 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 @@ -17,7 +17,7 @@ import java.util.List; @Inheritance( strategy = InheritanceType.JOINED ) -public abstract class Team

extends BasicEntity { +public abstract class Team extends BasicEntity { /** * The list of members in this group. @@ -30,7 +30,7 @@ public abstract class Team

extends BasicEntity { joinColumns = @JoinColumn(name = "team_id"), inverseJoinColumns = @JoinColumn(name = "person_id") ) - protected List

members; + protected List members; /** * The course that this team belongs to. A team cannot exist on its own, it must belong to a course. @@ -47,23 +47,23 @@ public abstract class Team

extends BasicEntity { this.members = new ArrayList<>(); } - public void addMember(P person) { + public void addMember(Person person) { if (!this.containsMember(person)) { this.members.add(person); } } - public void addMembers(List

people) { - for (P person : people) { + public void addMembers(List people) { + for (Person person : people) { this.addMember(person); } } - public boolean containsMember(P person) { + public boolean containsMember(Person person) { return this.members.contains(person); } - public void removeMember(P person) { + public void removeMember(Person person) { this.members.remove(person); } @@ -79,7 +79,7 @@ public abstract class Team

extends BasicEntity { * Gets a list of all members of this team. * @return A list of all the members in this team. */ - public List

getMembers() { + public List getMembers() { return this.members; } @@ -90,7 +90,7 @@ public abstract class Team

extends BasicEntity { @Override public String toString() { StringBuilder sb = new StringBuilder(); - for (P p : this.getMembers()) { + for (Person p : this.getMembers()) { sb.append(p.getFullName()).append(", "); } return sb.toString(); diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentRepository.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentRepository.java new file mode 100644 index 0000000..3f86bb7 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentRepository.java @@ -0,0 +1,17 @@ +package nl.andrewlalis.teaching_assistant_assistant.model.repositories; + +import nl.andrewlalis.teaching_assistant_assistant.model.people.Student; +import org.springframework.data.repository.CrudRepository; + +import java.util.Optional; + +public interface StudentRepository extends CrudRepository { + + /** + * Tries to find a student by its unique student number. + * @param studentNumber The student number to search for. + * @return An optional Student object. + */ + public Optional findByStudentNumber(int studentNumber); + +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentTeamRepository.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentTeamRepository.java new file mode 100644 index 0000000..f0b5250 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/StudentTeamRepository.java @@ -0,0 +1,7 @@ +package nl.andrewlalis.teaching_assistant_assistant.model.repositories; + +import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.StudentTeam; +import org.springframework.data.repository.CrudRepository; + +public interface StudentTeamRepository extends CrudRepository { +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index e12669e..57daaf2 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,7 +1,10 @@ -spring.jpa.hibernate.ddl-auto=create -#spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect - -#spring.jpa.properties.hibernate.show_sql=true +spring.jpa.hibernate.ddl-auto=create-drop +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL57Dialect +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.use_sql_comments=true #spring.jpa.properties.hibernate.format_sql=true #spring.jpa.properties.hibernate.type=trace diff --git a/src/main/resources/templates/courses.html b/src/main/resources/templates/courses.html index d6f8562..42b41e7 100644 --- a/src/main/resources/templates/courses.html +++ b/src/main/resources/templates/courses.html @@ -11,6 +11,7 @@ Name Code Created at + Students @@ -18,6 +19,7 @@ + diff --git a/src/main/resources/templates/courses/entity.html b/src/main/resources/templates/courses/entity.html index 511b5b4..4933903 100644 --- a/src/main/resources/templates/courses/entity.html +++ b/src/main/resources/templates/courses/entity.html @@ -41,6 +41,22 @@ + +

All Students ():

+ + + + + + + +
+ + Student + +
diff --git a/src/main/resources/templates/courses/entity.html b/src/main/resources/templates/courses/entity.html index 4933903..fb2c3e2 100644 --- a/src/main/resources/templates/courses/entity.html +++ b/src/main/resources/templates/courses/entity.html @@ -10,53 +10,20 @@

(Code: )


-

Teaching Assistant Groups ():

- - - - - -
- - TA Team - - - -
- -

Student Groups ():

- - - - - -
- - Student Team - - - -
- -

All Students ():

- - - - - - - -
- - Student - -
+ 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 894b13a..07123a5 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams.html @@ -9,8 +9,23 @@

Teaching Assistant Teams for

+
+

No teaching assistant teams.

+
- + + + + +
+ + Teaching Assistant Team + + + +
diff --git a/src/main/resources/templates/courses/entity/teaching_assistants.html b/src/main/resources/templates/courses/entity/teaching_assistants.html new file mode 100644 index 0000000..9ca9361 --- /dev/null +++ b/src/main/resources/templates/courses/entity/teaching_assistants.html @@ -0,0 +1,26 @@ + + + + Teaching Assistants + + + +
+

Teaching Assistants for

+ + + + + + +
+ +
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index 2cd3521..1e60e1d 100644 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -13,6 +13,7 @@ From f1add7dc846fe525e987b3e806f3945f8b781e79 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 20 Apr 2019 15:40:28 +0200 Subject: [PATCH 06/12] Fixed errors in ta teams listing for course. --- .../templates/courses/entity/teaching_assistant_teams.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 07123a5..aefe237 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams.html @@ -21,10 +21,10 @@ Teaching Assistant Team - + - + From 8aeb445a7ceb2d1bf98587280ae45f03ff9dd281 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 20 Apr 2019 23:32:32 +0200 Subject: [PATCH 07/12] Added team manipulation --- .../controllers/TeachingAssistants.java | 53 +++++++++++++ .../{Create.java => CreateCourse.java} | 4 +- .../controllers/courses/Generate.java | 2 +- .../CreateTeachingAssistantTeam.java | 75 +++++++++++++++++++ .../TeachingAssistantTeamEntity.java | 55 ++++++++++++++ .../TeachingAssistantEntity.java | 41 ++++++++++ .../model/Course.java | 8 ++ .../model/people/Person.java | 4 + .../TeachingAssistantRepository.java | 7 ++ .../TeachingAssistantTeamRepository.java | 7 ++ .../util/github/GithubManager.java | 3 + .../entity/teaching_assistant_teams.html | 10 ++- .../teaching_assistant_teams/create.html | 37 +++++++++ .../teaching_assistant_teams/entity.html | 33 ++++++++ .../courses/entity/teaching_assistants.html | 5 +- .../entity/teaching_assistants/create.html | 36 +++++++++ .../resources/templates/fragments/header.html | 1 + .../templates/teaching_assistants.html | 32 ++++++++ .../templates/teaching_assistants/entity.html | 34 +++++++++ 19 files changed, 442 insertions(+), 5 deletions(-) create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java rename src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/{Create.java => CreateCourse.java} (88%) create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/TeachingAssistantTeamEntity.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/teaching_assistants/TeachingAssistantEntity.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantRepository.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantTeamRepository.java create mode 100644 src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html create mode 100644 src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html create mode 100644 src/main/resources/templates/courses/entity/teaching_assistants/create.html create mode 100644 src/main/resources/templates/teaching_assistants.html create mode 100644 src/main/resources/templates/teaching_assistants/entity.html 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 new file mode 100644 index 0000000..821ad41 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/TeachingAssistants.java @@ -0,0 +1,53 @@ +package nl.andrewlalis.teaching_assistant_assistant.controllers; + +import nl.andrewlalis.teaching_assistant_assistant.model.Course; +import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeachingAssistantRepository; +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.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; + +import java.util.Optional; + +@Controller +public class TeachingAssistants { + + private TeachingAssistantRepository teachingAssistantRepository; + private CourseRepository courseRepository; + + protected TeachingAssistants(TeachingAssistantRepository teachingAssistantRepository, CourseRepository courseRepository) { + this.teachingAssistantRepository = teachingAssistantRepository; + this.courseRepository = courseRepository; + } + + @GetMapping("/teaching_assistants") + public String get(Model model) { + model.addAttribute("teaching_assistants", teachingAssistantRepository.findAll()); + return "teaching_assistants"; + } + + @GetMapping("/courses/{code}/teaching_assistants/create") + public String getCreate(@PathVariable String code, Model model) { + model.addAttribute("teachingAssistant", new TeachingAssistant("First Name", "Last Name", "github Username", "me@example.com")); + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> model.addAttribute("course", course)); + return "courses/entity/teaching_assistants/create"; + } + + @PostMapping( + value = "/courses/{code}/teaching_assistants", + consumes = "application/x-www-form-urlencoded" + ) + public String post(@PathVariable String code, @ModelAttribute("teachingAssistant") TeachingAssistant teachingAssistant) { + Optional optionalCourse = this.courseRepository.findByCode(code); + optionalCourse.ifPresent(course -> { + course.addParticipant(teachingAssistant); + this.courseRepository.save(course); + }); + return "teaching_assistants/entity"; + } +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Create.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/CreateCourse.java similarity index 88% rename from src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Create.java rename to src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/CreateCourse.java index 08376e2..7d8cd0d 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Create.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/CreateCourse.java @@ -7,11 +7,11 @@ import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; @Controller -public class Create { +public class CreateCourse { private CourseRepository courseRepository; - protected Create(CourseRepository courseRepository) { + protected CreateCourse(CourseRepository courseRepository) { this.courseRepository = courseRepository; } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Generate.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Generate.java index c7f8ae5..2d5227b 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Generate.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/Generate.java @@ -26,7 +26,7 @@ public class Generate { this.courseRepository.saveAll(courses); model.addAttribute("courses", courseRepository.findAll()); - return "courses"; + return "redirect:/courses"; } } 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 new file mode 100644 index 0000000..542528d --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/CreateTeachingAssistantTeam.java @@ -0,0 +1,75 @@ +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.TeachingAssistant; +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.TeachingAssistantRepository; +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.Optional; + +@Controller +public class CreateTeachingAssistantTeam { + + private CourseRepository courseRepository; + private TeachingAssistantRepository teachingAssistantRepository; + + protected CreateTeachingAssistantTeam(CourseRepository courseRepository, TeachingAssistantRepository teachingAssistantRepository) { + this.courseRepository = courseRepository; + this.teachingAssistantRepository = teachingAssistantRepository; + } + + @GetMapping("/courses/{code}/teaching_assistant_teams/create") + 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/create"; + } + + @PostMapping( + value = "/courses/{code}/teaching_assistant_teams", + consumes = "application/x-www-form-urlencoded" + ) + public String post( + @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(); + team.setGithubTeamName(githubTeamName); + + 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."); + Course course = optionalCourse.get(); + team.setCourse(course); + + team.addMember(optionalTeachingAssistant1.get()); + team.addMember(optionalTeachingAssistant2.get()); + + course.addTeachingAssistantTeam(team); + this.courseRepository.save(course); + + model.addAttribute("course", course); + + return "courses/entity/teaching_assistant_teams"; + } else { + System.out.println("Missing data!"); + } + + return "redirect:/courses/entity"; + } +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/TeachingAssistantTeamEntity.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/TeachingAssistantTeamEntity.java new file mode 100644 index 0000000..5923980 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/TeachingAssistantTeamEntity.java @@ -0,0 +1,55 @@ +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.TeachingAssistantTeam; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.CourseRepository; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeachingAssistantTeamRepository; +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 java.util.Optional; + +@Controller +public class TeachingAssistantTeamEntity { + + private CourseRepository courseRepository; + private TeachingAssistantTeamRepository teachingAssistantTeamRepository; + + protected TeachingAssistantTeamEntity(CourseRepository courseRepository, TeachingAssistantTeamRepository teachingAssistantTeamRepository) { + this.courseRepository = courseRepository; + this.teachingAssistantTeamRepository = teachingAssistantTeamRepository; + } + + @GetMapping("/courses/{courseCode}/teaching_assistant_teams/{teamId}") + public String get(@PathVariable String courseCode, @PathVariable long teamId, Model model) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalTeachingAssistantTeam = this.teachingAssistantTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalTeachingAssistantTeam.isPresent()) { + model.addAttribute("course", optionalCourse.get()); + model.addAttribute("teachingAssistantTeam", optionalTeachingAssistantTeam.get()); + } + + return "courses/entity/teaching_assistant_teams/entity"; + } + + @GetMapping("/courses/{courseCode}/teaching_assistant_teams/{teamId}/delete") + public String delete(@PathVariable String courseCode, @PathVariable long teamId) { + Optional optionalCourse = this.courseRepository.findByCode(courseCode); + Optional optionalTeachingAssistantTeam = this.teachingAssistantTeamRepository.findById(teamId); + + if (optionalCourse.isPresent() && optionalTeachingAssistantTeam.isPresent()) { + Course course = optionalCourse.get(); + TeachingAssistantTeam team = optionalTeachingAssistantTeam.get(); + course.removeTeachingAssistantTeam(team); + + this.teachingAssistantTeamRepository.delete(team); + this.courseRepository.save(course); + } + + return "redirect:/courses/entity/teaching_assistants"; + } + +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/teaching_assistants/TeachingAssistantEntity.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/teaching_assistants/TeachingAssistantEntity.java new file mode 100644 index 0000000..99991b3 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/teaching_assistants/TeachingAssistantEntity.java @@ -0,0 +1,41 @@ +package nl.andrewlalis.teaching_assistant_assistant.controllers.teaching_assistants; + +import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant; +import nl.andrewlalis.teaching_assistant_assistant.model.repositories.TeachingAssistantRepository; +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 java.util.Optional; + +@Controller +public class TeachingAssistantEntity { + + private TeachingAssistantRepository teachingAssistantRepository; + + protected TeachingAssistantEntity(TeachingAssistantRepository teachingAssistantRepository) { + this.teachingAssistantRepository = teachingAssistantRepository; + } + + @GetMapping("/teaching_assistants/{id}") + public String get(@PathVariable long id, Model model) { + Optional optionalTeachingAssistant = this.teachingAssistantRepository.findById(id); + optionalTeachingAssistant.ifPresent(teachingAssistant -> model.addAttribute("teachingAssistant", teachingAssistant)); + return "teaching_assistants/entity"; + } + + @GetMapping("/teaching_assistants/{id}/delete") + public String delete(@PathVariable long id) { + Optional optionalTeachingAssistant = this.teachingAssistantRepository.findById(id); + optionalTeachingAssistant.ifPresent(teachingAssistant -> { + teachingAssistant.getCourses().forEach(course -> course.removeParticipant(teachingAssistant)); + teachingAssistant.getTeams().forEach(team -> { + team.removeMember(teachingAssistant); + }); + this.teachingAssistantRepository.delete(teachingAssistant); + }); + + return "redirect:/teaching_assistants"; + } +} 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 4e65534..97a62f3 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 @@ -108,12 +108,20 @@ public class Course extends BasicEntity { this.teachingAssistantTeams.add(team); } + public void removeTeachingAssistantTeam(TeachingAssistantTeam team) { + this.teachingAssistantTeams.remove(team); + } + public void addParticipant(Person person) { if (!this.participants.contains(person)) { this.participants.add(person); } } + public void removeParticipant(Person person) { + this.participants.remove(person); + } + /* Getters and Setters */ 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 7909a7f..70245af 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 @@ -114,6 +114,10 @@ public abstract class Person extends BasicEntity { return this.courses; } + public List getTeams() { + return this.teams; + } + /** * Determines if two Persons are equal. They are considered equal when all of the basic identifying information * about the person is the same, regardless of case. diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantRepository.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantRepository.java new file mode 100644 index 0000000..63154c9 --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantRepository.java @@ -0,0 +1,7 @@ +package nl.andrewlalis.teaching_assistant_assistant.model.repositories; + +import nl.andrewlalis.teaching_assistant_assistant.model.people.TeachingAssistant; +import org.springframework.data.repository.CrudRepository; + +public interface TeachingAssistantRepository extends CrudRepository { +} diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantTeamRepository.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantTeamRepository.java new file mode 100644 index 0000000..a06552b --- /dev/null +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/model/repositories/TeachingAssistantTeamRepository.java @@ -0,0 +1,7 @@ +package nl.andrewlalis.teaching_assistant_assistant.model.repositories; + +import nl.andrewlalis.teaching_assistant_assistant.model.people.teams.TeachingAssistantTeam; +import org.springframework.data.repository.CrudRepository; + +public interface TeachingAssistantTeamRepository extends CrudRepository { +} 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 5925a87..c314d8f 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 @@ -10,6 +10,9 @@ import java.util.ArrayList; import java.util.Calendar; import java.util.List; +/** + * Encapsulates much of the github functionality that is needed. + */ public class GithubManager { private GitHub github; 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 aefe237..b1eb8b7 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistant_teams.html +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams.html @@ -25,12 +25,20 @@ + + + Delete + + 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 new file mode 100644 index 0000000..b37e0d9 --- /dev/null +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams/create.html @@ -0,0 +1,37 @@ + + + + Create Teaching Assistant Team + + + +
+

+ Create your course here +

+
+ + + + + + + + + + +
+
+ + + + + \ No newline at end of file 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 new file mode 100644 index 0000000..24c3041 --- /dev/null +++ b/src/main/resources/templates/courses/entity/teaching_assistant_teams/entity.html @@ -0,0 +1,33 @@ + + + + Teaching Assistant Team + + + +
+

Teaching Assistant Team for

+ +
    +
  • + Github Team Name: +
  • +
  • + Teaching Assistants: + +
  • +
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/courses/entity/teaching_assistants.html b/src/main/resources/templates/courses/entity/teaching_assistants.html index 9ca9361..12f75f0 100644 --- a/src/main/resources/templates/courses/entity/teaching_assistants.html +++ b/src/main/resources/templates/courses/entity/teaching_assistants.html @@ -14,12 +14,15 @@ + diff --git a/src/main/resources/templates/courses/entity/teaching_assistants/create.html b/src/main/resources/templates/courses/entity/teaching_assistants/create.html new file mode 100644 index 0000000..88de95a --- /dev/null +++ b/src/main/resources/templates/courses/entity/teaching_assistants/create.html @@ -0,0 +1,36 @@ + + + + Create a Course + + + +
+

+ Create your course here +

+
+ + + + + + + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/src/main/resources/templates/fragments/header.html b/src/main/resources/templates/fragments/header.html index 1e60e1d..5b1b3f6 100644 --- a/src/main/resources/templates/fragments/header.html +++ b/src/main/resources/templates/fragments/header.html @@ -14,6 +14,7 @@
  • Home
  • Courses
  • Students
  • +
  • Teaching Assistants
  • diff --git a/src/main/resources/templates/teaching_assistants.html b/src/main/resources/templates/teaching_assistants.html new file mode 100644 index 0000000..1084796 --- /dev/null +++ b/src/main/resources/templates/teaching_assistants.html @@ -0,0 +1,32 @@ + + + + + Teaching Assistants + + + +
    +

    All Teaching Assistants

    + + + + + + + + + +
    + + + Delete +
    +
    + + + + + \ No newline at end of file diff --git a/src/main/resources/templates/teaching_assistants/entity.html b/src/main/resources/templates/teaching_assistants/entity.html new file mode 100644 index 0000000..d9c061b --- /dev/null +++ b/src/main/resources/templates/teaching_assistants/entity.html @@ -0,0 +1,34 @@ + + + + Teaching Assistant + + + +
    +

    Teaching Assistant:

    + +
      +
    • + Email: +
    • +
    • + Github Username: +
    • +
    • + Courses: +
        +
      • + +
      • +
      +
    • +
    +
    + + + + + \ No newline at end of file From 4823bad19f25a2deeae5895b64f943042dbcd5fb Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sat, 20 Apr 2019 23:48:41 +0200 Subject: [PATCH 08/12] Added basic table styling. --- src/main/resources/static/css/style.css | 5 +++++ .../courses/entity/student_teams.html | 20 +++++++++++++++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/src/main/resources/static/css/style.css b/src/main/resources/static/css/style.css index da877e4..e1d6b8e 100644 --- a/src/main/resources/static/css/style.css +++ b/src/main/resources/static/css/style.css @@ -34,4 +34,9 @@ body { .sidebar_block a:hover { color: lightgreen; +} + +table, th, td { + border: 1px solid black; + border-collapse: collapse; } \ No newline at end of file diff --git a/src/main/resources/templates/courses/entity/student_teams.html b/src/main/resources/templates/courses/entity/student_teams.html index 3e8f818..eefcc85 100644 --- a/src/main/resources/templates/courses/entity/student_teams.html +++ b/src/main/resources/templates/courses/entity/student_teams.html @@ -13,6 +13,12 @@

    No student teams.

    + + + + + + - +
    TeamStudentsRepository NameAssigned TA Team
    - + +
    +
    +
    + +
    From fabe05ba541dade9a5a3718c6a8d8dfdf21ea18c Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 21 Apr 2019 00:00:55 +0200 Subject: [PATCH 09/12] Removed sensitive info. --- .../entity/student_teams/GenerateRepositories.java | 2 +- .../teaching_assistant_assistant/model/Course.java | 14 ++++++++++++++ .../util/team_importing/StudentTeamImporter.java | 10 ---------- src/main/resources/templates/courses/create.html | 12 +++++++++--- 4 files changed, 24 insertions(+), 14 deletions(-) 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 a0818a4..07717c5 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 @@ -39,7 +39,7 @@ public class GenerateRepositories { optionalCourse.ifPresent(course -> { course.setGithubOrganizationName("InitializerTesting"); try { - GithubManager manager = new GithubManager("andrewlalis", "scrubstub43"); + GithubManager manager = new GithubManager(course.getApiKey()); manager.generateStudentTeamRepository(course.getStudentTeams().get(0)); } catch (IOException e) { e.printStackTrace(); 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 97a62f3..8c33111 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 @@ -38,6 +38,12 @@ public class Course extends BasicEntity { @Column private String githubOrganizationName; + /** + * The API key that will be used for Github interaction with this organization. + */ + @Column + private String apiKey; + /** * The list of assignments this course contains. */ @@ -142,6 +148,14 @@ public class Course extends BasicEntity { this.githubOrganizationName = name; } + public String getApiKey() { + return this.apiKey; + } + + public void setApiKey(String key) { + this.apiKey = key; + } + public List getAssignments() { return assignments; } diff --git a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java index 774991d..560b33b 100644 --- a/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java +++ b/src/main/java/nl/andrewlalis/teaching_assistant_assistant/util/team_importing/StudentTeamImporter.java @@ -36,18 +36,8 @@ public class StudentTeamImporter { List studentEntries = extractStudentsFromRecords(records); - System.out.println("<<< Entries (" + studentEntries.size() + ") >>>"); - for (StudentRecordEntry entry : studentEntries) { - System.out.println(entry.toString()); - } - List studentTeams = generateTeamsFromStudentEntries(studentEntries, 0); - System.out.println("<<< Teams (" + studentTeams.size() + ") >>>"); - for (StudentTeam team : studentTeams) { - System.out.println("Team: " + team); - } - return studentTeams; } diff --git a/src/main/resources/templates/courses/create.html b/src/main/resources/templates/courses/create.html index a90b098..4751be5 100644 --- a/src/main/resources/templates/courses/create.html +++ b/src/main/resources/templates/courses/create.html @@ -11,11 +11,17 @@

    - + + - + + - + + + + +
    From b1bd5c6e04a883e1b60265de84a5d83f6ecf62e7 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 21 Apr 2019 19:48:12 +0200 Subject: [PATCH 10/12] Added assigning to student teams, and proper repository generation. --- ...TeachingAssistantAssistantApplication.java | 6 - .../controllers/TeachingAssistants.java | 2 +- .../courses/entity/ImportStudents.java | 6 + .../student_teams/ExportStudentTeams.java | 59 ++++++ .../student_teams/GenerateRepositories.java | 25 ++- .../students/InviteAllToRepository.java | 112 +++++++++++ .../AssignToStudentTeams.java | 70 +++++++ .../CreateTeachingAssistantTeam.java | 8 +- .../people/teams/TeachingAssistantTeam.java | 12 ++ .../util/github/GithubManager.java | 181 ++++++++++++++---- src/main/resources/application.properties | 3 +- .../program_resources/getting_started.md | 44 ++++- .../courses/entity/student_teams.html | 13 +- .../courses/entity/student_teams/entity.html | 21 +- .../student_teams/generate_repositories.html | 4 + .../templates/courses/entity/students.html | 4 +- .../courses/entity/students/invite_all.html | 40 ++++ .../entity/teaching_assistant_teams.html | 3 + .../assign_to_student_teams.html | 21 ++ .../teaching_assistant_teams/create.html | 12 +- .../teaching_assistant_teams/entity.html | 10 + src/main/resources/templates/students.html | 1 + 22 files changed, 589 insertions(+), 68 deletions(-) create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/student_teams/ExportStudentTeams.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/students/InviteAllToRepository.java create mode 100644 src/main/java/nl/andrewlalis/teaching_assistant_assistant/controllers/courses/entity/teaching_assistant_teams/AssignToStudentTeams.java create mode 100644 src/main/resources/templates/courses/entity/students/invite_all.html create mode 100644 src/main/resources/templates/courses/entity/teaching_assistant_teams/assign_to_student_teams.html 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 @@ + From 22eb695e978280a79cc5c2d61e558b5ddafcba30 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 21 Apr 2019 20:47:26 +0200 Subject: [PATCH 11/12] Cleaned up export --- .../student_teams/ExportStudentTeams.java | 66 ++++++++++++++----- .../courses/entity/student_teams.html | 3 + 2 files changed, 52 insertions(+), 17 deletions(-) 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 index c1ff9bd..7375204 100644 --- 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 @@ -11,7 +11,6 @@ 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; @@ -36,24 +35,57 @@ public class ExportStudentTeams { } Course course = optionalCourse.get(); + response.setContentType("text/plain"); + response.setCharacterEncoding("UTF-8"); - 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.getOutputStream().write(getStudentTeamsSummary(course)); response.flushBuffer(); } + + @GetMapping("/courses/{code}/student_teams/export_contact_info") + public void exportContactInfo(@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/plain"); + response.setCharacterEncoding("UTF-8"); + + response.getOutputStream().write(getContactInfo(course)); + + response.flushBuffer(); + } + + private byte[] getStudentTeamsSummary(Course course) { + StringBuilder sb = new StringBuilder("Student Teams Export for Course: "); + sb.append(course.getName()).append('\n'); + for (TeachingAssistantTeam teachingAssistantTeam : course.getTeachingAssistantTeams()) { + sb.append("Teaching Assistant Team ").append(teachingAssistantTeam.getId()).append(", Github Team Name: ").append(teachingAssistantTeam.getGithubTeamName()).append('\n'); + List assignedTeams = teachingAssistantTeam.getAssignedStudentTeams(); + for (StudentTeam studentTeam : assignedTeams) { + sb.append("\tStudent Team ").append(studentTeam.getId()).append(": "); + for (Student student : studentTeam.getStudents()) { + sb.append(student.getFullName()).append(" (S").append(student.getStudentNumber()).append("), "); + } + sb.append('\n'); + } + } + return sb.toString().getBytes(); + } + + private byte[] getContactInfo(Course course) { + StringBuilder sb = new StringBuilder("Student Team Contact Details\n"); + for (StudentTeam team : course.getStudentTeams()) { + sb.append("2019_Team_").append(team.getId()).append(": "); + for (Student student : team.getStudents()) { + sb.append(student.getFullName()).append(" (").append(student.getEmailAddress()).append("), "); + } + sb.append("\n"); + } + return sb.toString().getBytes(); + } } diff --git a/src/main/resources/templates/courses/entity/student_teams.html b/src/main/resources/templates/courses/entity/student_teams.html index 4772726..12dcbab 100644 --- a/src/main/resources/templates/courses/entity/student_teams.html +++ b/src/main/resources/templates/courses/entity/student_teams.html @@ -60,6 +60,9 @@ + From 9c6dec0473bcef8678ed0cc5b1d8c4329c5821d3 Mon Sep 17 00:00:00 2001 From: Andrew Lalis Date: Sun, 21 Apr 2019 20:55:48 +0200 Subject: [PATCH 12/12] Improved Students listing with additional context links --- src/main/resources/templates/students.html | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/main/resources/templates/students.html b/src/main/resources/templates/students.html index 0dc3c9a..613a4f1 100644 --- a/src/main/resources/templates/students.html +++ b/src/main/resources/templates/students.html @@ -10,6 +10,13 @@

    All Students

    + + + + + + + +
    StudentStudent NumberEmailGithub UsernameTeams
    @@ -17,6 +24,19 @@ +
    + + from + +
    +