Added dialog to delegate a certain number of teams to each TA team.
This commit is contained in:
parent
a44814b1df
commit
7c6f2527c0
|
@ -43,6 +43,7 @@ public class Main {
|
||||||
executor.registerCommand("define_ta_teams", new DefineTaTeams(app));
|
executor.registerCommand("define_ta_teams", new DefineTaTeams(app));
|
||||||
executor.registerCommand("list_errors", new ListErrors());
|
executor.registerCommand("list_errors", new ListErrors());
|
||||||
executor.registerCommand("delete_repos", new DeleteRepos());
|
executor.registerCommand("delete_repos", new DeleteRepos());
|
||||||
|
executor.registerCommand("delegate_student_teams", new DelegateStudentTeams(app));
|
||||||
|
|
||||||
logger.info("GithubManager for Github Repositories in Educational Organizations.\n" +
|
logger.info("GithubManager for Github Repositories in Educational Organizations.\n" +
|
||||||
"© Andrew Lalis (2018), All rights reserved.\n" +
|
"© Andrew Lalis (2018), All rights reserved.\n" +
|
||||||
|
|
|
@ -19,6 +19,13 @@ public class Organization extends Observable {
|
||||||
*/
|
*/
|
||||||
private List<StudentTeam> studentTeams;
|
private List<StudentTeam> studentTeams;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all teaching assistant teams in this organization. These are generated from requests to the Github API
|
||||||
|
* and possibly supplementary information. Each teaching assistant team maintains a list of all student teams for
|
||||||
|
* which it is responsible.
|
||||||
|
*/
|
||||||
|
private List<TATeam> taTeams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A queue of errors that accumulates as the program runs. These will be output to the user after execution of
|
* A queue of errors that accumulates as the program runs. These will be output to the user after execution of
|
||||||
* critical sections, so that inevitable errors due to input imperfections are not overlooked.
|
* critical sections, so that inevitable errors due to input imperfections are not overlooked.
|
||||||
|
@ -30,6 +37,7 @@ public class Organization extends Observable {
|
||||||
*/
|
*/
|
||||||
public Organization() {
|
public Organization() {
|
||||||
this.studentTeams = new ArrayList<>();
|
this.studentTeams = new ArrayList<>();
|
||||||
|
this.taTeams = new ArrayList<>();
|
||||||
this.errors = new ArrayList<>();
|
this.errors = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,22 +49,6 @@ public class Organization extends Observable {
|
||||||
return this.studentTeams.isEmpty();
|
return this.studentTeams.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
// GETTERS
|
|
||||||
public List<StudentTeam> getStudentTeams() {
|
|
||||||
return this.studentTeams;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Error> getErrors() {
|
|
||||||
return this.errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
// SETTERS
|
|
||||||
public void setStudentTeams(List<StudentTeam> teams) {
|
|
||||||
this.studentTeams = teams;
|
|
||||||
this.setChanged();
|
|
||||||
this.notifyObservers();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds an error to the list of accumulated errors.
|
* Adds an error to the list of accumulated errors.
|
||||||
* @param newError The newly generated error to add.
|
* @param newError The newly generated error to add.
|
||||||
|
@ -67,4 +59,30 @@ public class Organization extends Observable {
|
||||||
this.notifyObservers();
|
this.notifyObservers();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GETTERS
|
||||||
|
public List<StudentTeam> getStudentTeams() {
|
||||||
|
return this.studentTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Error> getErrors() {
|
||||||
|
return this.errors;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TATeam> getTaTeams() {
|
||||||
|
return this.taTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETTERS
|
||||||
|
public void setStudentTeams(List<StudentTeam> teams) {
|
||||||
|
this.studentTeams = teams;
|
||||||
|
this.setChanged();
|
||||||
|
this.notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTaTeams(List<TATeam> teams) {
|
||||||
|
this.taTeams = teams;
|
||||||
|
this.setChanged();
|
||||||
|
this.notifyObservers();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,6 +22,11 @@ public class TATeam extends Team {
|
||||||
*/
|
*/
|
||||||
private GHTeam githubTeam;
|
private GHTeam githubTeam;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of all student teams for which this TA team is responsible.
|
||||||
|
*/
|
||||||
|
private List<StudentTeam> studentTeams;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructs a team without any teaching assistant members.
|
* Constructs a team without any teaching assistant members.
|
||||||
* @param name The name of the team.
|
* @param name The name of the team.
|
||||||
|
@ -40,6 +45,15 @@ public class TATeam extends Team {
|
||||||
return Arrays.copyOf(this.getMembers(), this.memberCount(), TeachingAssistant[].class);
|
return Arrays.copyOf(this.getMembers(), this.memberCount(), TeachingAssistant[].class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the given student team to the list of teams that this TA team is responsible for.
|
||||||
|
* @param team A student team.
|
||||||
|
*/
|
||||||
|
public void addStudentTeam(StudentTeam team) {
|
||||||
|
this.studentTeams.add(team);
|
||||||
|
}
|
||||||
|
|
||||||
|
// GETTERS
|
||||||
public String getName() {
|
public String getName() {
|
||||||
return this.name;
|
return this.name;
|
||||||
}
|
}
|
||||||
|
@ -48,6 +62,11 @@ public class TATeam extends Team {
|
||||||
return this.githubTeam;
|
return this.githubTeam;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<StudentTeam> getStudentTeams() {
|
||||||
|
return this.studentTeams;
|
||||||
|
}
|
||||||
|
|
||||||
|
// SETTERS
|
||||||
public void setGithubTeam(GHTeam team) {
|
public void setGithubTeam(GHTeam team) {
|
||||||
this.githubTeam = team;
|
this.githubTeam = team;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.ui.control.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
import nl.andrewlalis.ui.view.DefineTaTeamsDialog;
|
import nl.andrewlalis.ui.view.dialogs.DefineTaTeamsDialog;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
package nl.andrewlalis.ui.control.command.executables;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
import nl.andrewlalis.ui.view.dialogs.DelegateStudentTeamsDialog;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An executable which opens up a dialog to allow a user to delegate how many student teams each TATeam gets to manage,
|
||||||
|
* and actually generate the student repositories using the manager.
|
||||||
|
*/
|
||||||
|
public class DelegateStudentTeams extends GithubExecutable {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A reference to the main application frame, used as the parent for the dialog which is shown.
|
||||||
|
*/
|
||||||
|
private InitializerApp app;
|
||||||
|
|
||||||
|
public DelegateStudentTeams(InitializerApp app) {
|
||||||
|
this.app = app;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean executeWithManager(GithubManager manager, String[] args) {
|
||||||
|
DelegateStudentTeamsDialog dialog = new DelegateStudentTeamsDialog(this.app, manager);
|
||||||
|
dialog.begin();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,16 +0,0 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An executable which opens up a dialog to allow a user to delegate how many student teams each TATeam gets to manage,
|
|
||||||
* and actually generate the student repositories using the manager.
|
|
||||||
*/
|
|
||||||
public class GenerateStudentRepos extends GithubExecutable {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected boolean executeWithManager(GithubManager manager, String[] args) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,9 +1,6 @@
|
||||||
package nl.andrewlalis.ui.control.command.executables;
|
package nl.andrewlalis.ui.control.command.executables;
|
||||||
|
|
||||||
import nl.andrewlalis.model.Organization;
|
|
||||||
import nl.andrewlalis.model.StudentTeam;
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
import nl.andrewlalis.model.error.Error;
|
|
||||||
import nl.andrewlalis.model.error.Severity;
|
|
||||||
import nl.andrewlalis.ui.control.command.Executable;
|
import nl.andrewlalis.ui.control.command.Executable;
|
||||||
import nl.andrewlalis.ui.view.InitializerApp;
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
import nl.andrewlalis.util.FileUtils;
|
import nl.andrewlalis.util.FileUtils;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
package nl.andrewlalis.ui.control.listeners;
|
||||||
|
|
||||||
|
import nl.andrewlalis.ui.control.command.CommandExecutor;
|
||||||
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Listens for when a user performs an action to open the dialog to delegate student teams amongst the TA teams.
|
||||||
|
*/
|
||||||
|
public class DelegateStudentTeamsListener extends ExecutableListener {
|
||||||
|
|
||||||
|
public DelegateStudentTeamsListener(CommandExecutor executor, InitializerApp app) {
|
||||||
|
super(executor, app);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent actionEvent) {
|
||||||
|
this.executor.executeCommand("delegate_student_teams", new String[]{
|
||||||
|
app.getOrganizationName(),
|
||||||
|
app.getAccessToken()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -121,6 +121,7 @@ public class InitializerApp extends JFrame {
|
||||||
|
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Archive All", new ArchiveAllListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Archive All", new ArchiveAllListener(this.executor, this)));
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Read Students File", new ReadStudentsFileListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Read Students File", new ReadStudentsFileListener(this.executor, this)));
|
||||||
|
commonActionsPanel.add(this.generateButtonPanel("Delegate Student Teams", new DelegateStudentTeamsListener(this.executor, this)));
|
||||||
commonActionsPanel.add(this.generateButtonPanel("Generate Assignments Repo", new GenerateAssignmentsRepoListener(this.executor, this)));
|
commonActionsPanel.add(this.generateButtonPanel("Generate Assignments Repo", new GenerateAssignmentsRepoListener(this.executor, this)));
|
||||||
|
|
||||||
// TODO: Enable this once the define teams dialog is complete.
|
// TODO: Enable this once the define teams dialog is complete.
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package nl.andrewlalis.ui.view;
|
package nl.andrewlalis.ui.view.dialogs;
|
||||||
|
|
||||||
import nl.andrewlalis.git_api.GithubManager;
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
import nl.andrewlalis.ui.view.list_models.TATeamListCellRenderer;
|
import nl.andrewlalis.ui.view.list_models.TATeamListCellRenderer;
|
||||||
import nl.andrewlalis.ui.view.list_models.TATeamListModel;
|
import nl.andrewlalis.ui.view.list_models.TATeamListModel;
|
||||||
import nl.andrewlalis.ui.view.list_models.TeachingAssistantListCellRenderer;
|
import nl.andrewlalis.ui.view.list_models.TeachingAssistantListCellRenderer;
|
|
@ -0,0 +1,130 @@
|
||||||
|
package nl.andrewlalis.ui.view.dialogs;
|
||||||
|
|
||||||
|
import nl.andrewlalis.git_api.GithubManager;
|
||||||
|
import nl.andrewlalis.model.StudentTeam;
|
||||||
|
import nl.andrewlalis.model.TATeam;
|
||||||
|
import nl.andrewlalis.ui.view.InitializerApp;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
import javax.swing.event.ChangeEvent;
|
||||||
|
import javax.swing.event.ChangeListener;
|
||||||
|
import java.awt.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This dialog shows a simple list of all teaching assistant teams, with a JSpinner next to the name, so that a user can
|
||||||
|
* set how many student teams each teaching assistant team would like to manage.
|
||||||
|
*/
|
||||||
|
public class DelegateStudentTeamsDialog extends JDialog {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The manager used to generate repositories.
|
||||||
|
*/
|
||||||
|
private GithubManager manager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Indicates how many student teams do not have an assigned team.
|
||||||
|
*/
|
||||||
|
private NumberIndicatorField unmatchedStudentsCounter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A list of JSpinner objects linked to specific team members.
|
||||||
|
*/
|
||||||
|
private List<TaTeamSpinner> teamSpinners;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A number indicating the total number of student teams.
|
||||||
|
*/
|
||||||
|
private final int totalStudentTeamsCount;
|
||||||
|
|
||||||
|
public DelegateStudentTeamsDialog(InitializerApp parentApp, GithubManager manager) {
|
||||||
|
super(parentApp, "Delegate Student Teams", true);
|
||||||
|
this.manager = manager;
|
||||||
|
this.teamSpinners = new ArrayList<>();
|
||||||
|
this.totalStudentTeamsCount = InitializerApp.organization.getStudentTeams().size();
|
||||||
|
this.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
|
||||||
|
|
||||||
|
this.initUI();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Begins showing the dialog.
|
||||||
|
*/
|
||||||
|
public void begin() {
|
||||||
|
this.setVisible(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepares the user interface components.
|
||||||
|
*/
|
||||||
|
private void initUI() {
|
||||||
|
JPanel mainPanel = new JPanel(new BorderLayout());
|
||||||
|
mainPanel.add(this.generateTopPanel(), BorderLayout.NORTH);
|
||||||
|
mainPanel.add(this.generateSpinnersPanel(), BorderLayout.CENTER);
|
||||||
|
|
||||||
|
this.setContentPane(mainPanel);
|
||||||
|
this.pack();
|
||||||
|
this.setLocationRelativeTo(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return A JPanel containing the top-most counter of how many student teams are unassigned, and a small label.
|
||||||
|
*/
|
||||||
|
private JPanel generateTopPanel() {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
panel.add(new JLabel("Unassigned teams: "), BorderLayout.CENTER);
|
||||||
|
this.unmatchedStudentsCounter = new NumberIndicatorField(this.totalStudentTeamsCount);
|
||||||
|
panel.add(this.unmatchedStudentsCounter, BorderLayout.EAST);
|
||||||
|
panel.setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel generateSpinnersPanel() {
|
||||||
|
JPanel spinnersPanel = new JPanel();
|
||||||
|
spinnersPanel.setLayout(new BoxLayout(spinnersPanel, BoxLayout.PAGE_AXIS));
|
||||||
|
List<TATeam> taTeams = this.manager.getTeams();
|
||||||
|
for (TATeam team : taTeams) {
|
||||||
|
spinnersPanel.add(this.generateTeamSpinnerPanel(team));
|
||||||
|
}
|
||||||
|
return spinnersPanel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JPanel generateTeamSpinnerPanel(TATeam team) {
|
||||||
|
JPanel panel = new JPanel(new BorderLayout());
|
||||||
|
JLabel teamLabel = new JLabel(team.getName());
|
||||||
|
teamLabel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
|
||||||
|
panel.add(teamLabel, BorderLayout.WEST);
|
||||||
|
TaTeamSpinner spinner = new TaTeamSpinner(team, this.totalStudentTeamsCount);
|
||||||
|
spinner.addChangeListener(changeEvent -> {
|
||||||
|
JSpinner s = (JSpinner) changeEvent.getSource();
|
||||||
|
int studentTeamsMatched = 0;
|
||||||
|
for (TaTeamSpinner teamSpinner : this.teamSpinners) {
|
||||||
|
studentTeamsMatched += (int)teamSpinner.getValue();
|
||||||
|
}
|
||||||
|
if (this.totalStudentTeamsCount - studentTeamsMatched < 0) {
|
||||||
|
s.setValue((int)s.getValue() + 1); // TODO: FIX! Causes stack overflow.
|
||||||
|
} else {
|
||||||
|
this.unmatchedStudentsCounter.setValue(this.totalStudentTeamsCount - studentTeamsMatched);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.teamSpinners.add(spinner);
|
||||||
|
panel.add(spinner, BorderLayout.EAST);
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a map containing an integer value for every TA team, which represents the number of student teams they will
|
||||||
|
* be responsible for.
|
||||||
|
* @return A map of TATeams and integer values.
|
||||||
|
*/
|
||||||
|
public Map<TATeam, Integer> getResult() {
|
||||||
|
Map<TATeam, Integer> results = new HashMap<>();
|
||||||
|
for (TaTeamSpinner spinner : this.teamSpinners) {
|
||||||
|
results.put(spinner.getTeam(), (int) spinner.getValue());
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
package nl.andrewlalis.ui.view.dialogs;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A custom JTextField which displays an integer value, and can be set and read more efficiently than if one were to
|
||||||
|
* parse back and forth between strings and integers.
|
||||||
|
*/
|
||||||
|
public class NumberIndicatorField extends JTextField {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The value currently displayed in the text field.
|
||||||
|
*/
|
||||||
|
private int value;
|
||||||
|
|
||||||
|
public NumberIndicatorField(int initialValue) {
|
||||||
|
this.setEditable(false);
|
||||||
|
this.setValue(initialValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the displayed value to a new integer value.
|
||||||
|
* @param newValue The new value to set.
|
||||||
|
*/
|
||||||
|
public void setValue(int newValue) {
|
||||||
|
this.value = newValue;
|
||||||
|
this.setText(String.valueOf(newValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decrement() {
|
||||||
|
this.setValue(this.value - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void increment() {
|
||||||
|
this.setValue(this.value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getValue() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
package nl.andrewlalis.ui.view.dialogs;
|
||||||
|
|
||||||
|
import nl.andrewlalis.model.TATeam;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A modified JSpinner which keeps track of a TATeam, and also has a default spinner number model in use.
|
||||||
|
*/
|
||||||
|
public class TaTeamSpinner extends JSpinner {
|
||||||
|
|
||||||
|
private TATeam team;
|
||||||
|
|
||||||
|
public TaTeamSpinner(TATeam team, int max) {
|
||||||
|
super(new SpinnerNumberModel(0, 0, max, 1));
|
||||||
|
((DefaultEditor) this.getEditor()).getTextField().setEditable(false);
|
||||||
|
this.team = team;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TATeam getTeam() {
|
||||||
|
return this.team;
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue