Half of features implemented #2
|
@ -1,20 +1,19 @@
|
|||
package nl.andrewlalis;
|
||||
|
||||
import nl.andrewlalis.database.Database;
|
||||
import nl.andrewlalis.git_api.GithubManager;
|
||||
import nl.andrewlalis.model.Student;
|
||||
import nl.andrewlalis.model.StudentTeam;
|
||||
import nl.andrewlalis.util.CommandLine;
|
||||
import nl.andrewlalis.util.FileUtils;
|
||||
import nl.andrewlalis.util.Logging;
|
||||
import nl.andrewlalis.util.TeamGenerator;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.ConsoleHandler;
|
||||
import java.util.logging.Handler;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import nl.andrewlalis.util.CommandLine;
|
||||
|
||||
/**
|
||||
* Main program entry point.
|
||||
*/
|
||||
|
@ -28,16 +27,9 @@ public class Main {
|
|||
Map<String, String> userOptions = CommandLine.parseArgs(args);
|
||||
|
||||
// Initialize logger.
|
||||
ConsoleHandler handler = new ConsoleHandler();
|
||||
handler.setLevel(Level.INFO);
|
||||
try {
|
||||
Logging.setup(true); // TODO: Replace true with command line arg.
|
||||
Handler[] handlers = logger.getHandlers();
|
||||
for (Handler h : handlers) {
|
||||
logger.removeHandler(h);
|
||||
}
|
||||
logger.setUseParentHandlers(false);
|
||||
logger.addHandler(handler);
|
||||
|
||||
} catch (IOException e) {
|
||||
logger.severe("Unable to save log to file.");
|
||||
}
|
||||
|
@ -62,6 +54,16 @@ public class Main {
|
|||
e.printStackTrace();
|
||||
}
|
||||
|
||||
// Initialize database.
|
||||
Database db = new Database("database/initializer.sqlite");
|
||||
db.initialize();
|
||||
for (StudentTeam team : studentTeams) {
|
||||
for (Student student : team.getStudents()) {
|
||||
db.storeStudent(student);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
package nl.andrewlalis.database;
|
||||
|
||||
import nl.andrewlalis.model.Person;
|
||||
import nl.andrewlalis.model.Student;
|
||||
import nl.andrewlalis.model.TeachingAssistant;
|
||||
import nl.andrewlalis.util.FileUtils;
|
||||
|
||||
import java.sql.*;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class abstracts many of the functions needed for interaction with the application's SQLite database.
|
||||
*/
|
||||
public class Database {
|
||||
|
||||
private static final int PERSON_TYPE_STUDENT = 0;
|
||||
private static final int PERSON_TYPE_TA = 1;
|
||||
|
||||
private static final int TEAM_TYPE_STUDENT = 0;
|
||||
private static final int TEAM_TYPE_TA = 1;
|
||||
private static final int TEAM_TYPE_TA_ALL = 2;
|
||||
|
||||
private static final int TEAM_NONE = 0;
|
||||
private static final int TEAM_TA_ALL = 1;
|
||||
|
||||
private static final int ERROR_TYPE_TEAM = 0;
|
||||
private static final int ERROR_TYPE_PERSON = 1;
|
||||
private static final int ERROR_TYPE_SYSTEM = 2;
|
||||
|
||||
/**
|
||||
* The connection needed for all queries.
|
||||
*/
|
||||
private Connection connection;
|
||||
|
||||
/**
|
||||
* The logger for outputting debug info.
|
||||
*/
|
||||
private static final Logger logger = Logger.getLogger(Database.class.getName());
|
||||
static {
|
||||
logger.setParent(Logger.getGlobal());
|
||||
}
|
||||
|
||||
public Database(String databaseFilename) {
|
||||
try {
|
||||
this.connection = DriverManager.getConnection("jdbc:sqlite:" + databaseFilename);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the database from the table_init.sql script, which defines the table schema and starting data.
|
||||
* @return True, if successful, false if not.
|
||||
*/
|
||||
public boolean initialize() {
|
||||
String sql = FileUtils.readStringFromFile("/sql/table_init.sql");
|
||||
String[] commands = sql.split(";");
|
||||
for (String command : commands) {
|
||||
logger.finest("Executing command: " + command);
|
||||
if (command.trim().length() > 1) {
|
||||
try {
|
||||
PreparedStatement statement = this.connection.prepareStatement(command);
|
||||
statement.execute();
|
||||
} catch (SQLException e) {
|
||||
logger.severe("SQLException: " + e.getErrorCode());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.fine("Database initialized.");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a person in the database.
|
||||
* @param person The person object to store.
|
||||
* @param personType The type of person to store, using a constant defined above.
|
||||
* @return True if successful, false otherwise.
|
||||
*/
|
||||
private boolean storePerson(Person person, int personType) {
|
||||
try {
|
||||
String sql = "INSERT INTO persons (id, name, email_address, github_username, person_type_id) VALUES (?, ?, ?, ?, ?);";
|
||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
||||
stmt.setInt(1, person.getNumber());
|
||||
stmt.setString(2, person.getName());
|
||||
stmt.setString(3, person.getEmailAddress());
|
||||
stmt.setString(4, person.getGithubUsername());
|
||||
stmt.setInt(5, personType);
|
||||
return stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a teaching assistant without a team.
|
||||
* @param ta The teaching assistant to store.
|
||||
* @return True if successful, false otherwise.
|
||||
*/
|
||||
public boolean storeTeachingAssistant(TeachingAssistant ta) {
|
||||
return this.storeTeachingAssistant(ta, TEAM_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a teaching assistant in the database.
|
||||
* @param ta The teaching assistant to store.
|
||||
* @param teamId The teaching assistant's team id.
|
||||
* @return True if successful, false otherwise.
|
||||
*/
|
||||
public boolean storeTeachingAssistant(TeachingAssistant ta, int teamId) {
|
||||
if (!storePerson(ta, PERSON_TYPE_TA)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
String sql = "INSERT INTO teaching_assistants (person_id, team_id) VALUES (?, ?);";
|
||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
||||
stmt.setInt(1, ta.getNumber());
|
||||
stmt.setInt(2, teamId);
|
||||
return stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a student without a team.
|
||||
* @param student The student to store.
|
||||
* @return True if successful, false otherwise.
|
||||
*/
|
||||
public boolean storeStudent(Student student) {
|
||||
return this.storeStudent(student, TEAM_NONE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stores a student in the database.
|
||||
* @param student The student to store.
|
||||
* @param teamId The team id for the team the student is in.
|
||||
* @return True if the operation was successful, false otherwise.
|
||||
*/
|
||||
public boolean storeStudent(Student student, int teamId) {
|
||||
if (!storePerson(student, PERSON_TYPE_STUDENT)) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
String sql = "INSERT INTO students (person_id, team_id, chose_partner) VALUES (?, ?, ?);";
|
||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
||||
stmt.setInt(1, student.getNumber());
|
||||
stmt.setInt(2, teamId);
|
||||
stmt.setInt(3, student.getPreferredPartners().size() > 0 ? 1 : 0);
|
||||
return stmt.execute();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,7 +14,6 @@ import org.kohsuke.github.*;
|
|||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
|
@ -206,21 +205,30 @@ public class GithubManager {
|
|||
List<GHRepository> repositories = this.organization.listRepositories().asList();
|
||||
for (GHRepository repo : repositories) {
|
||||
if (repo.getName().contains(sub)) {
|
||||
HttpPatch patch = new HttpPatch("https://api.github.com/repos/" + repo.getFullName() + "?access_token=" + this.accessToken);
|
||||
CloseableHttpClient client = HttpClientBuilder.create().build();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode root = mapper.createObjectNode();
|
||||
root.put("archived", true);
|
||||
String json = mapper.writeValueAsString(root);
|
||||
patch.setEntity(new StringEntity(json));
|
||||
HttpResponse response = client.execute(patch);
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new IOException("Could not archive repository: " + repo.getName() + ". Code: " + response.getStatusLine().getStatusCode());
|
||||
}
|
||||
logger.info("Archived repository: " + repo.getFullName());
|
||||
// TODO: archive repository using Github Java API, instead of Apache HttpUtils.
|
||||
archiveRepository(repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Archives a repository so that it can no longer be manipulated.
|
||||
* TODO: Change to using Github API instead of Apache HttpUtils.
|
||||
* @param repo The repository to archive.
|
||||
* @throws IOException If an error occurs with the HTTP request.
|
||||
*/
|
||||
public void archiveRepository(GHRepository repo) throws IOException {
|
||||
HttpPatch patch = new HttpPatch("https://api.github.com/repos/" + repo.getFullName() + "?access_token=" + this.accessToken);
|
||||
CloseableHttpClient client = HttpClientBuilder.create().build();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ObjectNode root = mapper.createObjectNode();
|
||||
root.put("archived", true);
|
||||
String json = mapper.writeValueAsString(root);
|
||||
patch.setEntity(new StringEntity(json));
|
||||
HttpResponse response = client.execute(patch);
|
||||
if (response.getStatusLine().getStatusCode() != 200) {
|
||||
throw new IOException("Could not archive repository: " + repo.getName() + ". Code: " + response.getStatusLine().getStatusCode());
|
||||
}
|
||||
logger.info("Archived repository: " + repo.getFullName());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ public class Student extends Person {
|
|||
* @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.
|
||||
* other students' numbers.
|
||||
*/
|
||||
public Student(int number, String name, String emailAddress, String githubUsername, List<Integer> preferredPartners) {
|
||||
super(number, name, emailAddress, githubUsername);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package nl.andrewlalis.util;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
/**
|
||||
* Contains some methods which come in handy in lots of other places.
|
||||
*/
|
||||
public class FileUtils {
|
||||
|
||||
/**
|
||||
* Reads the contents of the file specified by the filename into a String.
|
||||
* @param filename The filename to read the file of, either relative or absolute.
|
||||
* @return A string containing the file's contents.
|
||||
*/
|
||||
public static String readStringFromFile(String filename) {
|
||||
try (BufferedReader r = new BufferedReader(new InputStreamReader(FileUtils.class.getResourceAsStream(filename)))) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = r.readLine()) != null) {
|
||||
sb.append(line).append('\n');
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -14,16 +14,26 @@ public class Logging {
|
|||
public static void setup(boolean verbose) throws IOException {
|
||||
Logger logger = Logger.getGlobal();
|
||||
|
||||
if (verbose) {
|
||||
logger.setLevel(Level.FINEST);
|
||||
} else {
|
||||
logger.setLevel(Level.INFO);
|
||||
Handler[] handlers = logger.getHandlers();
|
||||
for (Handler h : handlers) {
|
||||
logger.removeHandler(h);
|
||||
}
|
||||
logger.setUseParentHandlers(false);
|
||||
|
||||
ConsoleHandler handler = new ConsoleHandler();
|
||||
if (verbose) {
|
||||
handler.setLevel(Level.FINEST);
|
||||
} else {
|
||||
handler.setLevel(Level.INFO);
|
||||
}
|
||||
|
||||
logger.addHandler(handler);
|
||||
|
||||
outputFile = new FileHandler("log/latest.txt");
|
||||
formatter = new SimpleFormatter();
|
||||
|
||||
outputFile.setFormatter(formatter);
|
||||
outputFile.setLevel(Level.FINEST);
|
||||
logger.addHandler(outputFile);
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
INSERT INTO persons (id, name, email_address, github_username, person_type_id)
|
||||
VALUES (?, ?, ?, ?, ?);
|
|
@ -0,0 +1,150 @@
|
|||
PRAGMA foreign_keys = TRUE;
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master WHERE type IN ('table', 'index', 'trigger');
|
||||
PRAGMA writable_schema = 0;
|
||||
VACUUM;
|
||||
|
||||
-- Basic schema design.
|
||||
CREATE TABLE IF NOT EXISTS person_types (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
INSERT INTO person_types (id, name)
|
||||
VALUES (0, 'student'),
|
||||
(1, 'teaching-assistant'),
|
||||
(2, 'professor');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS persons (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL,
|
||||
email_address TEXT NOT NULL,
|
||||
github_username TEXT NOT NULL UNIQUE,
|
||||
person_type_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (person_type_id)
|
||||
REFERENCES person_types(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS team_types (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
INSERT INTO team_types (id, name)
|
||||
VALUES (0, 'student_team'),
|
||||
(1, 'teaching_assistant_team'),
|
||||
(2, 'all_teaching_assistants'),
|
||||
(3, 'none');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS teams (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
team_type_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (team_type_id)
|
||||
REFERENCES team_types(id)
|
||||
ON DELETE CASCADE
|
||||
);
|
||||
|
||||
INSERT INTO teams (id, team_type_id)
|
||||
VALUES (0, 3), -- None team for all students or TA's without a team.
|
||||
(1, 2); -- Team for all teaching assistants.
|
||||
|
||||
CREATE TABLE IF NOT EXISTS teaching_assistant_teams (
|
||||
team_id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE,
|
||||
FOREIGN KEY (team_id)
|
||||
REFERENCES teams(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS student_teams (
|
||||
team_id INTEGER PRIMARY KEY,
|
||||
repository_name TEXT,
|
||||
group_id INTEGER NOT NULL UNIQUE,
|
||||
teaching_assistant_team_id INTEGER,
|
||||
FOREIGN KEY (team_id)
|
||||
REFERENCES teams(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
FOREIGN KEY (teaching_assistant_team_id)
|
||||
REFERENCES teaching_assistant_teams(team_id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS students (
|
||||
person_id INTEGER PRIMARY KEY,
|
||||
team_id INTEGER NOT NULL,
|
||||
chose_partner INTEGER NOT NULL,
|
||||
FOREIGN KEY (person_id)
|
||||
REFERENCES persons(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
FOREIGN KEY (team_id)
|
||||
REFERENCES teams(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS teaching_assistants (
|
||||
person_id INTEGER PRIMARY KEY,
|
||||
team_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (person_id)
|
||||
REFERENCES persons(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
FOREIGN KEY (team_id)
|
||||
REFERENCES teams(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
-- Error queue storage.
|
||||
CREATE TABLE IF NOT EXISTS error_types (
|
||||
id INTEGER PRIMARY KEY,
|
||||
name TEXT NOT NULL UNIQUE
|
||||
);
|
||||
|
||||
INSERT INTO error_types (id, name)
|
||||
VALUES (0, 'team_error'),
|
||||
(1, 'person_error'),
|
||||
(2, 'system_error');
|
||||
|
||||
CREATE TABLE IF NOT EXISTS errors (
|
||||
id INTEGER PRIMARY KEY,
|
||||
timestamp DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
error_type_id INTEGER NOT NULL,
|
||||
message TEXT NOT NULL,
|
||||
FOREIGN KEY (error_type_id)
|
||||
REFERENCES error_types(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS team_errors (
|
||||
error_id INTEGER PRIMARY KEY,
|
||||
team_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (error_id)
|
||||
REFERENCES errors(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
FOREIGN KEY (team_id)
|
||||
REFERENCES teams(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS person_errors (
|
||||
error_id INTEGER PRIMARY KEY,
|
||||
person_id INTEGER NOT NULL,
|
||||
FOREIGN KEY (error_id)
|
||||
REFERENCES errors(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE,
|
||||
FOREIGN KEY (person_id)
|
||||
REFERENCES persons(id)
|
||||
ON DELETE CASCADE
|
||||
ON UPDATE CASCADE
|
||||
);
|
Loading…
Reference in New Issue