Half of features implemented #2

Merged
andrewlalis merged 19 commits from repo_creation into master 2018-08-28 18:26:28 +00:00
10 changed files with 265 additions and 94 deletions
Showing only changes of commit 6394e9d1d4 - Show all commits

19
pom.xml
View File

@ -21,6 +21,10 @@
</build>
<packaging>jar</packaging>
<properties>
<jackson.version>2.9.6</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.commons</groupId>
@ -48,6 +52,21 @@
<artifactId>httpclient</artifactId>
<version>RELEASE</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
</project>

View File

@ -1,7 +1,7 @@
package nl.andrewlalis;
import nl.andrewlalis.git_api.Initializer;
import nl.andrewlalis.model.Team;
import nl.andrewlalis.model.StudentTeam;
import nl.andrewlalis.util.Logging;
import nl.andrewlalis.util.TeamGenerator;
@ -33,16 +33,16 @@ public class Main {
logger.info("Initializer for Github Repositories in Educational Organizations.");
// Get teams from CSV file.
List<Team> teams = null;
// Get studentTeams from CSV file.
List<StudentTeam> studentTeams = null;
try {
teams = TeamGenerator.generateFromCSV(
studentTeams = TeamGenerator.generateFromCSV(
userOptions.get("input"),
Integer.parseInt(userOptions.get("teamsize"))
);
logger.info("Teams created: " + teams);
logger.info("Teams created: " + studentTeams);
} catch (IOException | ArrayIndexOutOfBoundsException e) {
logger.severe("Unable to generate teams from CSV file, exiting.");
logger.severe("Unable to generate studentTeams from CSV file, exiting.");
System.exit(1);
}
@ -51,7 +51,7 @@ public class Main {
userOptions.get("token"),
"assignments"
);
initializer.initializeGithubRepos(teams);
initializer.initializeGithubRepos(studentTeams);
}
}

View File

@ -1,28 +1,62 @@
package nl.andrewlalis.git_api;
import nl.andrewlalis.model.Team;
import com.fasterxml.jackson.databind.ObjectMapper;
import nl.andrewlalis.model.StudentTeam;
import nl.andrewlalis.model.TATeam;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
/**
* This class is responsible for initializing the Github repositories and setting permissions, adding teams, etc.
*/
public class Initializer {
/**
* The name of the organization to operate on.
*/
private String organizationName;
/**
* The oauth access token needed for the request.
*/
private String accessToken;
/**
* The object which simplifies creating REST URL's for most of the requests.
*/
private URLBuilder urlBuilder;
/**
* The name of the assignments repository where students will get assignments from.
*/
private String assignmentsRepo;
/**
* The logger for outputting debug info.
*/
private static final Logger logger = Logger.getLogger(Initializer.class.getName());
static {
logger.setParent(Logger.getGlobal());
}
public Initializer(String organizationName, String accessToken, String assignmentsRepo) {
this.organizationName = organizationName;
this.accessToken = accessToken;
@ -31,26 +65,61 @@ public class Initializer {
}
/**
* Initializes the github repository for all teams given.
* Initializes the github repository for all studentTeams given.
*
* Creates for the entire organization:
* - an assignments repository with protected master branch and TA permissions.
* Creates for each team:
* - a repository
* - protected master branch
* - development branch
* - adds students to repository
* - creates assignments repository
* - adds all students to assignments repository.
* @param teams The list of student teams.
* @param studentTeams The list of student studentTeams.
*/
public void initializeGithubRepos(List<Team> teams) {
public void initializeGithubRepos(List<StudentTeam> studentTeams) {
listMemberTeams();
//this.createRepository(this.assignmentsRepo, )
}
private boolean createRepository(String repositoryName, TATeam taTeam) {
List<NameValuePair> nvps = new ArrayList<NameValuePair>();
nvps.add(new BasicNameValuePair("name", repositoryName));
nvps.add(new BasicNameValuePair("private", "True"));
nvps.add(new BasicNameValuePair("has_issues", "True"));
nvps.add(new BasicNameValuePair("has_wiki", "True"));
nvps.add(new BasicNameValuePair("team_id", taTeam.getId()));
nvps.add(new BasicNameValuePair("auto_init", "False"));
nvps.add(new BasicNameValuePair("gitignore_template", "Java"));
HttpClient client = HttpClientBuilder.create().build();
HttpPost post = new HttpPost(this.urlBuilder.buildRepoURL());
try {
post.setEntity(new StringEntity(nvps.toString()));
post.setHeader("Content-type", "application/json");
HttpResponse response = client.execute(post);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* Obtains a list of all teams in the organization, meaning all teaching assistant teams.
* @return A list of all teams in the organization.
*/
private Map<String, String> listMemberTeams() {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet get = new HttpGet(this.urlBuilder.buildTeamURL());
try (CloseableHttpResponse response = client.execute(get)) {
System.out.println(response.getStatusLine());
StatusLine line = response.getStatusLine();
logger.finest("Response code from listing member teams: " + line.getStatusCode());
logger.finest("Response entity: " + response.getEntity().toString());
HashMap<String,Object> result =
new ObjectMapper().readValue(response.getEntity().getContent(), HashMap.class);
for (Map.Entry<String, Object> e : result.entrySet()) {
logger.finest(e.getKey() + ": " + e.getValue());
}
} catch (IOException e) {
e.printStackTrace();
}

View File

@ -60,7 +60,7 @@ public class URLBuilder {
+ this.organizationName
+ '/'
+ repoName
+ "/git/refs/heads/master?acces_token="
+ "/git/refs/heads/master?access_token="
+ this.accessToken;
}

View File

@ -0,0 +1,89 @@
package nl.andrewlalis.model;
/**
* A generic object that students, teaching assistants, and professors can extend from. This covers all the basic
* functionality that applies to anyone in the system.
*/
public abstract class Person {
/**
* The unique identification number for this person. (P- or S-Number)
*/
protected int number;
/**
* The person's first and last name.
*/
protected String name;
/**
* The person's email address.
*/
protected String emailAddress;
/**
* The person's github username.
*/
protected String githubUsername;
/**
* Constructs a Person from all the basic information needed.
* @param number Either an S- or P-Number without the letter prefix.
* @param name The first, middle (if applicable) and last name.
* @param emailAddress The email address. (Either university or personal.)
* @param githubUsername The person's github username.
*/
public Person(int number, String name, String emailAddress, String githubUsername){
this.number = number;
this.name = name;
this.emailAddress = emailAddress;
this.githubUsername = githubUsername;
}
/**
* Accessors
*/
public int getNumber(){
return this.number;
}
public String getName(){
return this.name;
}
public String getEmailAddress(){
return this.emailAddress;
}
public String getGithubUsername(){
return this.githubUsername;
}
/**
* Determines if two persons are the same. This is defined as:
* Two persons are equal if at least one of their personal data points is identical. Because each of the data points
* should be unique to this person alone, if there is any conflict, assume that they are equal.
* @param o The object to compare to.
* @return True if the two persons share personal data, or false if all data is unique among the two.
*/
@Override
public boolean equals(Object o) {
if (!(o instanceof Person)) {
return false;
}
Person p = (Person)o;
return p.getNumber() == this.getNumber()
|| p.getEmailAddress().equals(this.getEmailAddress())
|| p.getGithubUsername().equals(this.getGithubUsername())
|| p.getName().equalsIgnoreCase(this.getName());
}
/**
* Represents the person as a basic comma-separated string object.
* @return A comma-separated String object.
*/
@Override
public String toString() {
return this.getName() + ", " + this.getNumber() + ", " + this.getEmailAddress() + ", " + this.getGithubUsername();
}
}

View File

@ -1,66 +1,34 @@
package nl.andrewlalis.model;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Represents one student's github information.
*/
public class Student {
/**
* The student's S-number.
*/
private int number;
/**
* The student's name.
*/
private String name;
/**
* The student's email.
*/
private String emailAddress;
/**
* The student's github username.
*/
private String githubUsername;
public class Student extends Person {
/**
* A list of partners that the student has said that they would like to be partners with.
*/
private List<Integer> preferredPartners;
/**
* Constructs a student similarly to a Person, but with an extra preferredPartners list.
* @param number The student's S-Number.
* @param name The student's name.
* @param emailAddress The student's email address.
* @param githubUsername The student's github username.
* @param preferredPartners A list of this student's preferred partners, as a list of integers representing the
* other students' numbers.
*/
public Student(int number, String name, String emailAddress, String githubUsername, List<Integer> preferredPartners) {
this.number = number;
this.name = name;
this.emailAddress = emailAddress;
this.githubUsername = githubUsername;
super(number, name, emailAddress, githubUsername);
this.preferredPartners = preferredPartners;
}
@Override
public String toString() {
return this.number + " - " + this.name + " - " + this.emailAddress + " - " + this.githubUsername;
}
public int getNumber() {
return number;
}
public String getEmailAddress() {
return emailAddress;
}
public String getGithubUsername() {
return githubUsername;
}
public List<Integer> getPreferredPartners() {
return preferredPartners;
return this.preferredPartners;
}
/**
@ -68,23 +36,12 @@ public class Student {
* @param studentMap A mapping from student number to student for all students who have signed up.
* @return A team with unknown id, comprised of this student's preferred partners.
*/
public Team getPreferredTeam(Map<Integer, Student> studentMap) {
Team t = new Team();
public StudentTeam getPreferredTeam(Map<Integer, Student> studentMap) {
StudentTeam t = new StudentTeam();
for (int partnerNumber : this.getPreferredPartners()) {
t.addStudent(studentMap.get(partnerNumber));
}
t.addStudent(this);
return t;
}
@Override
public boolean equals(Object s) {
if (!(s instanceof Student)) {
return false;
}
Student student = (Student) s;
return student.getNumber() == this.getNumber()
|| student.getEmailAddress().equals(this.getEmailAddress())
|| student.getGithubUsername().equals(this.getGithubUsername());
}
}

View File

@ -6,7 +6,7 @@ import java.util.List;
/**
* Represents one or more students' collective information.
*/
public class Team {
public class StudentTeam {
/**
* The list of students in this team.
@ -18,7 +18,7 @@ public class Team {
*/
private int id;
public Team() {
public StudentTeam() {
this.students = new ArrayList<>();
this.id = -1;
}
@ -118,7 +118,7 @@ public class Team {
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("Team: ");
StringBuilder sb = new StringBuilder("StudentTeam: ");
sb.append(this.id).append('\n');
for (Student s : this.students) {
sb.append('\t').append(s.toString()).append('\n');
@ -134,8 +134,8 @@ public class Team {
*/
@Override
public boolean equals(Object o) {
if (o instanceof Team) {
Team t = (Team) o;
if (o instanceof StudentTeam) {
StudentTeam t = (StudentTeam) o;
if (t.getStudentCount() != this.getStudentCount()) {
return false;
}

View File

@ -0,0 +1,21 @@
package nl.andrewlalis.model;
import java.util.List;
public class TATeam {
private List<TeachingAssistant> teachingAssistants;
public TATeam(List<TeachingAssistant> teachingAssistants) {
this.teachingAssistants = teachingAssistants;
}
/**
* Gets the unique identification for this TA team.
* @return A string representing the id of this team.
*/
public String getId() {
return null;
}
}

View File

@ -0,0 +1,16 @@
package nl.andrewlalis.model;
public class TeachingAssistant extends Person {
/**
* Constructs a Teaching Assistant from all the basic information needed, much like its parent, Person.
*
* @param number Either an S- or P-Number without the letter prefix.
* @param name The first, middle (if applicable) and last name.
* @param emailAddress The email address. (Either university or personal.)
* @param githubUsername The person's github username.
*/
public TeachingAssistant(int number, String name, String emailAddress, String githubUsername) {
super(number, name, emailAddress, githubUsername);
}
}

View File

@ -1,7 +1,7 @@
package nl.andrewlalis.util;
import nl.andrewlalis.model.Student;
import nl.andrewlalis.model.Team;
import nl.andrewlalis.model.StudentTeam;
import org.apache.commons.csv.CSVFormat;
import org.apache.commons.csv.CSVRecord;
@ -32,11 +32,11 @@ public class TeamGenerator {
* @throws IOException If the file is unable to be read.
* @throws IllegalArgumentException If an invalid teamsize is given.
*/
public static List<Team> generateFromCSV(String filename, int teamSize) throws IOException, IllegalArgumentException {
public static List<StudentTeam> generateFromCSV(String filename, int teamSize) throws IOException, IllegalArgumentException {
logger.info("Generating teams of size " + teamSize);
if (teamSize < 1) {
logger.severe("Invalid team size.");
throw new IllegalArgumentException("Team size must be greater than or equal to 1. Got " + teamSize);
throw new IllegalArgumentException("StudentTeam size must be greater than or equal to 1. Got " + teamSize);
}
logger.fine("Parsing CSV file.");
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(new FileReader(filename));
@ -46,8 +46,8 @@ public class TeamGenerator {
try {
studentMap = readAllStudents(records, teamSize);
} catch (ArrayIndexOutOfBoundsException e) {
logger.severe("Team size does not match column count in records.");
throw new IllegalArgumentException("Team size does not match column count in records.");
logger.severe("StudentTeam size does not match column count in records.");
throw new IllegalArgumentException("StudentTeam size does not match column count in records.");
}
@ -72,28 +72,28 @@ public class TeamGenerator {
* @param teamSize The preferred maximum size for a team.
* @return A list of teams, most of which are of teamSize size.
*/
private static List<Team> generateAllValidTeams(Map<Integer, Student> studentMap, int teamSize) {
private static List<StudentTeam> generateAllValidTeams(Map<Integer, Student> studentMap, int teamSize) {
List<Student> singleStudents = new ArrayList<>(studentMap.values());
List<Team> teams = new ArrayList<>();
List<StudentTeam> studentTeams = new ArrayList<>();
int teamCount = 1;
// For each student, try to make a team from its preferred partners.
for (Map.Entry<Integer, Student> e : studentMap.entrySet()) {
Team t = e.getValue().getPreferredTeam(studentMap);
StudentTeam t = e.getValue().getPreferredTeam(studentMap);
logger.finest("Checking if student's preferred team is valid: " + t.getStudents());
// Check if the team is of a valid size, and is not a duplicate.
// Note that at this stage, singles are treated as teams of 1, and thus not valid for any teamSize > 1.
if (t.isValid(teamSize) && !teams.contains(t)) {
// Note that at this stage, singles are treated as studentTeams of 1, and thus not valid for any teamSize > 1.
if (t.isValid(teamSize) && !studentTeams.contains(t)) {
// Once we know this team is completely valid, we remove all the students in it from the list of singles.
t.setId(teamCount++);
singleStudents.removeAll(t.getStudents());
teams.add(t);
studentTeams.add(t);
logger.fine("Created team: " + t);
}
}
teams.addAll(mergeSingleStudents(singleStudents, teamSize, teamCount));
return teams;
studentTeams.addAll(mergeSingleStudents(singleStudents, teamSize, teamCount));
return studentTeams;
}
/**
@ -104,10 +104,10 @@ public class TeamGenerator {
* @param teamIndex The current number used in assigning an id to the team.
* @return A list of teams comprising of single students.
*/
private static List<Team> mergeSingleStudents(List<Student> singleStudents, int teamSize, int teamIndex) {
List<Team> teams = new ArrayList<>();
private static List<StudentTeam> mergeSingleStudents(List<Student> singleStudents, int teamSize, int teamIndex) {
List<StudentTeam> studentTeams = new ArrayList<>();
while (!singleStudents.isEmpty()) {
Team t = new Team();
StudentTeam t = new StudentTeam();
t.setId(teamIndex++);
logger.fine("Creating new team of single students: " + t);
while (t.getStudentCount() < teamSize && !singleStudents.isEmpty()) {
@ -115,10 +115,10 @@ public class TeamGenerator {
logger.finest("Single student: " + s);
t.addStudent(s);
}
teams.add(t);
studentTeams.add(t);
logger.fine("Created team: " + t);
}
return teams;
return studentTeams;
}
/**