diff --git a/design/SystemDiagram.odg b/design/SystemDiagram.odg new file mode 100644 index 0000000..f857bab Binary files /dev/null and b/design/SystemDiagram.odg differ diff --git a/design/SystemDiagram.pdf b/design/SystemDiagram.pdf new file mode 100644 index 0000000..f2b342c Binary files /dev/null and b/design/SystemDiagram.pdf differ diff --git a/src/main/java/nl/andrewlalis/Main.java b/src/main/java/nl/andrewlalis/Main.java index a7f8af3..e7702a7 100644 --- a/src/main/java/nl/andrewlalis/Main.java +++ b/src/main/java/nl/andrewlalis/Main.java @@ -1,14 +1,12 @@ package nl.andrewlalis; -import nl.andrewlalis.model.Student; -import nl.andrewlalis.model.database.DbUtil; -import nl.andrewlalis.ui.control.command.CommandExecutor; -import nl.andrewlalis.ui.control.command.executables.*; +import nl.andrewlalis.command.CommandExecutor; +import nl.andrewlalis.command.executables.*; +import nl.andrewlalis.git_api.GithubManager; import nl.andrewlalis.ui.view.InitializerApp; +import nl.andrewlalis.ui.view.StartView; import nl.andrewlalis.util.CommandLine; import nl.andrewlalis.util.Logging; -import org.hibernate.Session; -import org.hibernate.SessionFactory; import java.util.Map; import java.util.logging.Logger; @@ -51,12 +49,15 @@ public class Main { "© Andrew Lalis (2018), All rights reserved.\n" + "Program initialized."); - SessionFactory factory = DbUtil.getSessionFactory(); - Session session = factory.openSession(); - session.beginTransaction(); - System.out.println(session.save(new Student(1, "a", "a@e.com", "git", null))); - session.getTransaction().commit(); - session.close(); + GithubManager manager = new GithubManager(); + StartView startView = new StartView(manager); + +// SessionFactory factory = DbUtil.getSessionFactory(); +// Session session = factory.openSession(); +// session.beginTransaction(); +// System.out.println(session.save(new Student(1, "a", "a@e.com", "git", null))); +// session.getTransaction().commit(); +// session.close(); } } diff --git a/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java b/src/main/java/nl/andrewlalis/command/CommandExecutor.java similarity index 96% rename from src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java rename to src/main/java/nl/andrewlalis/command/CommandExecutor.java index 4d3eaa0..f646a54 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/CommandExecutor.java +++ b/src/main/java/nl/andrewlalis/command/CommandExecutor.java @@ -1,6 +1,6 @@ -package nl.andrewlalis.ui.control.command; +package nl.andrewlalis.command; -import nl.andrewlalis.ui.control.command.executables.ExecutableContext; +import nl.andrewlalis.command.executables.ExecutableContext; import java.util.*; import java.util.logging.Logger; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/Executable.java b/src/main/java/nl/andrewlalis/command/Executable.java similarity index 90% rename from src/main/java/nl/andrewlalis/ui/control/command/Executable.java rename to src/main/java/nl/andrewlalis/command/Executable.java index 77ed4bd..f826ba9 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/Executable.java +++ b/src/main/java/nl/andrewlalis/command/Executable.java @@ -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 diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/ArchiveRepos.java b/src/main/java/nl/andrewlalis/command/executables/ArchiveRepos.java similarity index 90% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/ArchiveRepos.java rename to src/main/java/nl/andrewlalis/command/executables/ArchiveRepos.java index ef10441..28f1e08 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/ArchiveRepos.java +++ b/src/main/java/nl/andrewlalis/command/executables/ArchiveRepos.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.ui.control.command.executables; +package nl.andrewlalis.command.executables; import nl.andrewlalis.git_api.GithubManager; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java b/src/main/java/nl/andrewlalis/command/executables/DefineTaTeams.java similarity index 94% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java rename to src/main/java/nl/andrewlalis/command/executables/DefineTaTeams.java index add587d..8cd9703 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java +++ b/src/main/java/nl/andrewlalis/command/executables/DefineTaTeams.java @@ -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.ui.view.dialogs.DefineTaTeamsDialog; 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 diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/DelegateStudentTeams.java b/src/main/java/nl/andrewlalis/command/executables/DelegateStudentTeams.java similarity index 98% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/DelegateStudentTeams.java rename to src/main/java/nl/andrewlalis/command/executables/DelegateStudentTeams.java index 69ed042..95757d0 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/DelegateStudentTeams.java +++ b/src/main/java/nl/andrewlalis/command/executables/DelegateStudentTeams.java @@ -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.model.StudentTeam; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/DeleteRepos.java b/src/main/java/nl/andrewlalis/command/executables/DeleteRepos.java similarity index 87% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/DeleteRepos.java rename to src/main/java/nl/andrewlalis/command/executables/DeleteRepos.java index d38b615..67293f5 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/DeleteRepos.java +++ b/src/main/java/nl/andrewlalis/command/executables/DeleteRepos.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.ui.control.command.executables; +package nl.andrewlalis.command.executables; import nl.andrewlalis.git_api.GithubManager; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/ExecutableContext.java b/src/main/java/nl/andrewlalis/command/executables/ExecutableContext.java similarity index 88% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/ExecutableContext.java rename to src/main/java/nl/andrewlalis/command/executables/ExecutableContext.java index fc69f2c..a2d0f36 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/ExecutableContext.java +++ b/src/main/java/nl/andrewlalis/command/executables/ExecutableContext.java @@ -1,6 +1,6 @@ -package nl.andrewlalis.ui.control.command.executables; +package nl.andrewlalis.command.executables; -import nl.andrewlalis.ui.control.command.Executable; +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 diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/GenerateAssignmentsRepo.java b/src/main/java/nl/andrewlalis/command/executables/GenerateAssignmentsRepo.java similarity index 93% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/GenerateAssignmentsRepo.java rename to src/main/java/nl/andrewlalis/command/executables/GenerateAssignmentsRepo.java index 2ccb06b..be4dec0 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/GenerateAssignmentsRepo.java +++ b/src/main/java/nl/andrewlalis/command/executables/GenerateAssignmentsRepo.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.ui.control.command.executables; +package nl.andrewlalis.command.executables; import nl.andrewlalis.git_api.GithubManager; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java b/src/main/java/nl/andrewlalis/command/executables/GithubExecutable.java similarity index 92% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java rename to src/main/java/nl/andrewlalis/command/executables/GithubExecutable.java index 1cde937..d56ef51 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java +++ b/src/main/java/nl/andrewlalis/command/executables/GithubExecutable.java @@ -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.ui.control.command.Executable; /** * Represents an executable which interacts with github, and therefore needs access to a Github diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/ListErrors.java b/src/main/java/nl/andrewlalis/command/executables/ListErrors.java similarity index 91% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/ListErrors.java rename to src/main/java/nl/andrewlalis/command/executables/ListErrors.java index b99c07a..21cdc10 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/ListErrors.java +++ b/src/main/java/nl/andrewlalis/command/executables/ListErrors.java @@ -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.ui.control.command.Executable; import nl.andrewlalis.ui.view.InitializerApp; import java.util.logging.Logger; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/ListRepos.java b/src/main/java/nl/andrewlalis/command/executables/ListRepos.java similarity index 96% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/ListRepos.java rename to src/main/java/nl/andrewlalis/command/executables/ListRepos.java index 34e5874..1ae728d 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/ListRepos.java +++ b/src/main/java/nl/andrewlalis/command/executables/ListRepos.java @@ -1,4 +1,4 @@ -package nl.andrewlalis.ui.control.command.executables; +package nl.andrewlalis.command.executables; import nl.andrewlalis.git_api.GithubManager; import org.kohsuke.github.GHRepository; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/ReadStudentsFile.java b/src/main/java/nl/andrewlalis/command/executables/ReadStudentsFile.java similarity index 91% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/ReadStudentsFile.java rename to src/main/java/nl/andrewlalis/command/executables/ReadStudentsFile.java index cb0b4a0..c89b42e 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/ReadStudentsFile.java +++ b/src/main/java/nl/andrewlalis/command/executables/ReadStudentsFile.java @@ -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.ui.control.command.Executable; import nl.andrewlalis.ui.view.InitializerApp; import nl.andrewlalis.util.FileUtils; diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/SetupStudentRepos.java b/src/main/java/nl/andrewlalis/command/executables/SetupStudentRepos.java similarity index 95% rename from src/main/java/nl/andrewlalis/ui/control/command/executables/SetupStudentRepos.java rename to src/main/java/nl/andrewlalis/command/executables/SetupStudentRepos.java index cdc4380..081bb03 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/SetupStudentRepos.java +++ b/src/main/java/nl/andrewlalis/command/executables/SetupStudentRepos.java @@ -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.model.StudentTeam; diff --git a/src/main/java/nl/andrewlalis/git_api/GithubManager.java b/src/main/java/nl/andrewlalis/git_api/GithubManager.java index 9748239..3ad9091 100644 --- a/src/main/java/nl/andrewlalis/git_api/GithubManager.java +++ b/src/main/java/nl/andrewlalis/git_api/GithubManager.java @@ -31,6 +31,7 @@ public class GithubManager { private GitHub github; private GHOrganization organization; private String accessToken; + private String organizationName; /** * The logger for outputting debug info. @@ -40,14 +41,82 @@ public class GithubManager { 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) { + this.organizationName = organizationName; 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 { - this.github = GitHub.connectUsingOAuth(accessToken); - this.organization = this.github.getOrganization(organizationName); + this.organization = this.getOrganization(); + return this.organization != null; } catch (IOException e) { - logger.severe("Unable to make a GithubManager with organization name: " + organizationName + " and access token: " + accessToken); - e.printStackTrace(); + 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; } } @@ -59,7 +128,7 @@ public class GithubManager { public List listReposWithPrefix(String substring) { List repos = new ArrayList<>(); try { - List allRepos = this.organization.listRepositories().asList(); + List allRepos = this.getOrganization().listRepositories().asList(); for (GHRepository repo : allRepos) { if (repo.getName().contains(substring)) { repos.add(repo); @@ -81,7 +150,7 @@ public class GithubManager { public GHRepository getRepository(String name) { System.out.println(name); try { - return this.organization.getRepository(name); + return this.getOrganization().getRepository(name); } catch (IOException e) { logger.severe("No repository with name: " + name + " exists."); e.printStackTrace(); @@ -97,7 +166,7 @@ public class GithubManager { List teams = new ArrayList<>(); try { Random rand = new Random(); - for (Map.Entry entry : this.organization.getTeams().entrySet()) { + for (Map.Entry entry : this.getOrganization().getTeams().entrySet()) { TATeam team = new TATeam(entry.getKey(), entry.getValue().getId()); team.setGithubTeam(entry.getValue()); for (GHUser user : entry.getValue().listMembers().asList()) { @@ -119,7 +188,7 @@ public class GithubManager { public List getMembers() { List teachingAssistants = new ArrayList<>(); 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())); } } catch (IOException e) { @@ -137,9 +206,9 @@ public class GithubManager { * @throws IOException If an HTTP request failed. */ 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. - GHRepository existingRepo = this.organization.getRepository(assignmentsRepoName); + GHRepository existingRepo = this.getOrganization().getRepository(assignmentsRepoName); if (existingRepo != null) { existingRepo.delete(); logger.fine("Deleted pre-existing assignments repository."); @@ -174,9 +243,10 @@ public class GithubManager { return; } - GHRepository repo = this.createRepository(team.generateUniqueName(prefix), taTeam.getGithubTeam(), team.generateRepoDescription(), false, true, true); - - if (repo == null) { + GHRepository repo; + try { + repo = this.createRepository(team.generateUniqueName(prefix), taTeam.getGithubTeam(), team.generateRepoDescription(), false, true, true); + } catch (IOException e) { logger.severe("Repository for student team " + team.getNumber() + " could not be created."); return; } @@ -195,17 +265,22 @@ public class GithubManager { * @param substring The substring which repository names should contain to be deleted. */ public void deleteAllRepositories(String substring) { - List repositories = this.organization.listRepositories().asList(); - for (GHRepository repo : repositories) { - if (repo.getName().contains(substring)) { - try { - repo.delete(); - logger.info("Deleted repository: " + repo.getName()); - } catch (IOException e) { - logger.severe("Could not delete repository: " + repo.getName()); - e.printStackTrace(); + try { + List repositories = this.getOrganization().listRepositories().asList(); + for (GHRepository repo : repositories) { + if (repo.getName().contains(substring)) { + try { + repo.delete(); + logger.info("Deleted repository: " + repo.getName()); + } catch (IOException e) { + logger.severe("Could not delete repository: " + repo.getName()); + e.printStackTrace(); + } } } + } catch (IOException e) { + logger.severe("Could not get Organization for listing repositories."); + e.printStackTrace(); } } @@ -214,11 +289,16 @@ public class GithubManager { * @param sub Any repository containing this substring will be archived. */ public void archiveAllRepositories(String sub) { - List repositories = this.organization.listRepositories().asList(); - for (GHRepository repo : repositories) { - if (repo.getName().contains(sub)) { - archiveRepository(repo); + try { + List repositories = this.getOrganization().listRepositories().asList(); + for (GHRepository repo : repositories) { + if (repo.getName().contains(sub)) { + archiveRepository(repo); + } } + } catch (IOException e) { + logger.severe("Could not get Organization for listing repositories."); + e.printStackTrace(); } } @@ -339,25 +419,20 @@ public class GithubManager { * @param hasWiki Whether the repo has a wiki enabled. * @param hasIssues Whether the repo has issues enabled. * @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){ - try { - GHCreateRepositoryBuilder builder = this.organization.createRepository(name); - builder.team(taTeam); - builder.wiki(hasWiki); - builder.issues(hasIssues); - builder.description(description); - builder.gitignoreTemplate("Java"); - builder.private_(isPrivate); - GHRepository repo = builder.create(); - logger.fine("Created repository: " + repo.getName()); - return repo; - } catch (IOException e) { - logger.severe("Could not create repository: " + name); - e.printStackTrace(); - return null; - } + private GHRepository createRepository(String name, GHTeam taTeam, String description, boolean hasWiki, boolean hasIssues, boolean isPrivate) throws IOException { + GHCreateRepositoryBuilder builder = this.getOrganization().createRepository(name); + builder.team(taTeam); + builder.wiki(hasWiki); + builder.issues(hasIssues); + builder.description(description); + builder.gitignoreTemplate("Java"); + builder.private_(isPrivate); + GHRepository repo = builder.create(); + logger.fine("Created repository: " + repo.getName()); + return repo; } } diff --git a/src/main/java/nl/andrewlalis/model/DatabaseObject.java b/src/main/java/nl/andrewlalis/model/DatabaseObject.java deleted file mode 100644 index 9928da2..0000000 --- a/src/main/java/nl/andrewlalis/model/DatabaseObject.java +++ /dev/null @@ -1,9 +0,0 @@ -package nl.andrewlalis.model; - -public abstract class DatabaseObject { - - public abstract DatabaseObject retrieve(); - - public abstract boolean store(); - -} diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java index 98e4b86..065cc7b 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java @@ -1,6 +1,6 @@ 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 javax.swing.*; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/CommandFieldKeyListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/CommandFieldKeyListener.java index d68ac1e..2e98919 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/CommandFieldKeyListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/CommandFieldKeyListener.java @@ -1,6 +1,6 @@ package nl.andrewlalis.ui.control.listeners; -import nl.andrewlalis.ui.control.command.CommandExecutor; +import nl.andrewlalis.command.CommandExecutor; import javax.swing.*; import java.awt.event.KeyEvent; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java index fec0429..b4f12f3 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java @@ -1,6 +1,6 @@ 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 java.awt.event.ActionEvent; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/DelegateStudentTeamsListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/DelegateStudentTeamsListener.java index b9969ea..c5b40d1 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/DelegateStudentTeamsListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/DelegateStudentTeamsListener.java @@ -1,6 +1,6 @@ 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 java.awt.event.ActionEvent; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/DeleteReposListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/DeleteReposListener.java index d615bde..5e37ea2 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/DeleteReposListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/DeleteReposListener.java @@ -1,6 +1,6 @@ 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 javax.swing.*; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/ExecutableListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/ExecutableListener.java index 33401c0..4c7f268 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ExecutableListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ExecutableListener.java @@ -1,6 +1,6 @@ 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 java.awt.event.ActionListener; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java index ba74466..f194cb0 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java @@ -1,6 +1,6 @@ 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 javax.swing.*; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java index e65e7ba..c0535d2 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java @@ -1,6 +1,6 @@ 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 javax.swing.*; diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/ViewChangeListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/ViewChangeListener.java new file mode 100644 index 0000000..4d9a519 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ViewChangeListener.java @@ -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); + } +} diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/create_assignments_view/NextListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/create_assignments_view/NextListener.java new file mode 100644 index 0000000..268cdc0 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/create_assignments_view/NextListener.java @@ -0,0 +1,59 @@ +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(); + GithubManager manager = assignmentsView.getGithubManager(); + if (manager.repoExists(repoName)) { + return true; + } else { + 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 { + assignmentsView.getGithubManager().setupAssignmentsRepo(repoName, "", 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; + } + } + } + + 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; + } +} diff --git a/src/main/java/nl/andrewlalis/ui/control/listeners/start_view/CreateAssignmentsRepoListener.java b/src/main/java/nl/andrewlalis/ui/control/listeners/start_view/CreateAssignmentsRepoListener.java new file mode 100644 index 0000000..7a53c60 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/start_view/CreateAssignmentsRepoListener.java @@ -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(); + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/AbstractView.java b/src/main/java/nl/andrewlalis/ui/view/AbstractView.java new file mode 100644 index 0000000..01e9cb7 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/AbstractView.java @@ -0,0 +1,72 @@ +package nl.andrewlalis.ui.view; + +import nl.andrewlalis.git_api.GithubManager; + +import javax.swing.*; +import java.awt.*; + +/** + * 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; + + /** + * 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. + */ + AbstractView(String title, boolean startVisible, int defaultCloseOperation, Dimension preferredSize, GithubManager githubManager) { + super(title); + this.githubManager = githubManager; + 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. + } + + 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; + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/CreateAssignmentsView.java b/src/main/java/nl/andrewlalis/ui/view/CreateAssignmentsView.java new file mode 100644 index 0000000..7fb9473 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/CreateAssignmentsView.java @@ -0,0 +1,45 @@ +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()); + this.repositoryNameField = new JTextField(); + contentPane.add(this.generateTextFieldPanel("Repository name:", this.repositoryNameField), BorderLayout.CENTER); + + JButton nextButton = new JButton("Next"); + nextButton.addActionListener(new NextListener(this, new InputStudentsFileView(this.getGithubManager()))); + contentPane.add(nextButton, BorderLayout.SOUTH); + + return contentPane; + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java index bf5b353..b21c65e 100644 --- a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java +++ b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java @@ -1,8 +1,8 @@ package nl.andrewlalis.ui.view; +import nl.andrewlalis.command.CommandExecutor; import nl.andrewlalis.model.Organization; import nl.andrewlalis.ui.control.OutputTextHandler; -import nl.andrewlalis.ui.control.command.CommandExecutor; import nl.andrewlalis.ui.control.listeners.*; 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("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))); // Extra panel to push buttons to the top. diff --git a/src/main/java/nl/andrewlalis/ui/view/InputStudentsFileView.java b/src/main/java/nl/andrewlalis/ui/view/InputStudentsFileView.java new file mode 100644 index 0000000..6c48e80 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/InputStudentsFileView.java @@ -0,0 +1,33 @@ +package nl.andrewlalis.ui.view; + +import nl.andrewlalis.git_api.GithubManager; + +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 { + + InputStudentsFileView(GithubManager manager) { + super("Input Students CSV", + false, + DISPOSE_ON_CLOSE, + null, + manager); + } + + @Override + protected JPanel buildContentPane() { + JPanel contentPane = new JPanel(new BorderLayout()); + + JButton selectFileButton = new JButton("Select File"); + contentPane.add(selectFileButton, BorderLayout.CENTER); + + JButton doneButton = new JButton("Done"); + contentPane.add(doneButton, BorderLayout.SOUTH); + + return contentPane; + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/StartView.java b/src/main/java/nl/andrewlalis/ui/view/StartView.java new file mode 100644 index 0000000..65fde1a --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/StartView.java @@ -0,0 +1,63 @@ +package nl.andrewlalis.ui.view; + +import nl.andrewlalis.git_api.GithubManager; +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); + } + + 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(); + infoInputPanel.add(this.generateTextFieldPanel("Organization name:", this.organizationNameField)); + this.accessTokenField = new JTextField(); + infoInputPanel.add(this.generateTextFieldPanel("Access token:", this.accessTokenField)); + + JPanel buttonsPanel = new JPanel(); + JButton assignmentsViewButton = new JButton("Start New Course"); + assignmentsViewButton.addActionListener(new CreateAssignmentsRepoListener(this, new CreateAssignmentsView(this.getGithubManager()))); + JButton managementViewButton = new JButton("Manage Existing Course"); + + buttonsPanel.add(assignmentsViewButton); + buttonsPanel.add(managementViewButton); + + contentPane.add(infoInputPanel, BorderLayout.CENTER); + contentPane.add(buttonsPanel, BorderLayout.SOUTH); + return contentPane; + } +} diff --git a/src/main/resources/sql/insert/types.sql b/src/main/resources/sql/insert/types.sql deleted file mode 100644 index 3940146..0000000 --- a/src/main/resources/sql/insert/types.sql +++ /dev/null @@ -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'); \ No newline at end of file diff --git a/src/main/resources/sql/table_init.sql b/src/main/resources/sql/table_init.sql deleted file mode 100644 index 0bba75d..0000000 --- a/src/main/resources/sql/table_init.sql +++ /dev/null @@ -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 -); \ No newline at end of file