Abstracted team object.

This commit is contained in:
Andrew Lalis 2018-08-18 17:48:42 +02:00
parent b7a89c0946
commit 54498a4955
10 changed files with 258 additions and 130 deletions

View File

@ -1,11 +1,10 @@
package nl.andrewlalis; package nl.andrewlalis;
import nl.andrewlalis.database.Database; import nl.andrewlalis.model.database.Database;
import nl.andrewlalis.git_api.GithubManager; import nl.andrewlalis.git_api.GithubManager;
import nl.andrewlalis.model.Student; import nl.andrewlalis.model.Student;
import nl.andrewlalis.model.StudentTeam; import nl.andrewlalis.model.StudentTeam;
import nl.andrewlalis.util.CommandLine; import nl.andrewlalis.util.CommandLine;
import nl.andrewlalis.util.FileUtils;
import nl.andrewlalis.util.Logging; import nl.andrewlalis.util.Logging;
import nl.andrewlalis.util.TeamGenerator; import nl.andrewlalis.util.TeamGenerator;

View File

@ -2,6 +2,7 @@ package nl.andrewlalis.git_api;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.ObjectNode;
import jdk.nashorn.internal.ir.annotations.Ignore;
import nl.andrewlalis.model.Student; import nl.andrewlalis.model.Student;
import nl.andrewlalis.model.StudentTeam; import nl.andrewlalis.model.StudentTeam;
import org.apache.http.HttpResponse; import org.apache.http.HttpResponse;
@ -88,7 +89,7 @@ public class GithubManager {
StudentTeam t = new StudentTeam(); StudentTeam t = new StudentTeam();
Student s = new Student(3050831, "Andrew Lalis", "andrewlalisofficial@gmail.com", "andrewlalis", null); Student s = new Student(3050831, "Andrew Lalis", "andrewlalisofficial@gmail.com", "andrewlalis", null);
t.addStudent(s); t.addMember(s);
t.setId(42); t.setId(42);
this.setupStudentTeam(t, teamAll); this.setupStudentTeam(t, teamAll);
@ -99,6 +100,7 @@ public class GithubManager {
* @param allTeachingAssistants A team consisting of all teaching assistants. * @param allTeachingAssistants A team consisting of all teaching assistants.
* @throws IOException If an HTTP request failed. * @throws IOException If an HTTP request failed.
*/ */
@SuppressWarnings("deprecation")
private void setupAssignmentsRepo(GHTeam allTeachingAssistants) throws IOException { private void setupAssignmentsRepo(GHTeam allTeachingAssistants) throws IOException {
// Check if the repository already exists. // Check if the repository already exists.
GHRepository existingRepo = this.organization.getRepository(this.assignmentsRepoName); GHRepository existingRepo = this.organization.getRepository(this.assignmentsRepoName);
@ -139,10 +141,11 @@ public class GithubManager {
* @param taTeam The team of teaching assistants that is responsible for these students. * @param taTeam The team of teaching assistants that is responsible for these students.
* @throws IOException If an HTTP request fails. * @throws IOException If an HTTP request fails.
*/ */
@SuppressWarnings("deprecation")
private void setupStudentTeam(StudentTeam team, GHTeam taTeam) throws IOException { private void setupStudentTeam(StudentTeam team, GHTeam taTeam) throws IOException {
String teamRepoName = team.generateUniqueName(this.studentRepoPrefix); String teamRepoName = team.generateUniqueName(this.studentRepoPrefix);
List<Student> students = team.getStudents(); Student[] students = team.getStudents();
StringBuilder description = new StringBuilder("Group "); StringBuilder description = new StringBuilder("Group ");
description.append(team.getId()).append(": "); description.append(team.getId()).append(": ");

View File

@ -4,7 +4,7 @@ package nl.andrewlalis.model;
* A generic object that students, teaching assistants, and professors can extend from. This covers all the basic * 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. * functionality that applies to anyone in the system.
*/ */
public abstract class Person { public abstract class Person {
/** /**
* The unique identification number for this person. (P- or S-Number) * The unique identification number for this person. (P- or S-Number)

View File

@ -0,0 +1,18 @@
package nl.andrewlalis.model;
import java.sql.Connection;
/**
* Defines objects which may be stored in the database, and requires that they implement methods for both storage and
* retrieval of the objects.
*/
public interface Storable {
/**
* Stores the object in the database.
* @param connection The connection to the database which can be used for preparation of and execution of queries.
* @return True if the object is successfully stored, false if an error occurred.
*/
boolean store(Connection connection);
}

View File

@ -39,9 +39,9 @@ public class Student extends Person {
public StudentTeam getPreferredTeam(Map<Integer, Student> studentMap) { public StudentTeam getPreferredTeam(Map<Integer, Student> studentMap) {
StudentTeam t = new StudentTeam(); StudentTeam t = new StudentTeam();
for (int partnerNumber : this.getPreferredPartners()) { for (int partnerNumber : this.getPreferredPartners()) {
t.addStudent(studentMap.get(partnerNumber)); t.addMember(studentMap.get(partnerNumber));
} }
t.addStudent(this); t.addMember(this);
return t; return t;
} }
} }

View File

@ -1,74 +1,22 @@
package nl.andrewlalis.model; package nl.andrewlalis.model;
import java.util.ArrayList; import java.util.Arrays;
import java.util.List;
/** /**
* Represents one or more students' collective information. * Represents one or more students' collective information.
*/ */
public class StudentTeam { public class StudentTeam extends Team{
/**
* The list of students in this team.
*/
private List<Student> students;
/**
* The team identification number.
*/
private int id;
public StudentTeam() { public StudentTeam() {
this.students = new ArrayList<>(); super(-1);
this.id = -1;
} }
/** /**
* Determines if a student is already included in this team. * Gets a list of students, casted from the original Person[].
* @param student A student. * @return An array of Students.
* @return True if the student is in this team, false otherwise.
*/ */
public boolean hasStudent(Student student) { public Student[] getStudents() {
for (Student s : this.students) { return Arrays.copyOf(this.getMembers(), this.memberCount(), Student[].class);
if (s.equals(student)) {
return true;
}
}
return false;
}
public int getStudentCount() {
return this.students.size();
}
public int getId() {
return this.id;
}
public void setId(int id) {
this.id = id;
}
public void setStudents(List<Student> students) {
this.students = students;
}
public List<Student> getStudents() {
return this.students;
}
/**
* Adds a student to this team.
* @param student The student to add.
* @return True if the student could be added, false otherwise.
*/
public boolean addStudent(Student student) {
if (!this.hasStudent(student)) {
this.students.add(student);
return true;
} else {
return false;
}
} }
/** /**
@ -81,10 +29,9 @@ public class StudentTeam {
* @return True if the team is valid, and false otherwise. * @return True if the team is valid, and false otherwise.
*/ */
public boolean isValid(int teamSize) { public boolean isValid(int teamSize) {
if (this.getStudentCount() == teamSize) { if (this.memberCount() == teamSize) {
List<Integer> encounteredIds = new ArrayList<>(); for (Student studentA : this.getStudents()) {
for (Student studentA : this.students) { for (Student studentB : this.getStudents()) {
for (Student studentB : this.students) {
if (!studentA.equals(studentB) && !studentA.getPreferredPartners().contains(studentB.getNumber())) { if (!studentA.equals(studentB) && !studentA.getPreferredPartners().contains(studentB.getNumber())) {
return false; return false;
} }
@ -105,48 +52,9 @@ public class StudentTeam {
public String generateUniqueName(String prefix) { public String generateUniqueName(String prefix) {
StringBuilder sb = new StringBuilder(prefix); StringBuilder sb = new StringBuilder(prefix);
sb.append("_team_").append(this.id); sb.append("_team_").append(this.id);
for (Student s : this.students) { for (Student s : (Student[]) this.getMembers()) {
sb.append('_').append(s.getNumber()); sb.append('_').append(s.getNumber());
} }
return sb.toString(); return sb.toString();
} }
/**
* Returns a pretty formatting of this team so that it can be viewed in the command line. This is mainly for
* debugging purposes.
* @return A string representing the team.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder("StudentTeam: ");
sb.append(this.id).append('\n');
for (Student s : this.students) {
sb.append('\t').append(s.toString()).append('\n');
}
return sb.toString();
}
/**
* Determines if one team is equivalent to another. This is determined by if the two teams are comprised of the same
* students, in any order.
* @param o The object to compare to this team.
* @return True if the teams contain the same students, false otherwise.
*/
@Override
public boolean equals(Object o) {
if (o instanceof StudentTeam) {
StudentTeam t = (StudentTeam) o;
if (t.getStudentCount() != this.getStudentCount()) {
return false;
}
for (Student s : this.students) {
if (!t.hasStudent(s)) {
return false;
}
}
return true;
} else {
return false;
}
}
} }

View File

@ -0,0 +1,153 @@
package nl.andrewlalis.model;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* An abstract Team object from which both Teaching Assistant and Student teams can be built.
*/
public abstract class Team {
/**
* An identification number unique to this team alone.
*/
protected int id;
/**
* A list of members of this team.
*/
private List<Person> members;
/**
* Constructs this team with the given id.
* @param id The id to assign to this team.
*/
public Team(int id) {
this.id = id;
this.members = new ArrayList<>();
}
/**
* @param newId The new id number to assign to this team.
*/
public void setId(int newId) {
this.id = newId;
}
/**
* @return This team's id number.
*/
public int getId() {
return this.id;
}
/**
* Adds a new person to this team, only if they do not exist in this team yet.
* @param newMember The new member to add.
*/
public void addMember(Person newMember) {
for (Person person : this.members) {
if (person.equals(newMember)) {
return;
}
}
this.members.add(newMember);
}
/**
* Removes a person from this team.
* @param person The person to remove.
*/
public void removeMember(Person person) {
this.members.remove(person);
}
/**
* Checks if this team contains the given person.
* @param person The person to check for.
* @return True if the person is a member of this team, false otherwise.
*/
public boolean containsMember(Person person) {
for (Person p : this.members) {
if (p.equals(person)) {
return true;
}
}
return false;
}
/**
* Sets the team to be comprised of only the members given in the array.
* @param people The people which will make up the members of this team.
*/
public void setMembers(Person[] people) {
this.members = new ArrayList<>(Arrays.asList(people));
}
/**
* Gets a list of people in this team.
* @return A list of people in this team.
*/
public Person[] getMembers() {
Person[] people = new Person[this.memberCount()];
this.members.toArray(people);
return people;
}
/**
* Gets the number of people in this team.
* @return The number of people in this team.
*/
public int memberCount() {
return this.members.size();
}
/**
* Determines if another team has the same members as this team.
* @param team The team to compare to this team.
* @return True if the other team has all the same members as this team.
*/
public boolean hasSameMembers(Team team) {
if (this.memberCount() == team.memberCount()) {
for (Person person : this.members) {
if (!team.containsMember(person)) {
return false;
}
}
return true;
}
return false;
}
/**
* Checks if an object is equal to this team. First checks if the other object is a Team, and then if it has the
* same id and team size. If both of those conditions are met, then it will check that all team members are the
* same.
* @param obj The object to check for equality.
* @return True if the two objects represent the same team, or false otherwise.
*/
@Override
public boolean equals(Object obj) {
if (obj instanceof Team) {
Team team = (Team) obj;
if (team.getId() == this.getId() && this.hasSameMembers(team)) {
return true;
}
}
return false;
}
/**
* @return A String containing a line for each member in the team.
*/
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (Person person : this.members) {
sb.append(person.toString()).append('\n');
}
return sb.toString();
}
}

View File

@ -1,11 +1,11 @@
package nl.andrewlalis.database; package nl.andrewlalis.model.database;
import nl.andrewlalis.model.Person; import nl.andrewlalis.model.Person;
import nl.andrewlalis.model.Student; import nl.andrewlalis.model.Student;
import nl.andrewlalis.model.TeachingAssistant; import nl.andrewlalis.model.TeachingAssistant;
import nl.andrewlalis.util.FileUtils;
import java.sql.*; import java.sql.*;
import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
/** /**
@ -53,18 +53,13 @@ public class Database {
* @return True, if successful, false if not. * @return True, if successful, false if not.
*/ */
public boolean initialize() { public boolean initialize() {
String sql = FileUtils.readStringFromFile("/sql/table_init.sql"); List<PreparedStatement> statements = Utils.prepareStatementsFromFile("/sql/table_init.sql", this.connection);
String[] commands = sql.split(";"); for (PreparedStatement statement : statements) {
for (String command : commands) { try {
logger.finest("Executing command: " + command); statement.execute();
if (command.trim().length() > 1) { } catch (SQLException e) {
try { logger.severe("SQLException while executing prepared statement: " + statement.toString() + ". Code: " + e.getErrorCode());
PreparedStatement statement = this.connection.prepareStatement(command); return false;
statement.execute();
} catch (SQLException e) {
logger.severe("SQLException: " + e.getErrorCode());
return false;
}
} }
} }
logger.fine("Database initialized."); logger.fine("Database initialized.");

View File

@ -0,0 +1,52 @@
package nl.andrewlalis.model.database;
import nl.andrewlalis.util.FileUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
/**
* Contains some methods which make database actions much easier.
*/
public class Utils {
/**
* The logger for outputting debug info.
*/
private static final Logger logger = Logger.getLogger(Utils.class.getName());
static {
logger.setParent(Logger.getGlobal());
}
/**
* Gets an ordered list of prepared statements from a file which contains multiple statements separated by a
* semicolon. This method separates those statements into their own strings, and prepares them individually to be
* executed later.
* @param filename The name of the file which contains the statements.
* @param connection The connection to a database; used to prepare statements.
* @return An ordered list of prepared statements which are based on the contents of the file provided.
*/
public static List<PreparedStatement> prepareStatementsFromFile(String filename, Connection connection) {
String string = FileUtils.readStringFromFile(filename);
if (string == null || string.isEmpty()) {
return new ArrayList<>();
}
String[] splits = string.split(";");
List<PreparedStatement> statements = new ArrayList<>();
for (String split : splits) {
if (split.trim().length() > 1) {
try {
statements.add(connection.prepareStatement(split));
} catch (SQLException e) {
logger.severe("SQLException while preparing a statement:\n" + split + "\nError Code: " + e.getErrorCode());
}
}
}
return statements;
}
}

View File

@ -80,15 +80,15 @@ public class TeamGenerator {
// For each student, try to make a team from its preferred partners. // For each student, try to make a team from its preferred partners.
for (Map.Entry<Integer, Student> e : studentMap.entrySet()) { for (Map.Entry<Integer, Student> e : studentMap.entrySet()) {
StudentTeam newTeam = e.getValue().getPreferredTeam(studentMap); StudentTeam newTeam = e.getValue().getPreferredTeam(studentMap);
logger.finest("Checking if student's preferred team is valid: " + newTeam.getStudents()); logger.finest("Checking if student's preferred team is valid:\n" + newTeam);
// Check if the team is of a valid size, and is not a duplicate. // Check if the team is of a valid size, and is not a duplicate.
// Note that at this stage, singles are treated as studentTeams of 1, and thus not valid for any teamSize > 1. // Note that at this stage, singles are treated as studentTeams of 1, and thus not valid for any teamSize > 1.
if (newTeam.isValid(teamSize) && !studentTeams.contains(newTeam)) { if (newTeam.isValid(teamSize) && !studentTeams.contains(newTeam)) {
// Once we know this team is completely valid, we remove all the students in it from the list of singles. // Once we know this team is completely valid, we remove all the students in it from the list of singles.
newTeam.setId(teamCount++); newTeam.setId(teamCount++);
singleStudents.removeAll(newTeam.getStudents()); singleStudents.removeAll(Arrays.asList(newTeam.getStudents()));
studentTeams.add(newTeam); studentTeams.add(newTeam);
logger.fine("Created team: " + newTeam); logger.fine("Created team:\n" + newTeam);
} }
} }
@ -109,11 +109,11 @@ public class TeamGenerator {
while (!singleStudents.isEmpty()) { while (!singleStudents.isEmpty()) {
StudentTeam t = new StudentTeam(); StudentTeam t = new StudentTeam();
t.setId(teamIndex++); t.setId(teamIndex++);
logger.fine("Creating new team of single students: " + t); logger.fine("Creating new team of single students:\n" + t);
while (t.getStudentCount() < teamSize && !singleStudents.isEmpty()) { while (t.memberCount() < teamSize && !singleStudents.isEmpty()) {
Student s = singleStudents.remove(0); Student s = singleStudents.remove(0);
logger.finest("Single student: " + s); logger.finest("Single student: " + s);
t.addStudent(s); t.addMember(s);
} }
studentTeams.add(t); studentTeams.add(t);
logger.fine("Created team: " + t); logger.fine("Created team: " + t);