diff --git a/src/main/java/nl/andrewlalis/Main.java b/src/main/java/nl/andrewlalis/Main.java index 7241c30..d036670 100644 --- a/src/main/java/nl/andrewlalis/Main.java +++ b/src/main/java/nl/andrewlalis/Main.java @@ -1,24 +1,16 @@ package nl.andrewlalis; import nl.andrewlalis.model.database.Database; -import nl.andrewlalis.git_api.GithubManager; -import nl.andrewlalis.model.Student; -import nl.andrewlalis.model.StudentTeam; import nl.andrewlalis.ui.control.command.CommandExecutor; -import nl.andrewlalis.ui.control.command.Executable; import nl.andrewlalis.ui.control.command.executables.ArchiveRepos; +import nl.andrewlalis.ui.control.command.executables.DefineTaTeams; import nl.andrewlalis.ui.control.command.executables.GenerateAssignmentsRepo; import nl.andrewlalis.ui.control.command.executables.ReadStudentsFileToDB; import nl.andrewlalis.ui.view.InitializerApp; import nl.andrewlalis.util.CommandLine; import nl.andrewlalis.util.Logging; -import nl.andrewlalis.util.TeamGenerator; -import javax.swing.*; -import java.io.IOException; -import java.util.List; import java.util.Map; -import java.util.logging.Level; import java.util.logging.Logger; /** @@ -45,11 +37,13 @@ public class Main { app.setAccessToken(userOptions.get("token")); Database db = new Database("database/initializer.sqlite"); - db.initialize(); - executor.registerCommand("readstudents", new ReadStudentsFileToDB(db)); - executor.registerCommand("archiveall", new ArchiveRepos()); - executor.registerCommand("generateassignments", new GenerateAssignmentsRepo()); + executor.registerCommand("read_students", new ReadStudentsFileToDB(db)); + executor.registerCommand("archive_all", new ArchiveRepos()); + executor.registerCommand("generate_assignments", new GenerateAssignmentsRepo()); + executor.registerCommand("define_ta_teams", new DefineTaTeams(app)); + + db.initialize(); logger.info("GithubManager for Github Repositories in Educational Organizations. Program initialized."); } diff --git a/src/main/java/nl/andrewlalis/git_api/GithubManager.java b/src/main/java/nl/andrewlalis/git_api/GithubManager.java index 5de6ec7..5829129 100644 --- a/src/main/java/nl/andrewlalis/git_api/GithubManager.java +++ b/src/main/java/nl/andrewlalis/git_api/GithubManager.java @@ -2,9 +2,7 @@ package nl.andrewlalis.git_api; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import nl.andrewlalis.model.Student; -import nl.andrewlalis.model.StudentTeam; -import nl.andrewlalis.model.TATeam; +import nl.andrewlalis.model.*; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpPatch; import org.apache.http.entity.StringEntity; @@ -15,6 +13,7 @@ import org.kohsuke.github.*; import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.logging.Logger; /** @@ -53,6 +52,45 @@ public class GithubManager { } } + /** + * Gets a list of teams in the organization. + * @return A List of all TA teams in the organization. + */ + public List getTeams() { + List teams = new ArrayList<>(); + try { + for (Map.Entry entry : this.organization.getTeams().entrySet()) { + TATeam team = new TATeam(entry.getKey(), -1); + team.setGithubTeam(entry.getValue()); + for (GHUser user : entry.getValue().getMembers()) { + team.addMember(new TeachingAssistant(-1, user.getName(), user.getEmail(), user.getLogin())); + } + teams.add(team); + } + } catch (IOException e) { + logger.severe("Could not get a list of teams in the organization.\n" + e.getMessage()); + e.printStackTrace(); + } + return teams; + } + + /** + * Gets a list of all teaching assistants, or members, in the organization. + * @return A List of teaching assistants, and empty if an error occurred. + */ + public List getMembers() { + List teachingAssistants = new ArrayList<>(); + try { + for (GHUser member : this.organization.getMembers()) { + teachingAssistants.add(new TeachingAssistant(-1, member.getName(), member.getEmail(), member.getLogin())); + } + } catch (IOException e) { + logger.severe("Could not get list of members in the organization.\n" + e.getMessage()); + e.printStackTrace(); + } + return teachingAssistants; + } + /** * Initializes the github repository for all studentTeams given. * diff --git a/src/main/java/nl/andrewlalis/model/Person.java b/src/main/java/nl/andrewlalis/model/Person.java index 66b0fb7..0c06a40 100644 --- a/src/main/java/nl/andrewlalis/model/Person.java +++ b/src/main/java/nl/andrewlalis/model/Person.java @@ -26,6 +26,17 @@ public abstract class Person { */ protected String githubUsername; + /** + * Constructs a Person from only a github username, which is, in some cases, enough to perform a lot of actions. + * @param githubUsername The person's github username. + */ + public Person(String githubUsername) { + this.number = -1; + this.name = null; + this.emailAddress = null; + this.githubUsername = githubUsername; + } + /** * Constructs a Person from all the basic information needed. * @param number Either an S- or P-Number without the letter prefix. diff --git a/src/main/java/nl/andrewlalis/model/TATeam.java b/src/main/java/nl/andrewlalis/model/TATeam.java index 2e252df..49a5bc9 100644 --- a/src/main/java/nl/andrewlalis/model/TATeam.java +++ b/src/main/java/nl/andrewlalis/model/TATeam.java @@ -1,6 +1,5 @@ package nl.andrewlalis.model; -import org.kohsuke.github.GHOrganization; import org.kohsuke.github.GHTeam; import java.util.ArrayList; @@ -10,20 +9,13 @@ import java.util.List; * Represents a teaching assistant team, which is itself a 'team' in the organization. This class is used for parsing * json from requests to github to get a list of all teams in the organization. */ -public class TATeam { - - private List teachingAssistants; +public class TATeam extends Team { /** * The team's display name. */ private String name; - /** - * The team's unique identifier. - */ - private int id; - /** * The Github team associated with this team. */ @@ -35,27 +27,8 @@ public class TATeam { * @param id The unique identifier for this team. */ public TATeam(String name, int id) { + super(id); this.name = name; - this.id = id; - this.teachingAssistants = new ArrayList(); - } - - /** - * Constructs a team with a list of teaching assistants that are part of it. - * @param teachingAssistants The list of teaching assistants that are part of the team. - */ - public TATeam(List teachingAssistants, String name, int id) { - this.teachingAssistants = teachingAssistants; - this.name = name; - this.id = id; - } - - /** - * Gets the unique identification for this TA team. - * @return An integer representing the id of this team. - */ - public int getId() { - return this.id; } public String getName() { diff --git a/src/main/java/nl/andrewlalis/model/TeachingAssistant.java b/src/main/java/nl/andrewlalis/model/TeachingAssistant.java index 918bb5c..7362c48 100644 --- a/src/main/java/nl/andrewlalis/model/TeachingAssistant.java +++ b/src/main/java/nl/andrewlalis/model/TeachingAssistant.java @@ -1,7 +1,18 @@ package nl.andrewlalis.model; +/** + * Represents an administrator in the organization, who manages student teams. + */ public class TeachingAssistant extends Person { + /** + * Constructs a Teaching Assistant from only a github username. + * @param githubUsername The person's github username. + */ + public TeachingAssistant(String githubUsername) { + super(githubUsername); + } + /** * Constructs a Teaching Assistant from all the basic information needed, much like its parent, Person. * diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java b/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java index cccd3e8..a39dd56 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java +++ b/src/main/java/nl/andrewlalis/ui/control/command/executables/DefineTaTeams.java @@ -1,13 +1,30 @@ package nl.andrewlalis.ui.control.command.executables; import nl.andrewlalis.git_api.GithubManager; +import nl.andrewlalis.ui.view.DefineTaTeamsDialog; +import nl.andrewlalis.ui.view.InitializerApp; - +/** + * This executable is slightly different from the others, in that it opens up a user interface to make editing TA teams + * possible. Therefore, executing this command opens the 'DefineTaTeams' dialog, within which a user can make changes + * to the TA teams in the organization. + */ public class DefineTaTeams extends GithubExecutable { + /** + * An instance of the main application frame; used when constructing the dialog. + */ + private InitializerApp app; + + public DefineTaTeams(InitializerApp app) { + this.app = app; + } + @Override protected boolean executeWithManager(GithubManager manager, String[] args) { - return false; + DefineTaTeamsDialog dialog = new DefineTaTeamsDialog(this.app, manager); + dialog.begin(); + return true; } } diff --git a/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java b/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java index e7362bd..1cde937 100644 --- a/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java +++ b/src/main/java/nl/andrewlalis/ui/control/command/executables/GithubExecutable.java @@ -10,6 +10,9 @@ import nl.andrewlalis.ui.control.command.Executable; * Requires two arguments: * 1. The organization name. * 2. The organization's access token. + * + * Any additional arguments are added to a new String[] array which is passed along to child classes, so that they do + * not have to filter out the mandatory first two arguments. */ public abstract class GithubExecutable implements Executable { 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 6d3cbf2..98e4b86 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ArchiveAllListener.java @@ -19,7 +19,7 @@ public class ArchiveAllListener extends ExecutableListener { public void actionPerformed(ActionEvent actionEvent) { String response = JOptionPane.showInputDialog(this.app, "Enter a substring to archive repositories by.", "Enter a substring", JOptionPane.QUESTION_MESSAGE); if (response != null) { - this.executor.executeCommand("archiveall", new String[]{ + this.executor.executeCommand("archive_all", new String[]{ this.app.getOrganizationName(), this.app.getAccessToken(), response 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 0e85be3..fec0429 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/DefineTaTeamsListener.java @@ -5,6 +5,9 @@ import nl.andrewlalis.ui.view.InitializerApp; import java.awt.event.ActionEvent; +/** + * Listens for when the user wants to open the 'DefineTaTeams' dialog. + */ public class DefineTaTeamsListener extends ExecutableListener { public DefineTaTeamsListener(CommandExecutor executor, InitializerApp app) { @@ -13,6 +16,9 @@ public class DefineTaTeamsListener extends ExecutableListener { @Override public void actionPerformed(ActionEvent actionEvent) { - + this.executor.executeCommand("define_ta_teams", new String[]{ + this.app.getOrganizationName(), + this.app.getAccessToken() + }); } } 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 6143821..ba74466 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/GenerateAssignmentsRepoListener.java @@ -22,7 +22,7 @@ public class GenerateAssignmentsRepoListener extends ExecutableListener { String description = JOptionPane.showInputDialog(this.app, "Enter a description for the repository.", "Repository Description", JOptionPane.QUESTION_MESSAGE); String teamName = JOptionPane.showInputDialog(this.app, "Enter the name of the TA team containing all teaching assistants.", "TA Team Name", JOptionPane.QUESTION_MESSAGE); if (teamName != null) { - this.executor.executeCommand("generateassignments", new String[]{ + this.executor.executeCommand("generate_assignments", new String[]{ this.app.getOrganizationName(), this.app.getAccessToken(), repoName, 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 4a270c8..046e78d 100644 --- a/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java +++ b/src/main/java/nl/andrewlalis/ui/control/listeners/ReadStudentsFileListener.java @@ -10,6 +10,9 @@ import java.io.File; /** * Listens for when the user performs an action to read all students from a file, and output the contents to a database. + * + * Because filename and team size are not provided via arguments when a user clicks on a button, these are obtained via + * a JFileChooser and JOptionPane input dialog. If all inputs are valid, then the command is executed. */ public class ReadStudentsFileListener extends ExecutableListener { @@ -40,7 +43,7 @@ public class ReadStudentsFileListener extends ExecutableListener { if (fileResponse == JFileChooser.APPROVE_OPTION) { String teamSizeString = JOptionPane.showInputDialog(this.app, "Enter the student team size.", "Team Size", JOptionPane.QUESTION_MESSAGE); if (teamSizeString != null) { - this.executor.executeCommand("readstudents", new String[]{ + this.executor.executeCommand("read_students", new String[]{ chooser.getSelectedFile().getName(), teamSizeString }); diff --git a/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java b/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java index 3a3e641..6e5faef 100644 --- a/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java +++ b/src/main/java/nl/andrewlalis/ui/view/DefineTaTeamsDialog.java @@ -1,13 +1,96 @@ package nl.andrewlalis.ui.view; -import nl.andrewlalis.ui.control.command.executables.DefineTaTeams; +import nl.andrewlalis.git_api.GithubManager; +import nl.andrewlalis.ui.view.list_models.TATeamListModel; +import nl.andrewlalis.ui.view.list_models.TeachingAssistantListCellRenderer; +import nl.andrewlalis.ui.view.list_models.TeachingAssistantsListModel; import javax.swing.*; +import java.awt.*; +/** + * This class represents a pop-up dialog that appears when the user wants to manipulate teams in the organization. With + * this dialog, it will be possible to do the following: + * - View all members of the organization. + * - View all teams in the organization. + * - Create new teams from a selection of members. + * - Invite new members to the organization. + */ public class DefineTaTeamsDialog extends JDialog { - public DefineTaTeamsDialog(InitializerApp parentApp) { - super(parentApp); + /** + * The manager used to manipulate the organization. + */ + private GithubManager manager; + + public DefineTaTeamsDialog(InitializerApp parentApp, GithubManager manager) { + super(parentApp, "Define TA Teams", true); + this.setDefaultCloseOperation(DISPOSE_ON_CLOSE); + this.manager = manager; + + this.initUI(); } + /** + * Sets the dialog as visible. + */ + public void begin() { + this.setVisible(true); + } + + /** + * Constructs all UI components. + */ + private void initUI() { + JPanel mainPanel = new JPanel(new BorderLayout()); + mainPanel.add(new JLabel("Hello world", SwingConstants.CENTER), BorderLayout.NORTH); + + mainPanel.add(this.initMembersPanel(), BorderLayout.WEST); + mainPanel.add(this.initTeamsPanel(), BorderLayout.EAST); + + this.setContentPane(mainPanel); + this.setPreferredSize(new Dimension(600, 800)); + this.pack(); + this.setLocationRelativeTo(null); + } + + /** + * @return A JPanel containing the list of teams in the organization. + */ + private JPanel initTeamsPanel() { + JPanel teamsPanel = new JPanel(new BorderLayout()); + teamsPanel.add(new JLabel("Teams", SwingConstants.CENTER), BorderLayout.NORTH); + + ListModel model = new TATeamListModel(this.manager.getTeams()); + JList teamsList = new JList(model); + teamsList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + + JScrollPane teamsListScrollPane = new JScrollPane(teamsList); + teamsListScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + teamsListScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + teamsPanel.add(teamsListScrollPane, BorderLayout.CENTER); + + return teamsPanel; + } + + /** + * @return A JPanel containing the list of members of the organization. + */ + private JPanel initMembersPanel() { + JPanel membersPanel = new JPanel(new BorderLayout()); + membersPanel.add(new JLabel("Members", SwingConstants.CENTER), BorderLayout.NORTH); + + ListModel model = new TeachingAssistantsListModel(this.manager.getMembers()); + JList membersList = new JList(); + membersList.setModel(model); + membersList.setCellRenderer(new TeachingAssistantListCellRenderer()); + membersList.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); + + JScrollPane membersListScrollPane = new JScrollPane(membersList); + membersListScrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + membersListScrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + membersPanel.add(membersListScrollPane, BorderLayout.CENTER); + + return membersPanel; + } } diff --git a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java index 526840f..c92e0f5 100644 --- a/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java +++ b/src/main/java/nl/andrewlalis/ui/view/InitializerApp.java @@ -50,7 +50,6 @@ public class InitializerApp extends JFrame { * Begins showing the application */ public void begin() { - this.pack(); this.setVisible(true); } @@ -79,6 +78,8 @@ public class InitializerApp extends JFrame { mainPanel.add(this.initGithubManagerPanel(), BorderLayout.EAST); this.setContentPane(mainPanel); + this.pack(); + this.setLocationRelativeTo(null); this.initLoggingHandler(); } diff --git a/src/main/java/nl/andrewlalis/ui/view/list_models/TATeamListModel.java b/src/main/java/nl/andrewlalis/ui/view/list_models/TATeamListModel.java new file mode 100644 index 0000000..1565f58 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/list_models/TATeamListModel.java @@ -0,0 +1,31 @@ +package nl.andrewlalis.ui.view.list_models; + +import nl.andrewlalis.model.TATeam; + +import javax.swing.*; +import java.util.List; + +/** + * A list model for displaying TATeams. + */ +public class TATeamListModel extends AbstractListModel { + + /** + * A list of teams. + */ + private List teams; + + public TATeamListModel(List taTeams) { + this.teams = taTeams; + } + + @Override + public int getSize() { + return this.teams.size(); + } + + @Override + public Object getElementAt(int i) { + return this.teams.get(i); + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantListCellRenderer.java b/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantListCellRenderer.java new file mode 100644 index 0000000..0c8b91f --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantListCellRenderer.java @@ -0,0 +1,20 @@ +package nl.andrewlalis.ui.view.list_models; + +import nl.andrewlalis.model.TeachingAssistant; + +import javax.swing.*; +import java.awt.*; + +public class TeachingAssistantListCellRenderer extends DefaultListCellRenderer { + + @Override + public Component getListCellRendererComponent(JList jList, Object o, int i, boolean b, boolean b1) { + super.getListCellRendererComponent(jList, o, i, b, b1); + if (o instanceof TeachingAssistant) { + TeachingAssistant ta = (TeachingAssistant) o; + this.setText(ta.getGithubUsername()); + this.setToolTipText(ta.getName()); + } + return this; + } +} diff --git a/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantsListModel.java b/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantsListModel.java new file mode 100644 index 0000000..2cff0c2 --- /dev/null +++ b/src/main/java/nl/andrewlalis/ui/view/list_models/TeachingAssistantsListModel.java @@ -0,0 +1,31 @@ +package nl.andrewlalis.ui.view.list_models; + +import nl.andrewlalis.model.TeachingAssistant; + +import javax.swing.*; +import java.util.List; + +/** + * A list model for displaying a list of teaching assistants as members of an organization. + */ +public class TeachingAssistantsListModel extends AbstractListModel { + + /** + * The list of teaching assistants which this model uses. + */ + private List teachingAssistants; + + public TeachingAssistantsListModel(List teachingAssistants) { + this.teachingAssistants = teachingAssistants; + } + + @Override + public int getSize() { + return this.teachingAssistants.size(); + } + + @Override + public Object getElementAt(int i) { + return this.teachingAssistants.get(i); + } +} diff --git a/src/main/resources/sql/table_init.sql b/src/main/resources/sql/table_init.sql index e5ffa1d..9f0d8f2 100644 --- a/src/main/resources/sql/table_init.sql +++ b/src/main/resources/sql/table_init.sql @@ -16,12 +16,18 @@ CREATE TABLE IF NOT EXISTS persons ( 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 + 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 @@ -33,6 +39,22 @@ CREATE TABLE IF NOT EXISTS teams ( 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, + 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 ( @@ -59,17 +81,13 @@ CREATE TABLE IF NOT EXISTS student_teams ( ON UPDATE CASCADE ); +-- Student-specific tables. CREATE TABLE IF NOT EXISTS students ( person_id INTEGER PRIMARY KEY, - team_id INTEGER NOT NULL, chose_partner INTEGER NOT NULL, FOREIGN KEY (person_id) REFERENCES persons(id) ON DELETE CASCADE - ON UPDATE CASCADE, - FOREIGN KEY (team_id) - REFERENCES teams(id) - ON DELETE CASCADE ON UPDATE CASCADE ); @@ -83,16 +101,12 @@ CREATE TABLE IF NOT EXISTS student_preferred_partners ( UNIQUE (student_id, partner_id) ); +-- TeachingAssistant-specific tables. CREATE TABLE IF NOT EXISTS teaching_assistants ( person_id INTEGER PRIMARY KEY, - team_id INTEGER NOT NULL, FOREIGN KEY (person_id) REFERENCES persons(id) ON DELETE CASCADE - ON UPDATE CASCADE, - FOREIGN KEY (team_id) - REFERENCES teams(id) - ON DELETE CASCADE ON UPDATE CASCADE );