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.
This commit is contained in:
Andrew Lalis 2018-10-15 13:57:35 +02:00
parent 6e9ebcad74
commit b939094b98
36 changed files with 527 additions and 270 deletions

BIN
design/SystemDiagram.odg Normal file

Binary file not shown.

BIN
design/SystemDiagram.pdf Normal file

Binary file not shown.

View File

@ -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();
}
}

View File

@ -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;

View File

@ -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

View File

@ -1,4 +1,4 @@
package nl.andrewlalis.ui.control.command.executables;
package nl.andrewlalis.command.executables;
import nl.andrewlalis.git_api.GithubManager;

View File

@ -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

View File

@ -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;

View File

@ -1,4 +1,4 @@
package nl.andrewlalis.ui.control.command.executables;
package nl.andrewlalis.command.executables;
import nl.andrewlalis.git_api.GithubManager;

View File

@ -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

View File

@ -1,4 +1,4 @@
package nl.andrewlalis.ui.control.command.executables;
package nl.andrewlalis.command.executables;
import nl.andrewlalis.git_api.GithubManager;

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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<GHRepository> listReposWithPrefix(String substring) {
List<GHRepository> repos = new ArrayList<>();
try {
List<GHRepository> allRepos = this.organization.listRepositories().asList();
List<GHRepository> 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<TATeam> teams = new ArrayList<>();
try {
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());
team.setGithubTeam(entry.getValue());
for (GHUser user : entry.getValue().listMembers().asList()) {
@ -119,7 +188,7 @@ public class GithubManager {
public List<TeachingAssistant> getMembers() {
List<TeachingAssistant> 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<GHRepository> 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<GHRepository> 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<GHRepository> repositories = this.organization.listRepositories().asList();
for (GHRepository repo : repositories) {
if (repo.getName().contains(sub)) {
archiveRepository(repo);
try {
List<GHRepository> 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;
}
}

View File

@ -1,9 +0,0 @@
package nl.andrewlalis.model;
public abstract class DatabaseObject {
public abstract DatabaseObject retrieve();
public abstract boolean store();
}

View File

@ -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.*;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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.*;

View File

@ -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;

View File

@ -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.*;

View File

@ -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.*;

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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');

View File

@ -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
);