From b939094b9839abc834c4f744f2dc0bf5323edc7f Mon Sep 17 00:00:00 2001 From: andrewlalis Date: Mon, 15 Oct 2018 13:57:35 +0200 Subject: [PATCH] Added beginning of new view framework, and first three views for new workflow. Also added design for new workflow, and made many changes to existing Github Manager. --- design/SystemDiagram.odg | Bin 0 -> 15806 bytes design/SystemDiagram.pdf | Bin 0 -> 18820 bytes src/main/java/nl/andrewlalis/Main.java | 25 +-- .../control => }/command/CommandExecutor.java | 4 +- .../{ui/control => }/command/Executable.java | 2 +- .../command/executables/ArchiveRepos.java | 2 +- .../command/executables/DefineTaTeams.java | 4 +- .../executables/DelegateStudentTeams.java | 2 +- .../command/executables/DeleteRepos.java | 2 +- .../executables/ExecutableContext.java | 4 +- .../executables/GenerateAssignmentsRepo.java | 2 +- .../command/executables/GithubExecutable.java | 4 +- .../command/executables/ListErrors.java | 4 +- .../command/executables/ListRepos.java | 2 +- .../command/executables/ReadStudentsFile.java | 4 +- .../executables/SetupStudentRepos.java | 2 +- .../nl/andrewlalis/git_api/GithubManager.java | 163 +++++++++++++----- .../nl/andrewlalis/model/DatabaseObject.java | 9 - .../control/listeners/ArchiveAllListener.java | 2 +- .../listeners/CommandFieldKeyListener.java | 2 +- .../listeners/DefineTaTeamsListener.java | 2 +- .../DelegateStudentTeamsListener.java | 2 +- .../listeners/DeleteReposListener.java | 2 +- .../control/listeners/ExecutableListener.java | 2 +- .../GenerateAssignmentsRepoListener.java | 2 +- .../listeners/ReadStudentsFileListener.java | 2 +- .../control/listeners/ViewChangeListener.java | 68 ++++++++ .../create_assignments_view/NextListener.java | 59 +++++++ .../CreateAssignmentsRepoListener.java | 27 +++ .../nl/andrewlalis/ui/view/AbstractView.java | 72 ++++++++ .../ui/view/CreateAssignmentsView.java | 45 +++++ .../andrewlalis/ui/view/InitializerApp.java | 7 +- .../ui/view/InputStudentsFileView.java | 33 ++++ .../nl/andrewlalis/ui/view/StartView.java | 63 +++++++ src/main/resources/sql/insert/types.sql | 19 -- src/main/resources/sql/table_init.sql | 153 ---------------- 36 files changed, 527 insertions(+), 270 deletions(-) create mode 100644 design/SystemDiagram.odg create mode 100644 design/SystemDiagram.pdf rename src/main/java/nl/andrewlalis/{ui/control => }/command/CommandExecutor.java (96%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/Executable.java (90%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/ArchiveRepos.java (90%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/DefineTaTeams.java (94%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/DelegateStudentTeams.java (98%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/DeleteRepos.java (87%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/ExecutableContext.java (88%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/GenerateAssignmentsRepo.java (93%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/GithubExecutable.java (92%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/ListErrors.java (91%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/ListRepos.java (96%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/ReadStudentsFile.java (91%) rename src/main/java/nl/andrewlalis/{ui/control => }/command/executables/SetupStudentRepos.java (95%) delete mode 100644 src/main/java/nl/andrewlalis/model/DatabaseObject.java create mode 100644 src/main/java/nl/andrewlalis/ui/control/listeners/ViewChangeListener.java create mode 100644 src/main/java/nl/andrewlalis/ui/control/listeners/create_assignments_view/NextListener.java create mode 100644 src/main/java/nl/andrewlalis/ui/control/listeners/start_view/CreateAssignmentsRepoListener.java create mode 100644 src/main/java/nl/andrewlalis/ui/view/AbstractView.java create mode 100644 src/main/java/nl/andrewlalis/ui/view/CreateAssignmentsView.java create mode 100644 src/main/java/nl/andrewlalis/ui/view/InputStudentsFileView.java create mode 100644 src/main/java/nl/andrewlalis/ui/view/StartView.java delete mode 100644 src/main/resources/sql/insert/types.sql delete mode 100644 src/main/resources/sql/table_init.sql diff --git a/design/SystemDiagram.odg b/design/SystemDiagram.odg new file mode 100644 index 0000000000000000000000000000000000000000..f857babc6ea08fd8c66288e2572874850fb78319 GIT binary patch literal 15806 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 bll|X>Ryj#9@ZZ%CJ~q~m2<^b}``!No1~71c literal 0 HcmV?d00001 diff --git a/design/SystemDiagram.pdf b/design/SystemDiagram.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f2b342c2718af0fc76488e282bf2227a34535a8b GIT binary patch literal 18820 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 QhZjOnS7Y&bw$qIL0oIVS 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