Merge pull request #5 from rug-advoop/improvements_1
Improvements and new version in development
This commit is contained in:
commit
87ddd8c0a3
Binary file not shown.
Binary file not shown.
46
pom.xml
46
pom.xml
|
@ -21,10 +21,6 @@
|
||||||
</build>
|
</build>
|
||||||
<packaging>jar</packaging>
|
<packaging>jar</packaging>
|
||||||
|
|
||||||
<properties>
|
|
||||||
<jackson.version>2.9.6</jackson.version>
|
|
||||||
</properties>
|
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.commons</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
|
@ -34,12 +30,12 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-cli</groupId>
|
<groupId>commons-cli</groupId>
|
||||||
<artifactId>commons-cli</artifactId>
|
<artifactId>commons-cli</artifactId>
|
||||||
<version>RELEASE</version>
|
<version>1.4</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>commons-email</groupId>
|
<groupId>org.apache.commons</groupId>
|
||||||
<artifactId>commons-email</artifactId>
|
<artifactId>commons-email</artifactId>
|
||||||
<version>RELEASE</version>
|
<version>1.5</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.jetbrains</groupId>
|
<groupId>org.jetbrains</groupId>
|
||||||
|
@ -50,22 +46,7 @@
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
<version>RELEASE</version>
|
<version>4.5.6</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>
|
</dependency>
|
||||||
<!-- Github API Object Library -->
|
<!-- Github API Object Library -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
@ -73,11 +54,22 @@
|
||||||
<artifactId>github-api</artifactId>
|
<artifactId>github-api</artifactId>
|
||||||
<version>1.93</version>
|
<version>1.93</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- SQLite JDBC Driver -->
|
<!-- MySQL JDBC Driver -->
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.xerial</groupId>
|
<groupId>mysql</groupId>
|
||||||
<artifactId>sqlite-jdbc</artifactId>
|
<artifactId>mysql-connector-java</artifactId>
|
||||||
<version>3.23.1</version>
|
<version>8.0.12</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.h2database</groupId>
|
||||||
|
<artifactId>h2</artifactId>
|
||||||
|
<version>1.4.197</version>
|
||||||
|
</dependency>
|
||||||
|
<!-- Hibernate Database ORM -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.hibernate</groupId>
|
||||||
|
<artifactId>hibernate-core</artifactId>
|
||||||
|
<version>5.3.6.Final</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
package nl.andrewlalis;
|
package nl.andrewlalis;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.control.command.executables.*;
|
import nl.andrewlalis.command.executables.*;
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
import nl.andrewlalis.model.database.DbHelper;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
import nl.andrewlalis.ui.view.ManagementView;
|
||||||
|
import nl.andrewlalis.ui.view.StartView;
|
||||||
import nl.andrewlalis.util.CommandLine;
|
import nl.andrewlalis.util.CommandLine;
|
||||||
import nl.andrewlalis.util.Logging;
|
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.Map;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
@ -16,6 +24,11 @@ public class Main {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getGlobal();
|
private static final Logger logger = Logger.getGlobal();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The main application's view, which should be able to be referenced in many places.
|
||||||
|
*/
|
||||||
|
private static ManagementView managementView;
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
|
|
||||||
// Parsed command line arguments.
|
// Parsed command line arguments.
|
||||||
|
@ -24,6 +37,41 @@ public class Main {
|
||||||
// Initialize logger.
|
// Initialize logger.
|
||||||
Logging.setup();
|
Logging.setup();
|
||||||
|
|
||||||
|
//startOldVersion(userOptions);
|
||||||
|
|
||||||
|
logger.info("GithubManager for Github Repositories in Educational Organizations.\n" +
|
||||||
|
"© Andrew Lalis (2018), All rights reserved.\n" +
|
||||||
|
"Program initialized.");
|
||||||
|
|
||||||
|
GithubManager manager = new GithubManager();
|
||||||
|
managementView = new ManagementView(manager);
|
||||||
|
|
||||||
|
initializeTestingData();
|
||||||
|
StartView startView = new StartView(manager, "InitializerTesting", userOptions.get("token"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The management view used for the application.
|
||||||
|
*/
|
||||||
|
public static ManagementView getManagementView() {
|
||||||
|
return managementView;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void initializeTestingData() {
|
||||||
|
try {
|
||||||
|
List<StudentTeam> teams = TeamGenerator.generateFromCSV("/home/andrew/Documents/School/ta/GithubInitializer/student-groups.csv", 2);
|
||||||
|
DbHelper.saveStudentTeams(teams);
|
||||||
|
managementView.updateModels();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Legacy code to run the old version of the application.
|
||||||
|
* @param userOptions The options the user has entered in the command line.
|
||||||
|
*/
|
||||||
|
public static void startOldVersion(Map<String, String> userOptions) {
|
||||||
// Command executor which will be used by all actions the user can do.
|
// Command executor which will be used by all actions the user can do.
|
||||||
CommandExecutor executor = new CommandExecutor();
|
CommandExecutor executor = new CommandExecutor();
|
||||||
|
|
||||||
|
@ -41,10 +89,7 @@ public class Main {
|
||||||
executor.registerCommand("delete_repos", new DeleteRepos());
|
executor.registerCommand("delete_repos", new DeleteRepos());
|
||||||
executor.registerCommand("delegate_student_teams", new DelegateStudentTeams(app));
|
executor.registerCommand("delegate_student_teams", new DelegateStudentTeams(app));
|
||||||
executor.registerCommand("setup_student_repos", new SetupStudentRepos(app));
|
executor.registerCommand("setup_student_repos", new SetupStudentRepos(app));
|
||||||
|
executor.registerCommand("list_repos", new ListRepos());
|
||||||
logger.info("GithubManager for Github Repositories in Educational Organizations.\n" +
|
|
||||||
"© Andrew Lalis (2018), All rights reserved.\n" +
|
|
||||||
"Program initialized.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package nl.andrewlalis.ui.control.command;
|
package nl.andrewlalis.command;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import nl.andrewlalis.command.executables.ExecutableContext;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
import java.util.*;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -23,8 +23,20 @@ public class CommandExecutor {
|
||||||
*/
|
*/
|
||||||
private Map<String, Executable> commands;
|
private Map<String, Executable> commands;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all the executables which have failed to execute.
|
||||||
|
*/
|
||||||
|
private List<ExecutableContext> failedExecutables;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all executables which have been run successfully.
|
||||||
|
*/
|
||||||
|
private List<ExecutableContext> successfulExecutables;
|
||||||
|
|
||||||
public CommandExecutor() {
|
public CommandExecutor() {
|
||||||
this.commands = new HashMap<>();
|
this.commands = new HashMap<>();
|
||||||
|
this.failedExecutables = new ArrayList<>();
|
||||||
|
this.successfulExecutables = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,12 +76,24 @@ public class CommandExecutor {
|
||||||
public void executeCommand(String commandName, String[] args) {
|
public void executeCommand(String commandName, String[] args) {
|
||||||
if (this.commands.containsKey(commandName)) {
|
if (this.commands.containsKey(commandName)) {
|
||||||
logger.info("Command executed: " + commandName + ' ' + Arrays.toString(args));
|
logger.info("Command executed: " + commandName + ' ' + Arrays.toString(args));
|
||||||
if (!this.commands.get(commandName).execute(args)) {
|
Executable executable = this.commands.get(commandName);
|
||||||
|
ExecutableContext context = new ExecutableContext(executable, args);
|
||||||
|
if (!executable.execute(args)) {
|
||||||
logger.warning("Command did not execute successfully.");
|
logger.warning("Command did not execute successfully.");
|
||||||
|
this.failedExecutables.add(context);
|
||||||
|
} else {
|
||||||
|
this.successfulExecutables.add(context);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
logger.warning(commandName + " is not a valid command.");
|
logger.warning(commandName + " is not a valid command.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retries all failed executables, and if successful, removes them from the queue.
|
||||||
|
*/
|
||||||
|
public void rerunFailedExecutables() {
|
||||||
|
this.failedExecutables.removeIf(ExecutableContext::runAgain);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.ui.control.command;
|
package nl.andrewlalis.command;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Classes which implement this interface tell that they may be 'executed', either via command-line, or through the use
|
* Classes which implement this interface tell that they may be 'executed', either via command-line, or through the use
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
import nl.andrewlalis.ui.view.dialogs.DefineTaTeamsDialog;
|
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
import nl.andrewlalis.ui.view.dialogs.DefineTaTeamsDialog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This executable is slightly different from the others, in that it opens up a user interface to make editing TA teams
|
* This executable is slightly different from the others, in that it opens up a user interface to make editing TA teams
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
import nl.andrewlalis.model.StudentTeam;
|
import nl.andrewlalis.model.StudentTeam;
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.command.Executable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object to record a specific executable's execution context (args given), with the ability to re-run the executable
|
||||||
|
* if a failure occurs.
|
||||||
|
*/
|
||||||
|
public class ExecutableContext {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The executable object, without any contextual information.
|
||||||
|
*/
|
||||||
|
private Executable executable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of arguments given to the executable when it was called.
|
||||||
|
*/
|
||||||
|
private String[] args;
|
||||||
|
|
||||||
|
public ExecutableContext(Executable executable, String[] args) {
|
||||||
|
this.executable = executable;
|
||||||
|
this.args = args;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the stored executable again with the same arguments it was originally given.
|
||||||
|
* @return True if the execution was successful, or false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean runAgain() {
|
||||||
|
return this.executable.execute(this.args);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.command.Executable;
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
import nl.andrewlalis.ui.control.command.Executable;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an executable which interacts with github, and therefore needs access to a Github
|
* Represents an executable which interacts with github, and therefore needs access to a Github
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.command.Executable;
|
||||||
import nl.andrewlalis.model.error.Error;
|
import nl.andrewlalis.model.error.Error;
|
||||||
import nl.andrewlalis.ui.control.command.Executable;
|
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
|
@ -0,0 +1,48 @@
|
||||||
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import org.kohsuke.github.GHRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This executable shows a list of repositories with a given substring.
|
||||||
|
*/
|
||||||
|
public class ListRepos extends GithubExecutable {
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The logger for outputting debug info.
|
||||||
|
*/
|
||||||
|
private static final Logger logger = Logger.getLogger(ListRepos.class.getName());
|
||||||
|
static {
|
||||||
|
logger.setParent(Logger.getGlobal());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean executeWithManager(GithubManager manager, String[] args) {
|
||||||
|
if (args.length < 1) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<GHRepository> repos = manager.listReposWithPrefix(args[0]);
|
||||||
|
logger.info(outputRepoList(repos));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prints a nicely formatted list of repositories to a string.
|
||||||
|
* @param repos The list of repositories.
|
||||||
|
* @return A string representation of the list of repos.
|
||||||
|
*/
|
||||||
|
private static String outputRepoList(List<GHRepository> repos) {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
sb.append("List of ").append(repos.size()).append(" repositories:\n");
|
||||||
|
for (GHRepository repo : repos) {
|
||||||
|
sb.append('\t').append(repo.getName()).append('\n');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.command.Executable;
|
||||||
import nl.andrewlalis.model.StudentTeam;
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
import nl.andrewlalis.ui.control.command.Executable;
|
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
import nl.andrewlalis.util.FileUtils;
|
import nl.andrewlalis.util.FileUtils;
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
import nl.andrewlalis.model.StudentTeam;
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
import nl.andrewlalis.model.TATeam;
|
import nl.andrewlalis.model.TATeam;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
import org.kohsuke.github.GHRepository;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -23,13 +24,17 @@ public class SetupStudentRepos extends GithubExecutable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected boolean executeWithManager(GithubManager manager, String[] args) {
|
protected boolean executeWithManager(GithubManager manager, String[] args) {
|
||||||
if (args.length < 1) {
|
if (args.length < 2) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<TATeam> taTeams = this.app.getOrganization().getTaTeams();
|
List<TATeam> taTeams = this.app.getOrganization().getTaTeams();
|
||||||
for (TATeam team : taTeams) {
|
for (TATeam team : taTeams) {
|
||||||
for (StudentTeam studentTeam : team.getStudentTeams()) {
|
for (StudentTeam studentTeam : team.getStudentTeams()) {
|
||||||
manager.setupStudentRepo(studentTeam, team, args[0]);
|
GHRepository assignmentsRepo = manager.getRepository(args[1]);
|
||||||
|
if (assignmentsRepo == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
manager.setupStudentRepo(studentTeam, team, args[0], assignmentsRepo);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
|
@ -25,17 +25,13 @@ import java.util.logging.Logger;
|
||||||
*/
|
*/
|
||||||
public class GithubManager {
|
public class GithubManager {
|
||||||
|
|
||||||
/**
|
|
||||||
* The assignments repository where students will get assignments from.
|
|
||||||
*/
|
|
||||||
private GHRepository assignmentsRepo;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Github object for API interactions.
|
* Github object for API interactions.
|
||||||
*/
|
*/
|
||||||
private GitHub github;
|
private GitHub github;
|
||||||
private GHOrganization organization;
|
private GHOrganization organization;
|
||||||
private String accessToken;
|
private String accessToken;
|
||||||
|
private String organizationName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The logger for outputting debug info.
|
* The logger for outputting debug info.
|
||||||
|
@ -45,15 +41,121 @@ public class GithubManager {
|
||||||
logger.setParent(Logger.getGlobal());
|
logger.setParent(Logger.getGlobal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates an empty github manager, which at this point, essentially just allocates the object in memory.
|
||||||
|
*/
|
||||||
|
public GithubManager() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
public GithubManager(String organizationName, String accessToken) {
|
public GithubManager(String organizationName, String accessToken) {
|
||||||
|
this.organizationName = organizationName;
|
||||||
this.accessToken = accessToken;
|
this.accessToken = accessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the organization that this manager manages.
|
||||||
|
* @return The Organization object that this manager was constructed for.
|
||||||
|
* @throws IOException If the organization does not exist or some other error occurs.
|
||||||
|
*/
|
||||||
|
private GHOrganization getOrganization() throws IOException {
|
||||||
|
if (this.github == null || this.organization == null) {
|
||||||
|
this.github = GitHub.connectUsingOAuth(this.accessToken);
|
||||||
|
this.organization = this.github.getOrganization(this.organizationName);
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.organization;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine if the manager is currently connected to github with the current organization name and access token.
|
||||||
|
* @return True if the manager is connected, or false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean validate() {
|
||||||
try {
|
try {
|
||||||
this.github = GitHub.connectUsingOAuth(accessToken);
|
this.organization = this.getOrganization();
|
||||||
this.organization = this.github.getOrganization(organizationName);
|
return this.organization != null;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.severe("Unable to make a GithubManager with organization name: " + organizationName + " and access token: " + accessToken);
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOrganizationName(String name) {
|
||||||
|
this.organizationName = name;
|
||||||
|
this.github = null;
|
||||||
|
this.organization = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAccessToken(String token) {
|
||||||
|
this.accessToken = token;
|
||||||
|
this.github = null;
|
||||||
|
this.organization = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a repository exists in the current organization.
|
||||||
|
* @param repoName The name of the repository.
|
||||||
|
* @return True if the repository exists, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean repoExists(String repoName) {
|
||||||
|
try {
|
||||||
|
GHRepository repo = this.getOrganization().getRepository(repoName);
|
||||||
|
return repo != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if a team exists in the current organization.
|
||||||
|
* @param teamName The name of the team.
|
||||||
|
* @return True if the team exists, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean teamExists(String teamName) {
|
||||||
|
try {
|
||||||
|
GHTeam team = this.getOrganization().getTeamByName(teamName);
|
||||||
|
return team != null;
|
||||||
|
} catch (IOException e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of repositories with the given substring.
|
||||||
|
* @param substring A string which all repositories should contain.
|
||||||
|
* @return A List of repositories whose names contain the given substring.
|
||||||
|
*/
|
||||||
|
public List<GHRepository> listReposWithPrefix(String substring) {
|
||||||
|
List<GHRepository> repos = new ArrayList<>();
|
||||||
|
try {
|
||||||
|
List<GHRepository> allRepos = this.getOrganization().listRepositories().asList();
|
||||||
|
for (GHRepository repo : allRepos) {
|
||||||
|
if (repo.getName().contains(substring)) {
|
||||||
|
repos.add(repo);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
logger.severe("IOException while listing repositories in the organization.");
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return repos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a repository by name.
|
||||||
|
* @param name The name of the repository.
|
||||||
|
* @return The repository with the given name, or null if none exists.
|
||||||
|
*/
|
||||||
|
public GHRepository getRepository(String name) {
|
||||||
|
System.out.println(name);
|
||||||
|
try {
|
||||||
|
return this.getOrganization().getRepository(name);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.severe("No repository with name: " + name + " exists.");
|
||||||
|
e.printStackTrace();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -64,7 +166,7 @@ public class GithubManager {
|
||||||
List<TATeam> teams = new ArrayList<>();
|
List<TATeam> teams = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
Random rand = new Random();
|
Random rand = new Random();
|
||||||
for (Map.Entry<String, GHTeam> entry : this.organization.getTeams().entrySet()) {
|
for (Map.Entry<String, GHTeam> entry : this.getOrganization().getTeams().entrySet()) {
|
||||||
TATeam team = new TATeam(entry.getKey(), entry.getValue().getId());
|
TATeam team = new TATeam(entry.getKey(), entry.getValue().getId());
|
||||||
team.setGithubTeam(entry.getValue());
|
team.setGithubTeam(entry.getValue());
|
||||||
for (GHUser user : entry.getValue().listMembers().asList()) {
|
for (GHUser user : entry.getValue().listMembers().asList()) {
|
||||||
|
@ -86,7 +188,7 @@ public class GithubManager {
|
||||||
public List<TeachingAssistant> getMembers() {
|
public List<TeachingAssistant> getMembers() {
|
||||||
List<TeachingAssistant> teachingAssistants = new ArrayList<>();
|
List<TeachingAssistant> teachingAssistants = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
for (GHUser member : this.organization.listMembers().asList()) {
|
for (GHUser member : this.getOrganization().listMembers().asList()) {
|
||||||
teachingAssistants.add(new TeachingAssistant(-1, member.getName(), member.getEmail(), member.getLogin()));
|
teachingAssistants.add(new TeachingAssistant(-1, member.getName(), member.getEmail(), member.getLogin()));
|
||||||
}
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
@ -104,25 +206,25 @@ public class GithubManager {
|
||||||
* @throws IOException If an HTTP request failed.
|
* @throws IOException If an HTTP request failed.
|
||||||
*/
|
*/
|
||||||
public void setupAssignmentsRepo(String assignmentsRepoName, String description, String allTeachingAssistants) throws IOException {
|
public void setupAssignmentsRepo(String assignmentsRepoName, String description, String allTeachingAssistants) throws IOException {
|
||||||
GHTeam team = this.organization.getTeamByName(allTeachingAssistants);
|
GHTeam team = this.getOrganization().getTeamByName(allTeachingAssistants);
|
||||||
// Check if the repository already exists.
|
// Check if the repository already exists.
|
||||||
GHRepository existingRepo = this.organization.getRepository(assignmentsRepoName);
|
GHRepository existingRepo = this.getOrganization().getRepository(assignmentsRepoName);
|
||||||
if (existingRepo != null) {
|
if (existingRepo != null) {
|
||||||
existingRepo.delete();
|
existingRepo.delete();
|
||||||
logger.fine("Deleted pre-existing assignments repository.");
|
logger.fine("Deleted pre-existing assignments repository.");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.assignmentsRepo = this.createRepository(assignmentsRepoName, team, description, false, true, true);
|
GHRepository assignmentsRepo = this.createRepository(assignmentsRepoName, team, description, false, true, true);
|
||||||
|
|
||||||
if (this.assignmentsRepo == null) {
|
if (assignmentsRepo == null) {
|
||||||
logger.severe("Could not create assignments repository.");
|
logger.severe("Could not create assignments repository.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.protectMasterBranch(this.assignmentsRepo, team);
|
this.protectMasterBranch(assignmentsRepo, team);
|
||||||
|
|
||||||
// Grant all teaching assistants write access.
|
// Grant all teaching assistants write access.
|
||||||
team.add(this.assignmentsRepo, GHOrganization.Permission.ADMIN);
|
team.add(assignmentsRepo, GHOrganization.Permission.ADMIN);
|
||||||
logger.fine("Gave admin rights to all teaching assistants in team: " + team.getName());
|
logger.fine("Gave admin rights to all teaching assistants in team: " + team.getName());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -132,28 +234,30 @@ public class GithubManager {
|
||||||
* @param team The student team to set up.
|
* @param team The student team to set up.
|
||||||
* @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.
|
||||||
* @param prefix The prefix to append to the front of the repo name.
|
* @param prefix The prefix to append to the front of the repo name.
|
||||||
|
* @param assignmentsRepo The assignments repository.
|
||||||
*/
|
*/
|
||||||
public void setupStudentRepo(StudentTeam team, TATeam taTeam, String prefix) {
|
public void setupStudentRepo(StudentTeam team, TATeam taTeam, String prefix, GHRepository assignmentsRepo) {
|
||||||
// First check that the assignments repo exists, otherwise no invitations can be sent.
|
// First check that the assignments repo exists, otherwise no invitations can be sent.
|
||||||
if (this.assignmentsRepo == null) {
|
if (assignmentsRepo == null) {
|
||||||
logger.warning("Assignments repository must be created before student repositories.");
|
logger.warning("Assignments repository must be created before student repositories.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
GHRepository repo = this.createRepository(team.generateUniqueName(prefix), taTeam.getGithubTeam(), team.generateRepoDescription(), false, true, true);
|
GHRepository repo;
|
||||||
|
try {
|
||||||
if (repo == null) {
|
repo = this.createRepository(team.generateUniqueName(prefix), taTeam.getGithubTeam(), team.generateRepoDescription(), false, true, true);
|
||||||
logger.severe("Repository for student team " + team.getId() + " could not be created.");
|
} catch (IOException e) {
|
||||||
|
logger.severe("Repository for student team " + team.getNumber() + " could not be created.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
team.setRepositoryName(repo.getName());
|
||||||
|
team.setTaTeam(taTeam);
|
||||||
|
|
||||||
this.protectMasterBranch(repo, taTeam.getGithubTeam());
|
this.protectMasterBranch(repo, taTeam.getGithubTeam());
|
||||||
this.createDevelopmentBranch(repo);
|
this.createDevelopmentBranch(repo);
|
||||||
this.addTATeamAsAdmin(repo, taTeam.getGithubTeam());
|
this.addTATeamAsAdmin(repo, taTeam.getGithubTeam());
|
||||||
this.inviteStudentsToRepos(team, repo);
|
this.inviteStudentsToRepos(team, assignmentsRepo);
|
||||||
|
|
||||||
team.setRepository(repo);
|
|
||||||
team.setTaTeam(taTeam);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -161,17 +265,22 @@ public class GithubManager {
|
||||||
* @param substring The substring which repository names should contain to be deleted.
|
* @param substring The substring which repository names should contain to be deleted.
|
||||||
*/
|
*/
|
||||||
public void deleteAllRepositories(String substring) {
|
public void deleteAllRepositories(String substring) {
|
||||||
List<GHRepository> repositories = this.organization.listRepositories().asList();
|
try {
|
||||||
for (GHRepository repo : repositories) {
|
List<GHRepository> repositories = this.getOrganization().listRepositories().asList();
|
||||||
if (repo.getName().contains(substring)) {
|
for (GHRepository repo : repositories) {
|
||||||
try {
|
if (repo.getName().contains(substring)) {
|
||||||
repo.delete();
|
try {
|
||||||
logger.info("Deleted repository: " + repo.getName());
|
repo.delete();
|
||||||
} catch (IOException e) {
|
logger.info("Deleted repository: " + repo.getName());
|
||||||
logger.severe("Could not delete repository: " + repo.getName());
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
logger.severe("Could not delete repository: " + repo.getName());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.severe("Could not get Organization for listing repositories.");
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,11 +289,16 @@ public class GithubManager {
|
||||||
* @param sub Any repository containing this substring will be archived.
|
* @param sub Any repository containing this substring will be archived.
|
||||||
*/
|
*/
|
||||||
public void archiveAllRepositories(String sub) {
|
public void archiveAllRepositories(String sub) {
|
||||||
List<GHRepository> repositories = this.organization.listRepositories().asList();
|
try {
|
||||||
for (GHRepository repo : repositories) {
|
List<GHRepository> repositories = this.getOrganization().listRepositories().asList();
|
||||||
if (repo.getName().contains(sub)) {
|
for (GHRepository repo : repositories) {
|
||||||
archiveRepository(repo);
|
if (repo.getName().contains(sub)) {
|
||||||
|
archiveRepository(repo);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.severe("Could not get Organization for listing repositories.");
|
||||||
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -216,25 +330,36 @@ public class GithubManager {
|
||||||
/**
|
/**
|
||||||
* Invites students in a team to their repository, and the assignments repository.
|
* Invites students in a team to their repository, and the assignments repository.
|
||||||
* @param team The team of students to invite as collaborators.
|
* @param team The team of students to invite as collaborators.
|
||||||
* @param repo The repository created for the students.
|
* @param assignmentsRepo The repository that contains assignments for the class.
|
||||||
*/
|
*/
|
||||||
private void inviteStudentsToRepos(StudentTeam team, GHRepository repo) {
|
private void inviteStudentsToRepos(StudentTeam team, GHRepository assignmentsRepo) {
|
||||||
try {
|
try {
|
||||||
logger.finest("Adding students from team: " + team.getId() + " as collaborators.");
|
logger.finest("Adding students from team: " + team.getNumber() + " as collaborators.");
|
||||||
List<GHUser> users = new ArrayList<>();
|
|
||||||
for (Student student : team.getStudents()) {
|
for (Student student : team.getStudents()) {
|
||||||
GHUser user = this.github.getUser(student.getGithubUsername());
|
GHUser user = this.github.getUser(student.getGithubUsername());
|
||||||
users.add(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
repo.addCollaborators(users);
|
this.addCollaboratorToRepo(user, assignmentsRepo);
|
||||||
this.assignmentsRepo.addCollaborators(users);
|
this.addCollaboratorToRepo(user, this.organization.getRepository(team.getRepositoryName()));
|
||||||
|
}
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
logger.severe("Could not add students as collaborators to assignments or their repo.\n" + team);
|
logger.severe("Could not add students as collaborators to assignments or their repo.\n" + team);
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a user to a repository, or if a failure occurs, log the failure.
|
||||||
|
* @param user The user to add as a collaborator.
|
||||||
|
* @param repository The repository to add the user to.
|
||||||
|
*/
|
||||||
|
private void addCollaboratorToRepo(GHUser user, GHRepository repository) {
|
||||||
|
try {
|
||||||
|
repository.addCollaborators(user);
|
||||||
|
} catch (IOException e) {
|
||||||
|
logger.severe("Could not add user " + user.getLogin() + " to repository " + repository.getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a teaching assistant team as admins to a particular student repository.
|
* Adds a teaching assistant team as admins to a particular student repository.
|
||||||
* @param studentRepo The student repository.
|
* @param studentRepo The student repository.
|
||||||
|
@ -294,25 +419,20 @@ public class GithubManager {
|
||||||
* @param hasWiki Whether the repo has a wiki enabled.
|
* @param hasWiki Whether the repo has a wiki enabled.
|
||||||
* @param hasIssues Whether the repo has issues enabled.
|
* @param hasIssues Whether the repo has issues enabled.
|
||||||
* @param isPrivate Whether or not the repository is private.
|
* @param isPrivate Whether or not the repository is private.
|
||||||
* @return The repository that was created, or null if it could not be created.
|
* @return The repository that was created.
|
||||||
|
* @throws IOException If an exception occurred while sending a request.
|
||||||
*/
|
*/
|
||||||
private GHRepository createRepository(String name, GHTeam taTeam, String description, boolean hasWiki, boolean hasIssues, boolean isPrivate){
|
private GHRepository createRepository(String name, GHTeam taTeam, String description, boolean hasWiki, boolean hasIssues, boolean isPrivate) throws IOException {
|
||||||
try {
|
GHCreateRepositoryBuilder builder = this.getOrganization().createRepository(name);
|
||||||
GHCreateRepositoryBuilder builder = this.organization.createRepository(name);
|
builder.team(taTeam);
|
||||||
builder.team(taTeam);
|
builder.wiki(hasWiki);
|
||||||
builder.wiki(hasWiki);
|
builder.issues(hasIssues);
|
||||||
builder.issues(hasIssues);
|
builder.description(description);
|
||||||
builder.description(description);
|
builder.gitignoreTemplate("Java");
|
||||||
builder.gitignoreTemplate("Java");
|
builder.private_(false); // TODO: Testing value of false. Production uses true.
|
||||||
builder.private_(isPrivate);
|
GHRepository repo = builder.create();
|
||||||
GHRepository repo = builder.create();
|
logger.fine("Created repository: " + repo.getName());
|
||||||
logger.fine("Created repository: " + repo.getName());
|
return repo;
|
||||||
return repo;
|
|
||||||
} catch (IOException e) {
|
|
||||||
logger.severe("Could not create repository: " + name);
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +0,0 @@
|
||||||
package nl.andrewlalis.model;
|
|
||||||
|
|
||||||
public abstract class DatabaseObject {
|
|
||||||
|
|
||||||
public abstract DatabaseObject retrieve();
|
|
||||||
|
|
||||||
public abstract boolean store();
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,31 +1,57 @@
|
||||||
package nl.andrewlalis.model;
|
package nl.andrewlalis.model;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.database.BaseEntity;
|
||||||
|
import nl.andrewlalis.ui.view.components.Detailable;
|
||||||
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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 {
|
@Entity(name = "Person")
|
||||||
|
@Table(name = "persons")
|
||||||
|
public abstract class Person extends BaseEntity implements Detailable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The unique identification number for this person. (P- or S-Number)
|
* The unique identification number for this person. (P- or S-Number)
|
||||||
*/
|
*/
|
||||||
|
@Column(name="number", nullable = false)
|
||||||
protected int number;
|
protected int number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The person's first and last name.
|
* The person's first and last name.
|
||||||
*/
|
*/
|
||||||
|
@Column(name="name", nullable = false)
|
||||||
protected String name;
|
protected String name;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The person's email address.
|
* The person's email address.
|
||||||
*/
|
*/
|
||||||
|
@Column(name="email_address", nullable = false)
|
||||||
protected String emailAddress;
|
protected String emailAddress;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The person's github username.
|
* The person's github username.
|
||||||
*/
|
*/
|
||||||
|
@Column(name="github_username", nullable = false)
|
||||||
protected String githubUsername;
|
protected String githubUsername;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty default Person.
|
||||||
|
*/
|
||||||
|
public Person() {
|
||||||
|
this.number = -1;
|
||||||
|
this.name = null;
|
||||||
|
this.emailAddress = null;
|
||||||
|
this.githubUsername = null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a Person from only a github username, which is, in some cases, enough to perform a lot of actions.
|
* Constructs a Person from only a github username, which is, in some cases, enough to perform a lot of actions.
|
||||||
* @param githubUsername The person's github username.
|
* @param githubUsername The person's github username.
|
||||||
|
@ -58,18 +84,34 @@ public abstract class Person {
|
||||||
return this.number;
|
return this.number;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setNumber(int number) {
|
||||||
|
this.number = number;
|
||||||
|
}
|
||||||
|
|
||||||
public String getName(){
|
public String getName(){
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setName(String name) {
|
||||||
|
this.name = name;
|
||||||
|
}
|
||||||
|
|
||||||
public String getEmailAddress(){
|
public String getEmailAddress(){
|
||||||
return this.emailAddress;
|
return this.emailAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setEmailAddress(String emailAddress) {
|
||||||
|
this.emailAddress = emailAddress;
|
||||||
|
}
|
||||||
|
|
||||||
public String getGithubUsername(){
|
public String getGithubUsername(){
|
||||||
return this.githubUsername;
|
return this.githubUsername;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setGithubUsername(String githubUsername) {
|
||||||
|
this.githubUsername = githubUsername;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determines if two persons are the same. This is defined as:
|
* 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
|
* Two persons are equal if at least one of their personal data points is identical. Because each of the data points
|
||||||
|
@ -100,4 +142,24 @@ public abstract class Person {
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return this.getName() + ", " + this.getNumber() + ", " + this.getEmailAddress() + ", " + this.getGithubUsername();
|
return this.getName() + ", " + this.getNumber() + ", " + this.getEmailAddress() + ", " + this.getGithubUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDetailName() {
|
||||||
|
return this.getName() + ", " + this.getNumber();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDetailDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Pair<String, String>> getDetailPairs() {
|
||||||
|
List<Pair<String, String>> pairs = new ArrayList<>();
|
||||||
|
pairs.add(new Pair<>("Name", this.getName()));
|
||||||
|
pairs.add(new Pair<>("Number", String.valueOf(this.getNumber())));
|
||||||
|
pairs.add(new Pair<>("Email Address", this.getEmailAddress()));
|
||||||
|
pairs.add(new Pair<>("Github Username", this.getGithubUsername()));
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,17 @@
|
||||||
package nl.andrewlalis.model;
|
package nl.andrewlalis.model;
|
||||||
|
|
||||||
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents one student's github information.
|
* Represents one student's github information.
|
||||||
*/
|
*/
|
||||||
|
@Entity(name = "Student")
|
||||||
|
@Table(name="students")
|
||||||
public class Student extends Person {
|
public class Student extends Person {
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(Student.class.getName());
|
private static final Logger logger = Logger.getLogger(Student.class.getName());
|
||||||
|
@ -17,7 +22,27 @@ public class Student extends Person {
|
||||||
/**
|
/**
|
||||||
* A list of partners that the student has said that they would like to be partners with.
|
* A list of partners that the student has said that they would like to be partners with.
|
||||||
*/
|
*/
|
||||||
private List<Integer> preferredPartners;
|
@ManyToMany(fetch = FetchType.EAGER)
|
||||||
|
@JoinTable(
|
||||||
|
name = "student_preferred_partners",
|
||||||
|
joinColumns = { @JoinColumn(name = "student_id")},
|
||||||
|
inverseJoinColumns = {@JoinColumn(name = "preferred_partner_id")}
|
||||||
|
)
|
||||||
|
private List<Student> preferredPartners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The team that this student is assigned to.
|
||||||
|
*/
|
||||||
|
@ManyToOne
|
||||||
|
@JoinColumn(name = "team_id")
|
||||||
|
private StudentTeam team;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty student object.
|
||||||
|
*/
|
||||||
|
public Student() {
|
||||||
|
this.preferredPartners = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a student similarly to a Person, but with an extra preferredPartners list.
|
* Constructs a student similarly to a Person, but with an extra preferredPartners list.
|
||||||
|
@ -28,26 +53,64 @@ public class Student extends Person {
|
||||||
* @param preferredPartners A list of this student's preferred partners, as a list of integers representing the
|
* @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) {
|
public Student(int number, String name, String emailAddress, String githubUsername, List<Student> preferredPartners) {
|
||||||
super(number, name, emailAddress, githubUsername);
|
super(number, name, emailAddress, githubUsername);
|
||||||
this.preferredPartners = preferredPartners;
|
this.preferredPartners = preferredPartners;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Integer> getPreferredPartners() {
|
public List<Student> getPreferredPartners() {
|
||||||
return this.preferredPartners;
|
return this.preferredPartners;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setPreferredPartners(List<Student> preferredPartners) {
|
||||||
|
this.preferredPartners = preferredPartners;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addPreferredPartner(Student student) {
|
||||||
|
this.preferredPartners.add(student);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Using a given map of all students, returns a student's preferred team.
|
* Returns a student's preferred team, including himself.
|
||||||
* @param studentMap A mapping from student number to student for all students who have signed up.
|
* @return A team with unknown number, comprised of this student's preferred partners.
|
||||||
* @return A team with unknown id, comprised of this student's preferred partners.
|
|
||||||
*/
|
*/
|
||||||
public StudentTeam getPreferredTeam(Map<Integer, Student> studentMap) {
|
public StudentTeam getPreferredTeam() {
|
||||||
StudentTeam t = new StudentTeam();
|
StudentTeam t = new StudentTeam();
|
||||||
for (int partnerNumber : this.getPreferredPartners()) {
|
for (Student partner : this.getPreferredPartners()) {
|
||||||
t.addMember(studentMap.get(partnerNumber));
|
t.addMember(partner);
|
||||||
}
|
}
|
||||||
t.addMember(this);
|
t.addMember(this);
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns this student to the given team, from the student's perspective.
|
||||||
|
* @param team The team to set as the assigned team.
|
||||||
|
*/
|
||||||
|
public void assignToTeam(StudentTeam team) {
|
||||||
|
this.team = team;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The team that this student is assigned to. May return null if the student is unassigned.
|
||||||
|
*/
|
||||||
|
public StudentTeam getAssignedTeam() {
|
||||||
|
return this.team;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Pair<String, String>> getDetailPairs() {
|
||||||
|
List<Pair<String, String>> pairs = super.getDetailPairs();
|
||||||
|
String teamNumber = "None";
|
||||||
|
if (this.getAssignedTeam() != null) {
|
||||||
|
teamNumber = String.valueOf(this.getAssignedTeam().getNumber());
|
||||||
|
}
|
||||||
|
pairs.add(new Pair<>("Team Number", teamNumber));
|
||||||
|
|
||||||
|
for (int i = 0; i < this.preferredPartners.size(); i++) {
|
||||||
|
pairs.add(new Pair<>("Preferred partner " + (i + 1), this.preferredPartners.get(i).getDetailName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,22 +1,31 @@
|
||||||
package nl.andrewlalis.model;
|
package nl.andrewlalis.model;
|
||||||
|
|
||||||
import org.kohsuke.github.GHRepository;
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import javax.persistence.Column;
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.ManyToOne;
|
||||||
|
import javax.persistence.Table;
|
||||||
import java.util.Arrays;
|
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 extends Team{
|
@Entity(name = "StudentTeam")
|
||||||
|
@Table(name = "student_teams")
|
||||||
|
public class StudentTeam extends Team {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The repository belonging to this team.
|
* The repository belonging to this team.
|
||||||
*/
|
*/
|
||||||
private GHRepository repository;
|
@Column(name = "repository_name", unique = true)
|
||||||
|
private String repositoryName;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The TATeam responsible for this student team.
|
* The TATeam responsible for this student team.
|
||||||
*/
|
*/
|
||||||
|
@ManyToOne
|
||||||
private TATeam taTeam;
|
private TATeam taTeam;
|
||||||
|
|
||||||
public StudentTeam() {
|
public StudentTeam() {
|
||||||
|
@ -46,7 +55,7 @@ public class StudentTeam extends Team{
|
||||||
// If the student doesn't have an preferred partners, then assume that this is valid.
|
// If the student doesn't have an preferred partners, then assume that this is valid.
|
||||||
if (!studentA.getPreferredPartners().isEmpty()) {
|
if (!studentA.getPreferredPartners().isEmpty()) {
|
||||||
for (Student studentB : this.getStudents()) {
|
for (Student studentB : this.getStudents()) {
|
||||||
if (!studentA.equals(studentB) && !studentA.getPreferredPartners().contains(studentB.getNumber())) {
|
if (!studentA.equals(studentB) && !studentA.getPreferredPartners().contains(studentB)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -62,11 +71,11 @@ public class StudentTeam extends Team{
|
||||||
* Generates a unique name which is intended to be used for the repository name of this team.
|
* Generates a unique name which is intended to be used for the repository name of this team.
|
||||||
* @param prefix A prefix to further reduce the chances of duplicate names.
|
* @param prefix A prefix to further reduce the chances of duplicate names.
|
||||||
* It is suggested to use something like "2018_OOP"
|
* It is suggested to use something like "2018_OOP"
|
||||||
* @return A string comprised of the prefix, team id, and student number of each team member.
|
* @return A string comprised of the prefix, team number, and student number of each team member.
|
||||||
*/
|
*/
|
||||||
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.number);
|
||||||
for (Student s : this.getStudents()) {
|
for (Student s : this.getStudents()) {
|
||||||
sb.append('_').append(s.getNumber());
|
sb.append('_').append(s.getNumber());
|
||||||
}
|
}
|
||||||
|
@ -79,7 +88,7 @@ public class StudentTeam extends Team{
|
||||||
*/
|
*/
|
||||||
public String generateRepoDescription() {
|
public String generateRepoDescription() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Group ").append(this.id).append(": ");
|
sb.append("Group ").append(this.number).append(": ");
|
||||||
for (int i = 0; i < this.memberCount(); i++) {
|
for (int i = 0; i < this.memberCount(); i++) {
|
||||||
sb.append(this.getStudents()[i].getName());
|
sb.append(this.getStudents()[i].getName());
|
||||||
if (i != this.memberCount()-1) {
|
if (i != this.memberCount()-1) {
|
||||||
|
@ -89,12 +98,12 @@ public class StudentTeam extends Team{
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public GHRepository getRepository() {
|
public String getRepositoryName() {
|
||||||
return this.repository;
|
return this.repositoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRepository(GHRepository repo) {
|
public void setRepositoryName(String repositoryName) {
|
||||||
this.repository = repo;
|
this.repositoryName = repositoryName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public TATeam getTaTeam() {
|
public TATeam getTaTeam() {
|
||||||
|
@ -104,4 +113,22 @@ public class StudentTeam extends Team{
|
||||||
public void setTaTeam(TATeam team) {
|
public void setTaTeam(TATeam team) {
|
||||||
this.taTeam = team;
|
this.taTeam = team;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDetailName() {
|
||||||
|
return this.generateRepoDescription();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Pair<String, String>> getDetailPairs() {
|
||||||
|
List<Pair<String, String>> pairs = super.getDetailPairs();
|
||||||
|
pairs.add(new Pair<>("Repository Name", this.getRepositoryName()));
|
||||||
|
String taTeamName = "None";
|
||||||
|
if (this.getTaTeam() != null) {
|
||||||
|
taTeamName = this.getTaTeam().getDetailName();
|
||||||
|
}
|
||||||
|
pairs.add(new Pair<>("TA Team", taTeamName));
|
||||||
|
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,19 @@ package nl.andrewlalis.model;
|
||||||
|
|
||||||
import org.kohsuke.github.GHTeam;
|
import org.kohsuke.github.GHTeam;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.OneToMany;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
import javax.persistence.Transient;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a teaching assistant team, which is itself a 'team' in the organization. This class is used for parsing
|
* Represents a teaching assistant team, which is itself a 'team' in the organization.
|
||||||
* json from requests to github to get a list of all teams in the organization.
|
|
||||||
*/
|
*/
|
||||||
|
@Entity(name = "TATeam")
|
||||||
|
@Table(name = "ta_teams")
|
||||||
public class TATeam extends Team {
|
public class TATeam extends Team {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -20,11 +25,13 @@ public class TATeam extends Team {
|
||||||
/**
|
/**
|
||||||
* The Github team associated with this team.
|
* The Github team associated with this team.
|
||||||
*/
|
*/
|
||||||
|
@Transient
|
||||||
private GHTeam githubTeam;
|
private GHTeam githubTeam;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of all student teams for which this TA team is responsible.
|
* A list of all student teams for which this TA team is responsible.
|
||||||
*/
|
*/
|
||||||
|
@OneToMany
|
||||||
private List<StudentTeam> studentTeams;
|
private List<StudentTeam> studentTeams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,8 +1,13 @@
|
||||||
package nl.andrewlalis.model;
|
package nl.andrewlalis.model;
|
||||||
|
|
||||||
|
import javax.persistence.Entity;
|
||||||
|
import javax.persistence.Table;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents an administrator in the organization, who manages student teams.
|
* Represents an administrator in the organization, who manages student teams.
|
||||||
*/
|
*/
|
||||||
|
@Entity(name = "TeachingAssistant")
|
||||||
|
@Table(name = "teaching_assistants")
|
||||||
public class TeachingAssistant extends Person {
|
public class TeachingAssistant extends Person {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -1,5 +1,10 @@
|
||||||
package nl.andrewlalis.model;
|
package nl.andrewlalis.model;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.database.BaseEntity;
|
||||||
|
import nl.andrewlalis.ui.view.components.Detailable;
|
||||||
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import javax.persistence.*;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -8,39 +13,55 @@ import java.util.List;
|
||||||
* An abstract Team object from which both Teaching Assistant and Student teams can be built. A Team consists of a list
|
* An abstract Team object from which both Teaching Assistant and Student teams can be built. A Team consists of a list
|
||||||
* of members, and a unique identification number.
|
* of members, and a unique identification number.
|
||||||
*/
|
*/
|
||||||
public abstract class Team {
|
@Entity(name = "Team")
|
||||||
|
@Table(name = "teams")
|
||||||
|
public abstract class Team extends BaseEntity implements Detailable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An identification number unique to this team alone.
|
* An identification number unique to this team alone.
|
||||||
*/
|
*/
|
||||||
protected int id;
|
@Column(name = "number")
|
||||||
|
protected int number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A list of members of this team.
|
* A list of members of this team.
|
||||||
*/
|
*/
|
||||||
|
@OneToMany(fetch = FetchType.EAGER)
|
||||||
|
@JoinTable(
|
||||||
|
name = "team_members",
|
||||||
|
joinColumns = {@JoinColumn(name = "team_id")},
|
||||||
|
inverseJoinColumns = {@JoinColumn(name = "person_id")}
|
||||||
|
)
|
||||||
private List<Person> members;
|
private List<Person> members;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs this team with the given id.
|
* Constructs this team with the given number.
|
||||||
* @param id The id to assign to this team.
|
* @param number The number to assign to this team.
|
||||||
*/
|
*/
|
||||||
public Team(int id) {
|
public Team(int number) {
|
||||||
this.id = id;
|
this.number = number;
|
||||||
this.members = new ArrayList<>();
|
this.members = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param newId The new id number to assign to this team.
|
* Constructs an empty team with a default id of -1.
|
||||||
*/
|
*/
|
||||||
public void setId(int newId) {
|
protected Team() {
|
||||||
this.id = newId;
|
this(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return This team's id number.
|
* @param newId The new number number to assign to this team.
|
||||||
*/
|
*/
|
||||||
public int getId() {
|
public void setNumber(int newId) {
|
||||||
return this.id;
|
this.number = newId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return This team's number number.
|
||||||
|
*/
|
||||||
|
public int getNumber() {
|
||||||
|
return this.number;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,7 +144,7 @@ public abstract class Team {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an object is equal to this team. First checks if the other object is a Team, and then if it has the
|
* 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 number and team size. If both of those conditions are met, then it will check that all team members are the
|
||||||
* same.
|
* same.
|
||||||
* @param obj The object to check for equality.
|
* @param obj The object to check for equality.
|
||||||
* @return True if the two objects represent the same team, or false otherwise.
|
* @return True if the two objects represent the same team, or false otherwise.
|
||||||
|
@ -132,7 +153,7 @@ public abstract class Team {
|
||||||
public boolean equals(Object obj) {
|
public boolean equals(Object obj) {
|
||||||
if (obj instanceof Team) {
|
if (obj instanceof Team) {
|
||||||
Team team = (Team) obj;
|
Team team = (Team) obj;
|
||||||
return team.getId() == this.getId() && this.hasSameMembers(team);
|
return team.getNumber() == this.getNumber() && this.hasSameMembers(team);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -143,11 +164,32 @@ public abstract class Team {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Team of ").append(this.memberCount()).append(" members:\tID: ").append(this.id).append('\n');
|
sb.append("Team of ").append(this.memberCount()).append(" members:\tID: ").append(this.number).append('\n');
|
||||||
for (Person person : this.members) {
|
for (Person person : this.members) {
|
||||||
sb.append(person.toString()).append('\n');
|
sb.append(person.toString()).append('\n');
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDetailName() {
|
||||||
|
return String.valueOf(this.getNumber());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDetailDescription() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Pair<String, String>> getDetailPairs() {
|
||||||
|
List<Pair<String, String>> pairs = new ArrayList<>();
|
||||||
|
pairs.add(new Pair<>("Number", this.getDetailName()));
|
||||||
|
|
||||||
|
for (int i = 0; i < this.memberCount(); i++) {
|
||||||
|
pairs.add(new Pair<>("Member " + (i + 1), this.members.get(i).getDetailName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return pairs;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
package nl.andrewlalis.model.database;
|
||||||
|
|
||||||
|
import javax.persistence.GeneratedValue;
|
||||||
|
import javax.persistence.GenerationType;
|
||||||
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.MappedSuperclass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines a base entity which all others in the database extend from.
|
||||||
|
*/
|
||||||
|
@MappedSuperclass
|
||||||
|
public abstract class BaseEntity {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The number for this entity.
|
||||||
|
*/
|
||||||
|
@Id
|
||||||
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
private Long id;
|
||||||
|
|
||||||
|
public Long getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setId(Long id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,266 +0,0 @@
|
||||||
package nl.andrewlalis.model.database;
|
|
||||||
|
|
||||||
import nl.andrewlalis.model.*;
|
|
||||||
|
|
||||||
import java.sql.*;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
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 = 1000000;
|
|
||||||
private static final int TEAM_TA_ALL = 1000001;
|
|
||||||
|
|
||||||
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.
|
|
||||||
* Then, inserts some constant starter data from /sql/insert/types.sql.
|
|
||||||
* @return True if successful, false if not.
|
|
||||||
*/
|
|
||||||
public boolean initialize() {
|
|
||||||
List<PreparedStatement> tableStatements = Utils.prepareStatementsFromFile("/sql/table_init.sql", this.connection);
|
|
||||||
for (PreparedStatement statement : tableStatements) {
|
|
||||||
try {
|
|
||||||
statement.execute();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQLException while executing prepared statement:\n" + statement.toString() + "\nCode: " + e.getErrorCode());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.fine("Database tables initialized.");
|
|
||||||
List<PreparedStatement> insertStatements = Utils.prepareStatementsFromFile("/sql/insert/types.sql", this.connection);
|
|
||||||
for (PreparedStatement statement : insertStatements) {
|
|
||||||
try {
|
|
||||||
statement.execute();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQLException while inserting into table:\n" + statement.toString() + "\nCode: " + e.getErrorCode());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.fine("Initial types inserted.");
|
|
||||||
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.
|
|
||||||
*/
|
|
||||||
public boolean insertPerson(Person person, int personType) {
|
|
||||||
try {
|
|
||||||
logger.finest("Storing person: " + person);
|
|
||||||
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) {
|
|
||||||
logger.severe("SQLException while inserting Person: " + person + '\n' + e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a student in the database.
|
|
||||||
* @param student The student to store.
|
|
||||||
* @return True if the operation was successful, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean insertStudent(Student student) {
|
|
||||||
logger.finest("Storing student: " + student);
|
|
||||||
if (!insertPerson(student, PERSON_TYPE_STUDENT)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO students (person_id, chose_partner) VALUES (?, ?);";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, student.getNumber());
|
|
||||||
stmt.setInt(2, student.getPreferredPartners().size() > 0 ? 1 : 0);
|
|
||||||
if (!stmt.execute()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
// Storing partners.
|
|
||||||
String sqlPartner = "INSERT INTO student_preferred_partners (student_id, partner_id) VALUES (?, ?);";
|
|
||||||
PreparedStatement stmtPartner = this.connection.prepareStatement(sqlPartner);
|
|
||||||
for (int partnerId : student.getPreferredPartners()) {
|
|
||||||
stmtPartner.setInt(1, student.getNumber());
|
|
||||||
stmtPartner.setInt(2, partnerId);
|
|
||||||
stmtPartner.execute();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQL Exception while inserting Student into database.\n" + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a teaching assistant in the database.
|
|
||||||
* @param ta The teaching assistant to store.
|
|
||||||
* @return True if successful, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean insertTeachingAssistant(TeachingAssistant ta) {
|
|
||||||
if (!insertPerson(ta, PERSON_TYPE_TA)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO teaching_assistants (person_id) VALUES (?);";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, ta.getNumber());
|
|
||||||
stmt.execute();
|
|
||||||
return true;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQL Exception while inserting TeachingAssistant.\n" + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a team in the database, and any persons who do not yet exist.
|
|
||||||
* @param team The team to store.
|
|
||||||
* @param type The type of team that this is.
|
|
||||||
* @param personType The type of people that this team is made of.
|
|
||||||
* @return True if successful, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean insertTeam(Team team, int type, int personType) {
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO teams (id, team_type_id) VALUES (?, ?);";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, team.getId());
|
|
||||||
stmt.setInt(2, type);
|
|
||||||
if (stmt.execute()) {
|
|
||||||
for (Person p : team.getMembers()) {
|
|
||||||
this.insertPerson(p, personType);
|
|
||||||
String sqlMembers = "INSERT INTO team_members (team_id, person_id) VALUES (?, ?);";
|
|
||||||
PreparedStatement stmtMembers = this.connection.prepareStatement(sqlMembers);
|
|
||||||
stmtMembers.setInt(1, team.getId());
|
|
||||||
stmtMembers.setInt(2, p.getNumber());
|
|
||||||
stmtMembers.execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQLException while inserting team: " + team + '\n' + e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a student team in the database.
|
|
||||||
* @param team The team to store.
|
|
||||||
* @return True if successful, false otherwise.
|
|
||||||
*/
|
|
||||||
public boolean insertStudentTeam(StudentTeam team) {
|
|
||||||
if (!this.insertTeam(team, TEAM_TYPE_STUDENT, PERSON_TYPE_STUDENT)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
String sql = "INSERT INTO student_teams (team_id, repository_name, teaching_assistant_team_id) VALUES (?, ?, ?);";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, team.getId());
|
|
||||||
stmt.setString(2, (team.getRepository() == null) ? null : team.getRepository().getName());
|
|
||||||
stmt.setInt(3, (team.getTaTeam() == null) ? TEAM_NONE : team.getTaTeam().getId());
|
|
||||||
return stmt.execute();
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQLException while inserting student team: " + team + '\n' + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores a list of student teams in the database.
|
|
||||||
* @param teams The list of teams to store.
|
|
||||||
* @return True if successful, or false if an error occurred.
|
|
||||||
*/
|
|
||||||
public boolean storeStudentTeams(List<StudentTeam> teams) {
|
|
||||||
for (StudentTeam team : teams) {
|
|
||||||
this.insertStudentTeam(team);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a list of preferred partners that each student has set.
|
|
||||||
* @param studentId The student id to search by.
|
|
||||||
* @return A list of student id's for all students that the given student wishes to be their partner.
|
|
||||||
*/
|
|
||||||
private List<Integer> retrievePreferredPartners(int studentId) {
|
|
||||||
try {
|
|
||||||
logger.finest("Retrieving preferred partners of student: " + studentId);
|
|
||||||
String sql = "SELECT partner_id FROM student_preferred_partners WHERE student_id=?;";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, studentId);
|
|
||||||
ResultSet results = stmt.executeQuery();
|
|
||||||
List<Integer> partners = new ArrayList<>();
|
|
||||||
while (results.next()) {
|
|
||||||
partners.add(results.getInt(1));
|
|
||||||
}
|
|
||||||
return partners;
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQL Exception while retrieving preferred partners of student: " + studentId + '\n' + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return new ArrayList<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves a student by their id.
|
|
||||||
* @param id The id of the student (student number)
|
|
||||||
* @return The student corresponding to this number, or null if it could not be found.
|
|
||||||
*/
|
|
||||||
public Student retrieveStudent(int id) {
|
|
||||||
try {
|
|
||||||
String sql = "SELECT * FROM persons WHERE id=?";
|
|
||||||
PreparedStatement stmt = this.connection.prepareStatement(sql);
|
|
||||||
stmt.setInt(1, id);
|
|
||||||
ResultSet result = stmt.executeQuery();
|
|
||||||
return new Student(id, result.getString("name"), result.getString("email_address"), result.getString("github_username"), this.retrievePreferredPartners(id));
|
|
||||||
} catch (SQLException e) {
|
|
||||||
logger.severe("SQL Exception while retrieving Student.\n" + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package nl.andrewlalis.model.database;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.Student;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
import org.hibernate.Session;
|
||||||
|
import org.hibernate.Transaction;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class will contain some static methods to help in the retrieval of commonly used information.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public class DbHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of students in the database.
|
||||||
|
* @return A list of students.
|
||||||
|
*/
|
||||||
|
public static List<Student> getStudents() {
|
||||||
|
Session session = DbUtil.getSessionFactory().openSession();
|
||||||
|
List<Student> students = (List<Student>) session.createQuery("from Student").list();
|
||||||
|
session.close();
|
||||||
|
return students;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves a list of student teams to the database.
|
||||||
|
*/
|
||||||
|
public static void saveStudentTeams(List<StudentTeam> teams) {
|
||||||
|
Session session = DbUtil.getSessionFactory().openSession();
|
||||||
|
Transaction tx = session.beginTransaction();
|
||||||
|
|
||||||
|
for (StudentTeam team : teams) {
|
||||||
|
for (Student s : team.getStudents()) {
|
||||||
|
session.save(s);
|
||||||
|
}
|
||||||
|
session.save(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.commit();
|
||||||
|
session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a list of student teams in the database.
|
||||||
|
* @return A list of student teams.
|
||||||
|
*/
|
||||||
|
public static List<StudentTeam> getStudentTeams() {
|
||||||
|
Session session = DbUtil.getSessionFactory().openSession();
|
||||||
|
List<StudentTeam> studentTeams = (List<StudentTeam>) session.createQuery("from StudentTeam").list();
|
||||||
|
session.close();
|
||||||
|
return studentTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package nl.andrewlalis.model.database;
|
||||||
|
|
||||||
|
import org.hibernate.SessionFactory;
|
||||||
|
import org.hibernate.boot.MetadataSources;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistry;
|
||||||
|
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class for easier interaction with the Hibernate database.
|
||||||
|
*/
|
||||||
|
public class DbUtil {
|
||||||
|
|
||||||
|
private static SessionFactory sessionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set up the session factory based on hibernate.cfg.xml.
|
||||||
|
*/
|
||||||
|
private static void setUp() {
|
||||||
|
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
|
||||||
|
.configure()
|
||||||
|
.build();
|
||||||
|
try {
|
||||||
|
sessionFactory = new MetadataSources(registry)
|
||||||
|
.buildMetadata()
|
||||||
|
.buildSessionFactory();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
StandardServiceRegistryBuilder.destroy(registry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Close the session factory when it's no longer needed.
|
||||||
|
*/
|
||||||
|
public static void tearDown() {
|
||||||
|
if (sessionFactory != null) {
|
||||||
|
sessionFactory.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the session factory so that sessions can be made.
|
||||||
|
* @return The session factory.
|
||||||
|
*/
|
||||||
|
public static SessionFactory getSessionFactory() {
|
||||||
|
if (sessionFactory == null) {
|
||||||
|
setUp();
|
||||||
|
}
|
||||||
|
return sessionFactory;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,52 +0,0 @@
|
||||||
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() + '\n' + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return statements;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import java.awt.event.ActionEvent;
|
import java.awt.event.ActionEvent;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import java.awt.event.ActionListener;
|
import java.awt.event.ActionListener;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.listeners;
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
|
|
@ -0,0 +1,68 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.view.AbstractView;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ViewChangeListener is attached to buttons which should change the view to a new view. With this listener, one
|
||||||
|
* needs to simply give the previous view, and the next view, and
|
||||||
|
*/
|
||||||
|
public class ViewChangeListener implements ActionListener {
|
||||||
|
|
||||||
|
protected AbstractView previousView;
|
||||||
|
protected AbstractView newView;
|
||||||
|
|
||||||
|
public ViewChangeListener(AbstractView previousView, AbstractView newView) {
|
||||||
|
this.previousView = previousView;
|
||||||
|
this.newView = newView;
|
||||||
|
this.newView.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosing(WindowEvent windowEvent) {
|
||||||
|
back();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is called just before the view is changed.
|
||||||
|
* @return True if the change should happen, or false if some validation or check prevents the user from moving to
|
||||||
|
* the next view.
|
||||||
|
*/
|
||||||
|
protected boolean beforeChange() {
|
||||||
|
// Child classes can implement extra behavior here.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines some default behavior for switching to a new view.
|
||||||
|
* @param actionEvent The event which triggered this action.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
if (this.beforeChange()) {
|
||||||
|
this.forward();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes to the new view, and hides the previous view.
|
||||||
|
*/
|
||||||
|
private void forward() {
|
||||||
|
this.previousView.setVisible(false);
|
||||||
|
this.newView.reset();
|
||||||
|
this.newView.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Goes 'back in time', or rather, hides the current view and moves back to the one which sent us here.
|
||||||
|
*/
|
||||||
|
private void back() {
|
||||||
|
this.previousView.reset();
|
||||||
|
this.previousView.setVisible(true);
|
||||||
|
this.newView.setVisible(false);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,70 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.create_assignments_view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.ViewChangeListener;
|
||||||
|
import nl.andrewlalis.ui.view.AbstractView;
|
||||||
|
import nl.andrewlalis.ui.view.CreateAssignmentsView;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when the user clicks 'next' in the CreateAssignmentsView. This listener is responsible for checking that
|
||||||
|
* the user enters a correct repository name, or if not, asks if the user wishes to create the repository with that
|
||||||
|
* name.
|
||||||
|
*/
|
||||||
|
public class NextListener extends ViewChangeListener {
|
||||||
|
|
||||||
|
public NextListener(AbstractView previousView, AbstractView newView) {
|
||||||
|
super(previousView, newView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate that the repository the user has entered exists in the organization.
|
||||||
|
* @return True if the repository exists, or if the user creates a repository with that name, or false if an
|
||||||
|
* Assignments repository was not created.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean beforeChange() {
|
||||||
|
CreateAssignmentsView assignmentsView = (CreateAssignmentsView) this.previousView;
|
||||||
|
String repoName = assignmentsView.getRepositoryName();
|
||||||
|
|
||||||
|
// Check that the repository name is legitimate.
|
||||||
|
if (repoName.trim().length() == 0) {
|
||||||
|
JOptionPane.showMessageDialog(this.previousView, "Repository name is empty.", "Error", JOptionPane.WARNING_MESSAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the repository already exists.
|
||||||
|
GithubManager manager = assignmentsView.getGithubManager();
|
||||||
|
if (manager.repoExists(repoName)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
// If not, we have to create it here.
|
||||||
|
int reply = JOptionPane.showConfirmDialog(
|
||||||
|
assignmentsView,
|
||||||
|
"The repository you gave does not exist.\nWould you like to create it?",
|
||||||
|
"Create new repository?",
|
||||||
|
JOptionPane.YES_NO_OPTION);
|
||||||
|
if (reply == JOptionPane.YES_OPTION) {
|
||||||
|
try {
|
||||||
|
String description = JOptionPane.showInputDialog(assignmentsView, "Enter a description for the repository.", "Assignments Repository Description", JOptionPane.QUESTION_MESSAGE);
|
||||||
|
assignmentsView.getGithubManager().setupAssignmentsRepo(repoName, description, this.getTeachingAssistantsTeamName());
|
||||||
|
return true;
|
||||||
|
} catch (IOException e) {
|
||||||
|
//e.printStackTrace();
|
||||||
|
JOptionPane.showMessageDialog(assignmentsView, "Could not create repository:\n" + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Replace this with a selector for an existing team of teaching assistants. Or configure this afterwards.
|
||||||
|
private String getTeachingAssistantsTeamName() {
|
||||||
|
String name = JOptionPane.showInputDialog(this.previousView, "Please enter (exactly) the name of Github team\nthat contains all teaching assistants.", "Select TA Team", JOptionPane.QUESTION_MESSAGE);
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.input_students_file_view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.control.listeners.ViewChangeListener;
|
||||||
|
import nl.andrewlalis.ui.view.AbstractView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when the user clicks 'Done' after selecting a file to input.
|
||||||
|
*/
|
||||||
|
public class DoneListener extends ViewChangeListener {
|
||||||
|
|
||||||
|
public DoneListener(AbstractView previousView, AbstractView newView) {
|
||||||
|
super(previousView, newView);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.input_students_file_view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.Main;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
import nl.andrewlalis.model.database.DbHelper;
|
||||||
|
import nl.andrewlalis.ui.view.InputStudentsFileView;
|
||||||
|
import nl.andrewlalis.util.TeamGenerator;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.filechooser.FileFilter;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when the user selects a CSV file to use to populate the teams list.
|
||||||
|
*/
|
||||||
|
public class FileSelectListener implements ActionListener {
|
||||||
|
|
||||||
|
private InputStudentsFileView fileView;
|
||||||
|
|
||||||
|
public FileSelectListener(InputStudentsFileView parent) {
|
||||||
|
this.fileView = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
// First check if the user has entered a valid team size.
|
||||||
|
if (this.fileView.getStudentsPerTeam() < 1) {
|
||||||
|
JOptionPane.showMessageDialog(this.fileView, "Invalid or missing team size.", "Error", JOptionPane.WARNING_MESSAGE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It is assumed that the team size is valid, so the user can choose a file.
|
||||||
|
JFileChooser chooser = new JFileChooser();
|
||||||
|
chooser.setAcceptAllFileFilterUsed(false);
|
||||||
|
chooser.addChoosableFileFilter(new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File file) {
|
||||||
|
return file.isDirectory() || file.getName().toLowerCase().endsWith(".csv");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getDescription() {
|
||||||
|
return "CSV Files (*.csv)";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
int fileResponse = chooser.showOpenDialog(this.fileView);
|
||||||
|
|
||||||
|
if (fileResponse == JFileChooser.APPROVE_OPTION) {
|
||||||
|
int teamSize = this.fileView.getStudentsPerTeam();
|
||||||
|
try {
|
||||||
|
List<StudentTeam> teams = TeamGenerator.generateFromCSV(chooser.getSelectedFile().getAbsolutePath(), teamSize);
|
||||||
|
DbHelper.saveStudentTeams(teams);
|
||||||
|
Main.getManagementView().updateModels();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.management_view;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.PopupMenuEvent;
|
||||||
|
import javax.swing.event.PopupMenuListener;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This listener, when added to a JPopupMenu, will select the clicked row when a user right-clicks on the table.
|
||||||
|
*/
|
||||||
|
public class PopupSelector implements PopupMenuListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table on which to select rows.
|
||||||
|
*/
|
||||||
|
private JTable table;
|
||||||
|
|
||||||
|
public PopupSelector(JTable table) {
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeVisible(PopupMenuEvent popupMenuEvent) {
|
||||||
|
JPopupMenu popupMenu = (JPopupMenu) popupMenuEvent.getSource();
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
int rowAtPoint = table.rowAtPoint(SwingUtilities.convertPoint(popupMenu, new Point(0, 0), table));
|
||||||
|
if (rowAtPoint > -1) {
|
||||||
|
table.setRowSelectionInterval(rowAtPoint, rowAtPoint);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuWillBecomeInvisible(PopupMenuEvent popupMenuEvent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void popupMenuCanceled(PopupMenuEvent popupMenuEvent) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.management_view;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This abstract class defines listeners which listen to tables, that is, a table row is clicked on in the table, and
|
||||||
|
* that is passed to children.
|
||||||
|
*/
|
||||||
|
public abstract class TableRowListener implements ActionListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The table of which to get the row.
|
||||||
|
*/
|
||||||
|
private JTable table;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a table row listener.
|
||||||
|
* @param table The table on which to get selected rows.
|
||||||
|
*/
|
||||||
|
public TableRowListener(JTable table) {
|
||||||
|
this.table = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The selected row.
|
||||||
|
*/
|
||||||
|
protected final int getSelectedRow() {
|
||||||
|
return this.table.getSelectedRow();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The table that this listener is attached to.
|
||||||
|
*/
|
||||||
|
protected final JTable getTable() {
|
||||||
|
return this.table;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.management_view.student_actions;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.control.listeners.management_view.TableRowListener;
|
||||||
|
import nl.andrewlalis.ui.view.table_models.StudentTableModel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when the user intends to remove a selected student from the course entirely. This entails a few things:
|
||||||
|
* 1. Remove them from any team they are in.
|
||||||
|
* 2. Archive any repository that is empty as a result of removing them.
|
||||||
|
* 3. Remove the student from the list of students.
|
||||||
|
* (This should not actually remove the record, just set it as removed.)
|
||||||
|
*/
|
||||||
|
public class RemoveFromCourseListener extends TableRowListener {
|
||||||
|
|
||||||
|
public RemoveFromCourseListener(JTable table) {
|
||||||
|
super(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
StudentTableModel model = (StudentTableModel) this.getTable().getModel();
|
||||||
|
|
||||||
|
System.out.println(model.getStudentAt(this.getSelectedRow()));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.management_view.student_actions;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.Student;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.management_view.TableRowListener;
|
||||||
|
import nl.andrewlalis.ui.view.dialogs.TeamChooserDialog;
|
||||||
|
import nl.andrewlalis.ui.view.table_models.StudentTableModel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when the user wishes to set the team of a certain student. This should do the following:
|
||||||
|
* 1. User selects team to set student to, or chooses to create a new team.
|
||||||
|
* 2. StudentTeam object is created or modified.
|
||||||
|
* 3. The repository is updated automatically.
|
||||||
|
*/
|
||||||
|
public class SetTeamListener extends TableRowListener {
|
||||||
|
|
||||||
|
public SetTeamListener(JTable table) {
|
||||||
|
super(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
StudentTableModel model = (StudentTableModel) this.getTable().getModel();
|
||||||
|
Student student = model.getStudentAt(this.getSelectedRow());
|
||||||
|
|
||||||
|
StudentTeam chosenTeam = (StudentTeam) new TeamChooserDialog(SwingUtilities.getWindowAncestor(this.getTable())).getSelectedTeam();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners.start_view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.control.listeners.ViewChangeListener;
|
||||||
|
import nl.andrewlalis.ui.view.AbstractView;
|
||||||
|
import nl.andrewlalis.ui.view.StartView;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listener for when the user intends to create repositories for a new course.
|
||||||
|
*/
|
||||||
|
public class CreateAssignmentsRepoListener extends ViewChangeListener {
|
||||||
|
|
||||||
|
public CreateAssignmentsRepoListener(AbstractView previousView, AbstractView newView) {
|
||||||
|
super(previousView, newView);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All that needs to be done here is check that the github manager can work with the given info.
|
||||||
|
* @return True if the github manager accepts the organization name and access token, false otherwise.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
protected boolean beforeChange() {
|
||||||
|
StartView startView = (StartView) this.previousView;
|
||||||
|
startView.getGithubManager().setOrganizationName(startView.getOrganizationName());
|
||||||
|
startView.getGithubManager().setAccessToken(startView.getAccessToken());
|
||||||
|
return startView.getGithubManager().validate();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,140 @@
|
||||||
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* All views in the application will extend from this view, as a means of simplifying and organizing how visual
|
||||||
|
* components are made.
|
||||||
|
*/
|
||||||
|
public abstract class AbstractView extends JFrame {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A GithubManager object which can be used to interact with github.
|
||||||
|
*/
|
||||||
|
private GithubManager githubManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of views which are linked to this one via buttons in the component pane.
|
||||||
|
*/
|
||||||
|
private List<AbstractView> childViews;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of views which lead to this one.
|
||||||
|
*/
|
||||||
|
private List<AbstractView> parentViews;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The image icon for all abstract views.
|
||||||
|
*/
|
||||||
|
private static final ImageIcon imageIcon = new ImageIcon(AbstractView.class.getResource("/image/icon.png"));
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initializes the view by packing the content pane as it is defined by any child, and setting some generic swing
|
||||||
|
* values.
|
||||||
|
* @param title The window's title.
|
||||||
|
* @param startVisible Whether or not to start the view as visible.
|
||||||
|
* @param defaultCloseOperation What to do when the user closes the window.
|
||||||
|
* @param preferredSize The preferred size of the view.
|
||||||
|
* @param githubManager The manager used for this view.
|
||||||
|
*/
|
||||||
|
AbstractView(String title, boolean startVisible, int defaultCloseOperation, Dimension preferredSize, GithubManager githubManager) {
|
||||||
|
super(title);
|
||||||
|
this.githubManager = githubManager;
|
||||||
|
this.childViews = new ArrayList<>();
|
||||||
|
this.parentViews = new ArrayList<>();
|
||||||
|
this.setIconImage(imageIcon.getImage());
|
||||||
|
this.setContentPane(this.buildContentPane());
|
||||||
|
this.setDefaultCloseOperation(defaultCloseOperation);
|
||||||
|
if (preferredSize != null) {
|
||||||
|
this.setSize(preferredSize);
|
||||||
|
}
|
||||||
|
this.setLocationRelativeTo(null);
|
||||||
|
this.pack();
|
||||||
|
this.setVisible(startVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs this view. Child classes will define how the content pane is constructed by returning that content
|
||||||
|
* pane here.
|
||||||
|
* @return The content pane containing the view to be rendered.
|
||||||
|
*/
|
||||||
|
protected abstract JPanel buildContentPane();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resets this view and all form components within it. It is the responsibility of child classes to define how to
|
||||||
|
* reset themselves.
|
||||||
|
*/
|
||||||
|
public void reset() {
|
||||||
|
// Child classes can define custom behavior here.
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Extends the default expose behaviour by recursively disposing all views which are linked to this one.
|
||||||
|
*/
|
||||||
|
public void dispose() {
|
||||||
|
for (AbstractView view : this.childViews) {
|
||||||
|
view.dispose();
|
||||||
|
}
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public GithubManager getGithubManager() {
|
||||||
|
return githubManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a text input field panel.
|
||||||
|
* @param labelText The text for the label above the panel.
|
||||||
|
* @param textField A reference to the text field that is used in the panel.
|
||||||
|
* @return A JPanel containing the label and text field.
|
||||||
|
*/
|
||||||
|
final JPanel generateTextFieldPanel(String labelText, JTextField textField) {
|
||||||
|
JPanel newPanel = new JPanel(new BorderLayout());
|
||||||
|
newPanel.add(new JLabel(labelText), BorderLayout.NORTH);
|
||||||
|
newPanel.add(textField);
|
||||||
|
newPanel.setBorder(BorderFactory.createEmptyBorder(5, 2, 5, 2));
|
||||||
|
return newPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a view as linked to this one. That way, this view can be referenced elsewhere, even when hidden.
|
||||||
|
* @param view The view to link.
|
||||||
|
*/
|
||||||
|
protected final void addChildView(AbstractView view) {
|
||||||
|
this.childViews.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of children of this view.
|
||||||
|
*/
|
||||||
|
protected final List<AbstractView> getChildViews() {
|
||||||
|
return this.childViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a view as linked to this one as a parent.
|
||||||
|
* @param view The parent view.
|
||||||
|
*/
|
||||||
|
protected final void addParentView(AbstractView view) {
|
||||||
|
this.parentViews.add(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The list of parents of this view.
|
||||||
|
*/
|
||||||
|
protected final List<AbstractView> getParentViews() {
|
||||||
|
return this.parentViews;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all parents registered to this view.
|
||||||
|
*/
|
||||||
|
protected final void removeParents() {
|
||||||
|
this.parentViews.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,53 @@
|
||||||
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.create_assignments_view.NextListener;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In this view, the user will enter the name of an assignments repository to use for the course, or allows the user to
|
||||||
|
* create a new one.
|
||||||
|
*
|
||||||
|
* Once the user is here, it is guaranteed that the github manager has been validated.
|
||||||
|
*/
|
||||||
|
public class CreateAssignmentsView extends AbstractView {
|
||||||
|
|
||||||
|
private JTextField repositoryNameField;
|
||||||
|
|
||||||
|
public CreateAssignmentsView(GithubManager manager) {
|
||||||
|
super("Create/Set Assignments Repository",
|
||||||
|
false,
|
||||||
|
DISPOSE_ON_CLOSE,
|
||||||
|
null,
|
||||||
|
manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRepositoryName() {
|
||||||
|
return this.repositoryNameField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JPanel buildContentPane() {
|
||||||
|
JPanel contentPane = new JPanel();
|
||||||
|
contentPane.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
JPanel fieldPanel = new JPanel();
|
||||||
|
fieldPanel.setLayout(new BoxLayout(fieldPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
|
||||||
|
this.repositoryNameField = new JTextField();
|
||||||
|
fieldPanel.add(this.generateTextFieldPanel("Assignments repository name:", this.repositoryNameField));
|
||||||
|
contentPane.add(fieldPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
|
||||||
|
JButton nextButton = new JButton("Next");
|
||||||
|
InputStudentsFileView inputStudentsFileView = new InputStudentsFileView(this.getGithubManager());
|
||||||
|
this.addChildView(inputStudentsFileView);
|
||||||
|
inputStudentsFileView.addParentView(this);
|
||||||
|
nextButton.addActionListener(new NextListener(this, inputStudentsFileView));
|
||||||
|
contentPane.add(nextButton, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
return contentPane;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
package nl.andrewlalis.ui.view;
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.command.CommandExecutor;
|
||||||
import nl.andrewlalis.model.Organization;
|
import nl.andrewlalis.model.Organization;
|
||||||
import nl.andrewlalis.ui.control.OutputTextHandler;
|
import nl.andrewlalis.ui.control.OutputTextHandler;
|
||||||
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
|
||||||
import nl.andrewlalis.ui.control.listeners.*;
|
import nl.andrewlalis.ui.control.listeners.*;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
@ -129,11 +129,6 @@ public class InitializerApp extends JFrame {
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Delegate Student Teams", new DelegateStudentTeamsListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Delegate Student Teams", new DelegateStudentTeamsListener(this.executor, this)));
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Generate Assignments Repo", new GenerateAssignmentsRepoListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Generate Assignments Repo", new GenerateAssignmentsRepoListener(this.executor, this)));
|
||||||
|
|
||||||
// TODO: Enable this once the define teams dialog is complete.
|
|
||||||
// JButton defineTaTeamsButton = new JButton("Define TA Teams");
|
|
||||||
// defineTaTeamsButton.addActionListener(new DefineTaTeamsListener(this.executor, this));
|
|
||||||
// commonActionsPanel.add(f);
|
|
||||||
|
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Delete Repos", new DeleteReposListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Delete Repos", new DeleteReposListener(this.executor, this)));
|
||||||
|
|
||||||
// Extra panel to push buttons to the top.
|
// Extra panel to push buttons to the top.
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.Main;
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.input_students_file_view.DoneListener;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.input_students_file_view.FileSelectListener;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* In this view, the user will select a file to read a list of students from, and generates the list of teams from that.
|
||||||
|
*/
|
||||||
|
public class InputStudentsFileView extends AbstractView {
|
||||||
|
|
||||||
|
private JTextField studentsPerTeamField;
|
||||||
|
|
||||||
|
InputStudentsFileView(GithubManager manager) {
|
||||||
|
super("Input Students CSV",
|
||||||
|
false,
|
||||||
|
DISPOSE_ON_CLOSE,
|
||||||
|
null,
|
||||||
|
manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getStudentsPerTeam() {
|
||||||
|
return Integer.parseUnsignedInt(this.studentsPerTeamField.getText());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JPanel buildContentPane() {
|
||||||
|
JPanel contentPane = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
JLabel helpLabel = new JLabel("Please select the CSV file containing student sign-up responses.");
|
||||||
|
contentPane.add(helpLabel, BorderLayout.NORTH);
|
||||||
|
|
||||||
|
JPanel inputPanel = new JPanel();
|
||||||
|
inputPanel.setLayout(new BoxLayout(inputPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
// Button to select a file.
|
||||||
|
JButton selectFileButton = new JButton("Select File");
|
||||||
|
this.studentsPerTeamField = new JTextField("2");
|
||||||
|
inputPanel.add(this.generateTextFieldPanel("How many students per team?", this.studentsPerTeamField));
|
||||||
|
selectFileButton.addActionListener(new FileSelectListener(this));
|
||||||
|
inputPanel.add(selectFileButton);
|
||||||
|
|
||||||
|
contentPane.add(inputPanel, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// Button to confirm and move to the next view.
|
||||||
|
JButton doneButton = new JButton("Done");
|
||||||
|
Main.getManagementView().addParentView(this);
|
||||||
|
doneButton.addActionListener(new DoneListener(this, Main.getManagementView()));
|
||||||
|
contentPane.add(doneButton, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
return contentPane;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,186 @@
|
||||||
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.model.database.DbHelper;
|
||||||
|
import nl.andrewlalis.model.database.DbUtil;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.management_view.PopupSelector;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.management_view.student_actions.RemoveFromCourseListener;
|
||||||
|
import nl.andrewlalis.ui.view.components.DetailPanel;
|
||||||
|
import nl.andrewlalis.ui.view.table_models.StudentTableModel;
|
||||||
|
import nl.andrewlalis.ui.view.table_models.StudentTeamTableModel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.awt.event.WindowAdapter;
|
||||||
|
import java.awt.event.WindowEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The view in which the user manages a course.
|
||||||
|
*/
|
||||||
|
public class ManagementView extends AbstractView {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model for the students table.
|
||||||
|
*/
|
||||||
|
private StudentTableModel studentsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The model for the student teams table.
|
||||||
|
*/
|
||||||
|
private StudentTeamTableModel studentTeamModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A panel which displays the details of selected entities.
|
||||||
|
*/
|
||||||
|
private DetailPanel detailPanel;
|
||||||
|
|
||||||
|
public ManagementView(GithubManager githubManager) {
|
||||||
|
super(
|
||||||
|
"Course Management",
|
||||||
|
false,
|
||||||
|
DISPOSE_ON_CLOSE,
|
||||||
|
null,
|
||||||
|
githubManager
|
||||||
|
);
|
||||||
|
this.setExtendedState(this.getExtendedState() | JFrame.MAXIMIZED_BOTH);
|
||||||
|
|
||||||
|
// Dispose of all parents when this window closes. This is unique to the management view.
|
||||||
|
this.addWindowListener(new WindowAdapter() {
|
||||||
|
@Override
|
||||||
|
public void windowClosed(WindowEvent windowEvent) {
|
||||||
|
for (AbstractView parent : getParentViews()) {
|
||||||
|
parent.dispose();
|
||||||
|
}
|
||||||
|
DbUtil.tearDown(); // Shut down the database session factory once everything is done.
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JPanel buildContentPane() {
|
||||||
|
JPanel contentPane = new JPanel(new BorderLayout());
|
||||||
|
contentPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
|
this.detailPanel = new DetailPanel();
|
||||||
|
|
||||||
|
contentPane.add(this.buildCommandPanel(), BorderLayout.WEST);
|
||||||
|
contentPane.add(this.detailPanel, BorderLayout.EAST);
|
||||||
|
contentPane.add(this.buildOverviewPanel(), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return contentPane;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel for the command prompt interface.
|
||||||
|
*/
|
||||||
|
private JPanel buildCommandPanel() {
|
||||||
|
JPanel commandPanel = new JPanel(new BorderLayout());
|
||||||
|
commandPanel.setBorder(BorderFactory.createLoweredBevelBorder());
|
||||||
|
|
||||||
|
commandPanel.add(new JLabel("Commands", SwingConstants.CENTER), BorderLayout.NORTH);
|
||||||
|
commandPanel.add(new JTextArea("Command prompt area goes here."), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
// Construct the sub-panel for commands at the bottom of the panel.
|
||||||
|
JPanel inputPanel = new JPanel(new BorderLayout());
|
||||||
|
JTextField commandTextField = new JTextField();
|
||||||
|
inputPanel.add(commandTextField, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
commandPanel.add(inputPanel, BorderLayout.SOUTH);
|
||||||
|
|
||||||
|
return commandPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Builds the overview panel, containing a listing of entities.
|
||||||
|
*/
|
||||||
|
private JPanel buildOverviewPanel() {
|
||||||
|
JPanel overviewPanel = new JPanel(new BorderLayout());
|
||||||
|
overviewPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
|
||||||
|
|
||||||
|
overviewPanel.add(this.buildSearchPanel(), BorderLayout.NORTH);
|
||||||
|
|
||||||
|
// The real container for all the data views.
|
||||||
|
JTabbedPane tabbedPane = new JTabbedPane();
|
||||||
|
|
||||||
|
tabbedPane.addTab("Students", this.buildStudentsTablePanel());
|
||||||
|
tabbedPane.addTab("Student Teams", this.buildStudentTeamsTablePanel());
|
||||||
|
tabbedPane.addTab("Teaching Assistants", this.buildTAsTablePanel());
|
||||||
|
|
||||||
|
overviewPanel.add(tabbedPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return overviewPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a JPanel containing utilities to search the data in the various tables in the application.
|
||||||
|
* @return A JPanel containing search functionality.
|
||||||
|
*/
|
||||||
|
private JPanel buildSearchPanel() {
|
||||||
|
JPanel searchPanel = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
searchPanel.add(new JLabel("Search", SwingConstants.LEFT), BorderLayout.WEST);
|
||||||
|
searchPanel.add(new JTextField(), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return searchPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides a JScrollPane and JPanel to surround a table.
|
||||||
|
* @param table The table to wrap.
|
||||||
|
* @return The JPanel containing the table, wrapped in a JScrollPane.
|
||||||
|
*/
|
||||||
|
private JPanel buildGenericTablePanel(JTable table) {
|
||||||
|
JPanel surroundingPanel = new JPanel(new BorderLayout());
|
||||||
|
surroundingPanel.add(new JScrollPane(table), BorderLayout.CENTER);
|
||||||
|
return surroundingPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel to be put into a tab for display of a list of students.
|
||||||
|
*/
|
||||||
|
private JPanel buildStudentsTablePanel() {
|
||||||
|
// Initialize the model, table, and a surrounding scroll pane.
|
||||||
|
this.studentsModel = new StudentTableModel(DbHelper.getStudents());
|
||||||
|
|
||||||
|
JTable table = new JTable(this.studentsModel);
|
||||||
|
table.setFillsViewportHeight(true);
|
||||||
|
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
|
||||||
|
detailPanel.setDetailableEntity(studentsModel.getStudentAt(table.getSelectedRow()));
|
||||||
|
});
|
||||||
|
JPopupMenu menu = new JPopupMenu("Menu");
|
||||||
|
JMenuItem removeItem = new JMenuItem("Remove from course");
|
||||||
|
removeItem.addActionListener(new RemoveFromCourseListener(table));
|
||||||
|
menu.add(removeItem);
|
||||||
|
menu.addPopupMenuListener(new PopupSelector(table));
|
||||||
|
table.setComponentPopupMenu(menu);
|
||||||
|
|
||||||
|
return this.buildGenericTablePanel(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel to be put into a tab for display of a list of student teams.
|
||||||
|
*/
|
||||||
|
private JPanel buildStudentTeamsTablePanel() {
|
||||||
|
this.studentTeamModel = new StudentTeamTableModel(DbHelper.getStudentTeams());
|
||||||
|
|
||||||
|
JTable table = new JTable(this.studentTeamModel);
|
||||||
|
table.setFillsViewportHeight(true);
|
||||||
|
table.getSelectionModel().addListSelectionListener(listSelectionEvent -> {
|
||||||
|
detailPanel.setDetailableEntity(studentTeamModel.getStudentTeamAt(table.getSelectedRow()));
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.buildGenericTablePanel(table);
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel buildTAsTablePanel() {
|
||||||
|
return new JPanel();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all models in the management view in accordance with the database.
|
||||||
|
*/
|
||||||
|
public void updateModels() {
|
||||||
|
this.studentsModel.setStudentsList(DbHelper.getStudents());
|
||||||
|
this.studentTeamModel.setStudentTeamsList(DbHelper.getStudentTeams());
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package nl.andrewlalis.ui.view;
|
||||||
|
|
||||||
|
import nl.andrewlalis.Main;
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.ViewChangeListener;
|
||||||
|
import nl.andrewlalis.ui.control.listeners.start_view.CreateAssignmentsRepoListener;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At this view, the user is asked to first enter the name of the organization, and the access token they created for
|
||||||
|
* their authenticated Github account.
|
||||||
|
*
|
||||||
|
* Then, the user must choose whether they are starting a new course setup, or managing an existing one.
|
||||||
|
*
|
||||||
|
* If they choose to start a new course, they are taken to the AssignmentsRepoView, otherwise if they want to manage
|
||||||
|
* an existing course, they are taken to the ManagementView.
|
||||||
|
*/
|
||||||
|
public class StartView extends AbstractView {
|
||||||
|
|
||||||
|
// Fields which hold information needed by the Github Manager.
|
||||||
|
private JTextField organizationNameField;
|
||||||
|
private JTextField accessTokenField;
|
||||||
|
|
||||||
|
public StartView(GithubManager githubManager) {
|
||||||
|
super("Github Initializer Startup",
|
||||||
|
true,
|
||||||
|
DISPOSE_ON_CLOSE,
|
||||||
|
null,
|
||||||
|
githubManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs the starting view, with pre-defined organization and access tokens.
|
||||||
|
* @param githubManager A reference to the github manager this application uses.
|
||||||
|
* @param organizationName The name of the organization.
|
||||||
|
* @param accessToken The access token from the user.
|
||||||
|
*/
|
||||||
|
public StartView(GithubManager githubManager, String organizationName, String accessToken) {
|
||||||
|
this(githubManager);
|
||||||
|
this.organizationNameField.setText(organizationName);
|
||||||
|
this.accessTokenField.setText(accessToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getOrganizationName() {
|
||||||
|
return this.organizationNameField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAccessToken() {
|
||||||
|
return this.accessTokenField.getText();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected JPanel buildContentPane() {
|
||||||
|
JPanel contentPane = new JPanel(new BorderLayout());
|
||||||
|
|
||||||
|
JPanel infoInputPanel = new JPanel();
|
||||||
|
infoInputPanel.setLayout(new BoxLayout(infoInputPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
this.organizationNameField = new JTextField();
|
||||||
|
this.accessTokenField = new JTextField();
|
||||||
|
infoInputPanel.add(this.generateTextFieldPanel("Organization name:", this.organizationNameField));
|
||||||
|
infoInputPanel.add(this.generateTextFieldPanel("Access token:", this.accessTokenField));
|
||||||
|
|
||||||
|
JPanel buttonsPanel = new JPanel();
|
||||||
|
// Create the button for going to the Create assignments repository view.
|
||||||
|
JButton assignmentsViewButton = new JButton("Start New Course");
|
||||||
|
CreateAssignmentsView assignmentsView = new CreateAssignmentsView(this.getGithubManager());
|
||||||
|
this.addChildView(assignmentsView);
|
||||||
|
assignmentsView.addParentView(this);
|
||||||
|
assignmentsViewButton.addActionListener(new CreateAssignmentsRepoListener(this, assignmentsView));
|
||||||
|
|
||||||
|
// Create the button for going straight to the management view.
|
||||||
|
JButton managementViewButton = new JButton("Manage Existing Course");
|
||||||
|
this.addChildView(Main.getManagementView());
|
||||||
|
Main.getManagementView().addParentView(this);
|
||||||
|
managementViewButton.addActionListener(new ViewChangeListener(this, Main.getManagementView()));
|
||||||
|
|
||||||
|
buttonsPanel.add(assignmentsViewButton);
|
||||||
|
buttonsPanel.add(managementViewButton);
|
||||||
|
|
||||||
|
contentPane.add(infoInputPanel, BorderLayout.CENTER);
|
||||||
|
contentPane.add(buttonsPanel, BorderLayout.SOUTH);
|
||||||
|
return contentPane;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,95 @@
|
||||||
|
package nl.andrewlalis.ui.view.components;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.view.table_models.DetailPairsModel;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The detail panel is meant for displaying the details of a specific entity. The actual content/details to display is
|
||||||
|
* given by classes which implement the Detailable interface.
|
||||||
|
*/
|
||||||
|
public class DetailPanel extends JPanel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name field shows the entity's name.
|
||||||
|
*/
|
||||||
|
private JTextField nameField;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The description area shows the entity's description.
|
||||||
|
*/
|
||||||
|
private JTextArea descriptionTextArea;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A model to represent the key-value pairs of this entity.
|
||||||
|
*/
|
||||||
|
private DetailPairsModel detailPairsModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the panel with some basic empty components.
|
||||||
|
*/
|
||||||
|
public DetailPanel() {
|
||||||
|
super();
|
||||||
|
this.setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
|
||||||
|
this.add(new JLabel("Details", SwingConstants.CENTER));
|
||||||
|
this.add(this.buildNamePanel());
|
||||||
|
this.add(this.buildDescriptionPanel());
|
||||||
|
this.add(this.buildPairsTablePanel());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets this panel's properties according to the given entity.
|
||||||
|
* @param entity The entity to get details from.
|
||||||
|
*/
|
||||||
|
public void setDetailableEntity(Detailable entity) {
|
||||||
|
this.nameField.setText(entity.getDetailName());
|
||||||
|
this.descriptionTextArea.setText(entity.getDetailDescription());
|
||||||
|
this.detailPairsModel.setPairs(entity.getDetailPairs());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel containing the name field.
|
||||||
|
*/
|
||||||
|
private JPanel buildNamePanel() {
|
||||||
|
this.nameField = new JTextField();
|
||||||
|
this.nameField.setEditable(false);
|
||||||
|
|
||||||
|
JPanel namePanel = new JPanel(new BorderLayout());
|
||||||
|
namePanel.add(new JLabel("Name:", SwingConstants.LEFT), BorderLayout.WEST);
|
||||||
|
namePanel.add(this.nameField, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return namePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel containing the description text area.
|
||||||
|
*/
|
||||||
|
private JPanel buildDescriptionPanel() {
|
||||||
|
this.descriptionTextArea = new JTextArea();
|
||||||
|
this.descriptionTextArea.setEditable(false);
|
||||||
|
|
||||||
|
JPanel descriptionPanel = new JPanel(new BorderLayout());
|
||||||
|
descriptionPanel.add(new JLabel("Description:", SwingConstants.CENTER), BorderLayout.NORTH);
|
||||||
|
descriptionPanel.add(this.descriptionTextArea, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return descriptionPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel containing a table of properties.
|
||||||
|
*/
|
||||||
|
private JPanel buildPairsTablePanel() {
|
||||||
|
this.detailPairsModel = new DetailPairsModel();
|
||||||
|
|
||||||
|
JPanel tablePanel = new JPanel(new BorderLayout());
|
||||||
|
tablePanel.add(new JLabel("Properties:", SwingConstants.LEFT), BorderLayout.NORTH);
|
||||||
|
|
||||||
|
JTable pairsTable = new JTable(this.detailPairsModel);
|
||||||
|
JScrollPane scrollPane = new JScrollPane(pairsTable);
|
||||||
|
tablePanel.add(scrollPane, BorderLayout.CENTER);
|
||||||
|
|
||||||
|
return tablePanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
package nl.andrewlalis.ui.view.components;
|
||||||
|
|
||||||
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Objects which implement this interface must provide
|
||||||
|
*/
|
||||||
|
public interface Detailable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return The display name for this object.
|
||||||
|
*/
|
||||||
|
String getDetailName();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Some more information to display below the name for this object.
|
||||||
|
*/
|
||||||
|
String getDetailDescription();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A String-to-String mapping for some key value pairs of properties to display.
|
||||||
|
*/
|
||||||
|
List<Pair<String, String>> getDetailPairs();
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,18 @@
|
||||||
|
package nl.andrewlalis.ui.view.dialogs;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.Team;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
|
|
||||||
|
public class TeamChooserDialog extends JDialog {
|
||||||
|
|
||||||
|
public TeamChooserDialog(Window parent) {
|
||||||
|
super(parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Team getSelectedTeam() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
package nl.andrewlalis.ui.view.table_models;
|
||||||
|
|
||||||
|
import nl.andrewlalis.util.Pair;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Represents the small (2 column) table to display properties of a detailable entity.
|
||||||
|
*/
|
||||||
|
public class DetailPairsModel extends AbstractTableModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The pairs of properties.
|
||||||
|
*/
|
||||||
|
private List<Pair<String, String>> pairs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Columns for this model.
|
||||||
|
*/
|
||||||
|
private String[] columns = {"Property", "Value"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs an empty list of pairs.
|
||||||
|
*/
|
||||||
|
public DetailPairsModel() {
|
||||||
|
this.pairs = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPairs(List<Pair<String, String>> pairs) {
|
||||||
|
this.pairs = pairs;
|
||||||
|
this.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return this.pairs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return this.columns.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int i) {
|
||||||
|
return this.columns[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int i, int i1) {
|
||||||
|
Pair pair = this.pairs.get(i);
|
||||||
|
|
||||||
|
switch (i1) {
|
||||||
|
case 0:
|
||||||
|
return pair.getFirst();
|
||||||
|
case 1:
|
||||||
|
return pair.getSecond();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,86 @@
|
||||||
|
package nl.andrewlalis.ui.view.table_models;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.Student;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This table model is used for the representation of a list of persons, with their basic information.
|
||||||
|
*/
|
||||||
|
public class StudentTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The list of data that is used in the table.
|
||||||
|
*/
|
||||||
|
private List<Student> studentsList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A default list of column headers for this table.
|
||||||
|
*/
|
||||||
|
private String[] columns = {"Number", "Name", "Email", "Github", "Team"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new model based on the given list of students.
|
||||||
|
* @param studentsList A list of students to display in the table model.
|
||||||
|
*/
|
||||||
|
public StudentTableModel(List<Student> studentsList) {
|
||||||
|
this.studentsList = studentsList;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new list of students as the data for this list model.
|
||||||
|
* @param newList The new list of students to use.
|
||||||
|
*/
|
||||||
|
public void setStudentsList(List<Student> newList) {
|
||||||
|
this.studentsList = newList;
|
||||||
|
this.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the student in a particular row.
|
||||||
|
* @param row The row of the table.
|
||||||
|
* @return The student object at the specified row, or null if none is found.
|
||||||
|
*/
|
||||||
|
public Student getStudentAt(int row) {
|
||||||
|
if (row >= 0 && row < this.studentsList.size()) {
|
||||||
|
return this.studentsList.get(row);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return studentsList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return this.columns.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int i) {
|
||||||
|
return this.columns[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int row, int col) {
|
||||||
|
Student student = this.getStudentAt(row);
|
||||||
|
|
||||||
|
switch(col) {
|
||||||
|
case 0:
|
||||||
|
return student.getNumber();
|
||||||
|
case 1:
|
||||||
|
return student.getName();
|
||||||
|
case 2:
|
||||||
|
return student.getEmailAddress();
|
||||||
|
case 3:
|
||||||
|
return student.getGithubUsername();
|
||||||
|
case 4:
|
||||||
|
return student.getAssignedTeam().getId();
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,145 @@
|
||||||
|
package nl.andrewlalis.ui.view.table_models;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.Student;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
|
||||||
|
import javax.swing.table.AbstractTableModel;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This table model represents the list of student teams.
|
||||||
|
*/
|
||||||
|
public class StudentTeamTableModel extends AbstractTableModel {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The container for the data objects.
|
||||||
|
*/
|
||||||
|
private List<StudentTeam> studentTeamsList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The column headers for this model. In addition to these headers, this model will dynamically create headers for
|
||||||
|
* each additional student to be listed in the table.
|
||||||
|
*/
|
||||||
|
private final String[] staticColumns = {"Number", "Repository Name", "TA Team"};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dynamic columns which are generated depending on the teams.
|
||||||
|
*/
|
||||||
|
private String[] columns = {};
|
||||||
|
|
||||||
|
public StudentTeamTableModel() {
|
||||||
|
this.studentTeamsList = new ArrayList<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public StudentTeamTableModel(List<StudentTeam> teams) {
|
||||||
|
super();
|
||||||
|
this.setStudentTeamsList(teams);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a new list of student teams as the data for this list model.
|
||||||
|
* @param newList A list of student teams to display in the table model.
|
||||||
|
*/
|
||||||
|
public void setStudentTeamsList(List<StudentTeam> newList) {
|
||||||
|
this.studentTeamsList = newList;
|
||||||
|
int maxMembers = this.getMaxMemberCount();
|
||||||
|
if (this.columns.length != maxMembers) {
|
||||||
|
this.generateColumnNames(maxMembers);
|
||||||
|
this.fireTableStructureChanged();
|
||||||
|
}
|
||||||
|
this.fireTableDataChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the student team in a particular row.
|
||||||
|
* @param row The row of the table.
|
||||||
|
* @return The student team object at the specified row, or null if none is found.
|
||||||
|
*/
|
||||||
|
public StudentTeam getStudentTeamAt(int row) {
|
||||||
|
if (row >= 0 && row < this.studentTeamsList.size()) {
|
||||||
|
return this.studentTeamsList.get(row);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getRowCount() {
|
||||||
|
return this.studentTeamsList.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getColumnCount() {
|
||||||
|
return this.columns.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getColumnName(int i) {
|
||||||
|
if (i >= 0 && i < this.columns.length) {
|
||||||
|
return this.columns[i];
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValueAt(int i, int i1) {
|
||||||
|
StudentTeam team = this.getStudentTeamAt(i);
|
||||||
|
|
||||||
|
switch (i1) {
|
||||||
|
case 0:
|
||||||
|
return team.getId();
|
||||||
|
case 1:
|
||||||
|
return (team.getRepositoryName() == null) ? "None" : team.getRepositoryName();
|
||||||
|
case 2:
|
||||||
|
return (team.getTaTeam() == null) ? "None" : team.getTaTeam().getDetailName();
|
||||||
|
default:
|
||||||
|
return this.getMemberInColumn(team, i1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a particular student name in a column of the table. This is used for the staticColumns which show all members in
|
||||||
|
* the team.
|
||||||
|
* @param team The team for which to search for a student in.
|
||||||
|
* @param column The table column.
|
||||||
|
* @return The student detail name in a particular column, or null if none exists.
|
||||||
|
*/
|
||||||
|
private String getMemberInColumn(StudentTeam team, int column) {
|
||||||
|
Student[] students = team.getStudents();
|
||||||
|
int index = column - this.staticColumns.length; // Subtract the number of static staticColumns.
|
||||||
|
if (index >= 0 && index < students.length) {
|
||||||
|
return students[index].getDetailName();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the highest member count in the list of student teams.
|
||||||
|
* @return The maximum member count of all teams.
|
||||||
|
*/
|
||||||
|
private int getMaxMemberCount() {
|
||||||
|
int max = 0;
|
||||||
|
for (StudentTeam team : this.studentTeamsList) {
|
||||||
|
if (team.memberCount() > max) {
|
||||||
|
max = team.memberCount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates column names, including some procedurally generated headers based on the number of members in the team.
|
||||||
|
* @param maxMembers The highest number of members a team has.
|
||||||
|
*/
|
||||||
|
private void generateColumnNames(int maxMembers) {
|
||||||
|
this.columns = new String[this.staticColumns.length + maxMembers];
|
||||||
|
this.columns[0] = this.staticColumns[0];
|
||||||
|
this.columns[1] = this.staticColumns[1];
|
||||||
|
this.columns[2] = this.staticColumns[2];
|
||||||
|
for (int i = 0; i < maxMembers; i++) {
|
||||||
|
this.columns[i + this.staticColumns.length] = "Member " + (i + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,7 +1,10 @@
|
||||||
package nl.andrewlalis.util;
|
package nl.andrewlalis.util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.logging.*;
|
import java.util.logging.FileHandler;
|
||||||
|
import java.util.logging.Level;
|
||||||
|
import java.util.logging.Logger;
|
||||||
|
import java.util.logging.SimpleFormatter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Responsible for creating logs to standard output and writing to files.
|
* Responsible for creating logs to standard output and writing to files.
|
||||||
|
@ -25,7 +28,7 @@ public class Logging {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.setLevel(Level.ALL);
|
logger.setLevel(Level.FINEST);
|
||||||
Logger.getLogger("").setLevel(Level.OFF);
|
Logger.getLogger("").setLevel(Level.OFF);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package nl.andrewlalis.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A pair of objects.
|
||||||
|
* @param <T1> The first object.
|
||||||
|
* @param <T2> The second object.
|
||||||
|
*/
|
||||||
|
public class Pair<T1, T2> {
|
||||||
|
|
||||||
|
private T1 first;
|
||||||
|
private T2 second;
|
||||||
|
|
||||||
|
public Pair(T1 first, T2 second) {
|
||||||
|
this.first = first;
|
||||||
|
this.second = second;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T1 getFirst() {
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T2 getSecond() {
|
||||||
|
return second;
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,9 +2,6 @@ package nl.andrewlalis.util;
|
||||||
|
|
||||||
import nl.andrewlalis.model.Student;
|
import nl.andrewlalis.model.Student;
|
||||||
import nl.andrewlalis.model.StudentTeam;
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
import nl.andrewlalis.model.error.Error;
|
|
||||||
import nl.andrewlalis.model.error.Severity;
|
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
|
||||||
import org.apache.commons.csv.CSVFormat;
|
import org.apache.commons.csv.CSVFormat;
|
||||||
import org.apache.commons.csv.CSVRecord;
|
import org.apache.commons.csv.CSVRecord;
|
||||||
|
|
||||||
|
@ -45,9 +42,9 @@ public class TeamGenerator {
|
||||||
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(new FileReader(filename));
|
Iterable<CSVRecord> records = CSVFormat.DEFAULT.withFirstRecordAsHeader().parse(new FileReader(filename));
|
||||||
|
|
||||||
logger.fine("Reading all records into map.");
|
logger.fine("Reading all records into map.");
|
||||||
Map<Integer, Student> studentMap;
|
List<Student> students;
|
||||||
try {
|
try {
|
||||||
studentMap = readAllStudents(records, teamSize);
|
students = readAllStudents(records, teamSize);
|
||||||
} catch (ArrayIndexOutOfBoundsException e) {
|
} catch (ArrayIndexOutOfBoundsException e) {
|
||||||
logger.severe("StudentTeam 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.");
|
throw new IllegalArgumentException("StudentTeam size does not match column count in records.");
|
||||||
|
@ -55,7 +52,7 @@ public class TeamGenerator {
|
||||||
|
|
||||||
|
|
||||||
logger.fine("Generating all valid teams from student map.");
|
logger.fine("Generating all valid teams from student map.");
|
||||||
return generateAllValidTeams(studentMap, teamSize);
|
return generateAllValidTeams(students, teamSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -71,35 +68,39 @@ public class TeamGenerator {
|
||||||
* After all students with preferred partners are placed in teams, the single students are merged, and their teams
|
* After all students with preferred partners are placed in teams, the single students are merged, and their teams
|
||||||
* are added afterwards.
|
* are added afterwards.
|
||||||
*
|
*
|
||||||
* @param studentMap A mapping for each student to their student number.
|
* @param students A list of students, each with a list of preferred partners.
|
||||||
* @param teamSize The preferred maximum size for a team.
|
* @param teamSize The preferred maximum size for a team.
|
||||||
* @return A list of teams, most of which are of teamSize size.
|
* @return A list of teams, most of which are of teamSize size.
|
||||||
*/
|
*/
|
||||||
private static List<StudentTeam> generateAllValidTeams(Map<Integer, Student> studentMap, int teamSize) {
|
private static List<StudentTeam> generateAllValidTeams(List<Student> students, int teamSize) {
|
||||||
List<Student> singleStudents = new ArrayList<>(studentMap.values());
|
List<Student> singleStudents = new ArrayList<>(students);
|
||||||
List<StudentTeam> studentTeams = new ArrayList<>();
|
List<StudentTeam> studentTeams = new ArrayList<>();
|
||||||
|
|
||||||
|
// An integer which increments for each valid team. Used to create identifiers for each team.
|
||||||
int teamCount = 1;
|
int teamCount = 1;
|
||||||
|
|
||||||
// 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 (Student student : students) {
|
||||||
StudentTeam newTeam = e.getValue().getPreferredTeam(studentMap);
|
StudentTeam newTeam = student.getPreferredTeam();
|
||||||
logger.finest("Checking if student's preferred team is valid:\n" + newTeam);
|
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 student teams of 1, and thus not valid for any team size > 1.
|
||||||
if (newTeam.isValid(teamSize)) {
|
if (newTeam.isValid(teamSize)) {
|
||||||
// We know that the team is valid on its own, so now we check if it has members identical to any team already created.
|
// We know that the team is valid on its own, so now we check if it has members identical to any team already created.
|
||||||
boolean matchFound = false;
|
boolean matchFound = false;
|
||||||
for (StudentTeam team : studentTeams) {
|
for (StudentTeam team : studentTeams) {
|
||||||
if (newTeam.hasSameMembers(team)) {
|
if (newTeam.hasSameMembers(team)) {
|
||||||
matchFound = true;
|
matchFound = true;
|
||||||
|
logger.finest("A team was found with the same members: " + team.getId());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!matchFound) {
|
if (!matchFound) {
|
||||||
// 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.setNumber(teamCount++);
|
||||||
singleStudents.removeAll(Arrays.asList(newTeam.getStudents()));
|
|
||||||
studentTeams.add(newTeam);
|
studentTeams.add(newTeam);
|
||||||
|
singleStudents.removeAll(Arrays.asList(newTeam.getStudents()));
|
||||||
|
assignStudentsToTeam(newTeam);
|
||||||
logger.fine("Created team:\n" + newTeam);
|
logger.fine("Created team:\n" + newTeam);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -114,14 +115,14 @@ public class TeamGenerator {
|
||||||
* size as possible.
|
* size as possible.
|
||||||
* @param singleStudents A list of students who have no preferred partners.
|
* @param singleStudents A list of students who have no preferred partners.
|
||||||
* @param teamSize The preferred team size.
|
* @param teamSize The preferred team size.
|
||||||
* @param teamIndex The current number used in assigning an id to the team.
|
* @param teamIndex The current number used in assigning an number to the team.
|
||||||
* @return A list of teams comprising of single students.
|
* @return A list of teams comprising of single students.
|
||||||
*/
|
*/
|
||||||
private static List<StudentTeam> mergeSingleStudents(List<Student> singleStudents, int teamSize, int teamIndex) {
|
private static List<StudentTeam> mergeSingleStudents(List<Student> singleStudents, int teamSize, int teamIndex) {
|
||||||
List<StudentTeam> studentTeams = new ArrayList<>();
|
List<StudentTeam> studentTeams = new ArrayList<>();
|
||||||
while (!singleStudents.isEmpty()) {
|
while (!singleStudents.isEmpty()) {
|
||||||
StudentTeam t = new StudentTeam();
|
StudentTeam t = new StudentTeam();
|
||||||
t.setId(teamIndex++);
|
t.setNumber(teamIndex++);
|
||||||
logger.fine("Creating new team of single students:\n" + t);
|
logger.fine("Creating new team of single students:\n" + t);
|
||||||
while (t.memberCount() < teamSize && !singleStudents.isEmpty()) {
|
while (t.memberCount() < teamSize && !singleStudents.isEmpty()) {
|
||||||
Student s = singleStudents.remove(0);
|
Student s = singleStudents.remove(0);
|
||||||
|
@ -129,6 +130,7 @@ public class TeamGenerator {
|
||||||
t.addMember(s);
|
t.addMember(s);
|
||||||
}
|
}
|
||||||
studentTeams.add(t);
|
studentTeams.add(t);
|
||||||
|
assignStudentsToTeam(t);
|
||||||
logger.fine("Created team:\n" + t);
|
logger.fine("Created team:\n" + t);
|
||||||
}
|
}
|
||||||
return studentTeams;
|
return studentTeams;
|
||||||
|
@ -141,8 +143,10 @@ public class TeamGenerator {
|
||||||
* @return A map of all students in the file.
|
* @return A map of all students in the file.
|
||||||
* @throws ArrayIndexOutOfBoundsException if the teamSize does not work with the columns in the record.
|
* @throws ArrayIndexOutOfBoundsException if the teamSize does not work with the columns in the record.
|
||||||
*/
|
*/
|
||||||
private static Map<Integer, Student> readAllStudents(Iterable<CSVRecord> records, int teamSize) throws ArrayIndexOutOfBoundsException {
|
private static List<Student> readAllStudents(Iterable<CSVRecord> records, int teamSize) throws ArrayIndexOutOfBoundsException {
|
||||||
Map<Integer, Student> studentMap = new HashMap<>();
|
Map<Integer, Student> studentMap = new HashMap<>();
|
||||||
|
Map<Student, List<Integer>> studentPreferredIds = new HashMap<>();
|
||||||
|
// Perform the initial read of the students.
|
||||||
for (CSVRecord record : records) {
|
for (CSVRecord record : records) {
|
||||||
logger.finest("Read record: " + record);
|
logger.finest("Read record: " + record);
|
||||||
List<Integer> preferredIds = new ArrayList<>();
|
List<Integer> preferredIds = new ArrayList<>();
|
||||||
|
@ -152,21 +156,49 @@ public class TeamGenerator {
|
||||||
preferredIds.add(Integer.parseInt(record.get(columnOffset + i)));
|
preferredIds.add(Integer.parseInt(record.get(columnOffset + i)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Student s = new Student(Integer.parseInt(record.get(3)), record.get(2), record.get(1), record.get(4), preferredIds);
|
Student s = new Student();
|
||||||
|
s.setNumber(Integer.parseInt(record.get(3)));
|
||||||
|
s.setName(record.get(2));
|
||||||
|
s.setEmailAddress(record.get(1));
|
||||||
|
s.setGithubUsername(record.get(4));
|
||||||
if (studentMap.containsValue(s)) {
|
if (studentMap.containsValue(s)) {
|
||||||
logger.warning("Duplicate entry found for student: " + s + "\nOverwriting previous value.");
|
logger.warning("Duplicate entry found for student: " + s + "\nOverwriting previous value.");
|
||||||
}
|
}
|
||||||
studentMap.put(s.getNumber(), s);
|
studentMap.put(s.getNumber(), s);
|
||||||
|
studentPreferredIds.put(s, preferredIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform a safety check to ensure all preferred partners are valid students.
|
// The final list of students, with preferred partners set.
|
||||||
for (Map.Entry<Integer, Student> entry : studentMap.entrySet()) {
|
List<Student> students = new ArrayList<>();
|
||||||
// Remove any ids that don't exist in the whole list of students.
|
|
||||||
entry.getValue().getPreferredPartners().removeIf(partnerId -> !studentMap.containsKey(partnerId));
|
// Assign students to their preferred students.
|
||||||
|
for (Map.Entry<Student, List<Integer>> entry : studentPreferredIds.entrySet()) {
|
||||||
|
Student s = entry.getKey();
|
||||||
|
for (int partnerNumber : entry.getValue()) {
|
||||||
|
// Check that this preferred partner number exists.
|
||||||
|
if (studentMap.containsKey(partnerNumber)) {
|
||||||
|
s.addPreferredPartner(studentMap.get(partnerNumber));
|
||||||
|
} else {
|
||||||
|
logger.warning("Student " + s + " has invalid preferred partner.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
students.add(s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// At this point, all students are valid, and all preferred partners are valid.
|
// At this point, all students are valid, and all preferred partners are valid.
|
||||||
logger.fine("Read " + studentMap.size() + " students from records.");
|
logger.fine("Read " + students.size() + " students from records.");
|
||||||
return studentMap;
|
return students;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assigns all students in the given team to that team, such that all students then have a reference to the team
|
||||||
|
* they are in.
|
||||||
|
* @param team The team to assign students for.
|
||||||
|
*/
|
||||||
|
private static void assignStudentsToTeam(StudentTeam team) {
|
||||||
|
for (Student student : team.getStudents()) {
|
||||||
|
student.assignToTeam(team);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<!DOCTYPE hibernate-configuration PUBLIC
|
||||||
|
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
|
||||||
|
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
|
||||||
|
<hibernate-configuration>
|
||||||
|
<session-factory>
|
||||||
|
<!-- Database connection settings -->
|
||||||
|
<property name="hibernate.connection.driver_class">org.h2.Driver</property>
|
||||||
|
<property name="hibernate.connection.url">jdbc:h2:./initializer.h2</property>
|
||||||
|
<property name="hibernate.dialect">org.hibernate.dialect.H2Dialect</property>
|
||||||
|
<property name="hibernate.connection.username">root</property>
|
||||||
|
<property name="hibernate.connection.password">root</property>
|
||||||
|
|
||||||
|
<!-- JDBC connection pool -->
|
||||||
|
<property name="connection.pool_size">1</property>
|
||||||
|
|
||||||
|
<!-- Debugging to show sql queries. -->
|
||||||
|
<property name="show_sql">false</property>
|
||||||
|
|
||||||
|
<!-- Set Hibernate to create tables beforehand. -->
|
||||||
|
<property name="hibernate.hbm2ddl.auto">create-drop</property>
|
||||||
|
|
||||||
|
<!-- Mapping -->
|
||||||
|
<mapping class="nl.andrewlalis.model.Team"/>
|
||||||
|
<mapping class="nl.andrewlalis.model.Student"/>
|
||||||
|
<mapping class="nl.andrewlalis.model.StudentTeam"/>
|
||||||
|
<mapping class="nl.andrewlalis.model.TeachingAssistant"/>
|
||||||
|
<mapping class="nl.andrewlalis.model.TATeam"/>
|
||||||
|
</session-factory>
|
||||||
|
</hibernate-configuration>
|
|
@ -1,19 +0,0 @@
|
||||||
INSERT INTO person_types (id, name)
|
|
||||||
VALUES (0, 'student'),
|
|
||||||
(1, 'teaching-assistant'),
|
|
||||||
(2, 'professor');
|
|
||||||
|
|
||||||
INSERT INTO team_types (id, name)
|
|
||||||
VALUES (0, 'student_team'),
|
|
||||||
(1, 'teaching_assistant_team'),
|
|
||||||
(2, 'all_teaching_assistants'),
|
|
||||||
(3, 'none');
|
|
||||||
|
|
||||||
INSERT INTO teams (id, team_type_id)
|
|
||||||
VALUES (1000000, 3), -- None team for all students or TA's without a team.
|
|
||||||
(1000001, 2); -- Team for all teaching assistants.
|
|
||||||
|
|
||||||
INSERT INTO error_types (id, name)
|
|
||||||
VALUES (0, 'team_error'),
|
|
||||||
(1, 'person_error'),
|
|
||||||
(2, 'system_error');
|
|
|
@ -1,153 +0,0 @@
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
|
||||||
team_id INTEGER NULL,
|
|
||||||
FOREIGN KEY (person_type_id)
|
|
||||||
REFERENCES person_types(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE,
|
|
||||||
FOREIGN KEY (team_id)
|
|
||||||
REFERENCES teams(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Team tables for all types of teams.
|
|
||||||
CREATE TABLE IF NOT EXISTS team_types (
|
|
||||||
id INTEGER PRIMARY KEY,
|
|
||||||
name TEXT NOT NULL UNIQUE
|
|
||||||
);
|
|
||||||
|
|
||||||
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
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS team_members (
|
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
||||||
team_id INTEGER NOT NULL,
|
|
||||||
person_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (team_id)
|
|
||||||
REFERENCES teams(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE,
|
|
||||||
FOREIGN KEY (person_id)
|
|
||||||
REFERENCES persons(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE,
|
|
||||||
UNIQUE (team_id, person_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
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,
|
|
||||||
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
|
|
||||||
);
|
|
||||||
|
|
||||||
-- Student-specific tables.
|
|
||||||
CREATE TABLE IF NOT EXISTS students (
|
|
||||||
person_id INTEGER PRIMARY KEY,
|
|
||||||
chose_partner INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (person_id)
|
|
||||||
REFERENCES persons(id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS student_preferred_partners (
|
|
||||||
student_id INTEGER PRIMARY KEY,
|
|
||||||
partner_id INTEGER NOT NULL,
|
|
||||||
FOREIGN KEY (student_id)
|
|
||||||
REFERENCES students(person_id)
|
|
||||||
ON DELETE CASCADE
|
|
||||||
ON UPDATE CASCADE,
|
|
||||||
UNIQUE (student_id, partner_id)
|
|
||||||
);
|
|
||||||
|
|
||||||
-- TeachingAssistant-specific tables.
|
|
||||||
CREATE TABLE IF NOT EXISTS teaching_assistants (
|
|
||||||
person_id INTEGER PRIMARY KEY,
|
|
||||||
FOREIGN KEY (person_id)
|
|
||||||
REFERENCES persons(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
|
|
||||||
);
|
|
||||||
|
|
||||||
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