Repository: ubclaunchpad/rocket2

Issue:
Switch slack IDs in team table to Github ID
Currently the users and teams tables are strongly coupled, as the team table uses the slack IDs that are the primary keys of the users table. However, since we will assume Github is the source of truth for teams, we should instead store Github IDs.

Comments:

Patch:
diff --git a/db/dynamodb.py b/db/dynamodb.py
index 3ddf148..32b9835 100644
--- a/db/dynamodb.py
+++ b/db/dynamodb.py
@@ -111,7 +111,7 @@ class DynamoDB:
         **Note**: This function should **not** be called externally, and should
         only be called on initialization.
 
-        Teams are only required to have a ``github_team_name``. Since this is a
+        Teams are only required to have a ``github_team_id``. Since this is a
         NoSQL database, no other attributes are required.
         """
         logging.info("Creating table '{}'".format(self.teams_table))
@@ -119,13 +119,13 @@ class DynamoDB:
             TableName=self.teams_table,
             AttributeDefinitions=[
                 {
-                    'AttributeName': 'github_team_name',
+                    'AttributeName': 'github_team_id',
                     'AttributeType': 'S'
                 },
             ],
             KeySchema=[
                 {
-                    'AttributeName': 'github_team_name',
+                    'AttributeName': 'github_team_id',
                     'KeyType': 'HASH'
                 },
             ],
@@ -167,7 +167,7 @@ class DynamoDB:
             place_if_filled('email', user.get_email())
             place_if_filled('name', user.get_name())
             place_if_filled('github', user.get_github_username())
-            place_if_filled('guid', user.get_github_id())
+            place_if_filled('github_user_id', user.get_github_id())
             place_if_filled('major', user.get_major())
             place_if_filled('position', user.get_position())
             place_if_filled('bio', user.get_biography())
@@ -195,6 +195,7 @@ class DynamoDB:
 
             teams_table = self.ddb.Table(self.teams_table)
             tdict = {
+                'github_team_id': team.get_github_team_id(),
                 'github_team_name': team.get_github_team_name()
             }
             place_if_filled('display_name', team.get_display_name())
@@ -238,7 +239,7 @@ class DynamoDB:
         user.set_email(d.get('email', ''))
         user.set_name(d.get('name', ''))
         user.set_github_username(d.get('github', ''))
-        user.set_github_id(d.get('guid', ''))
+        user.set_github_id(d.get('github_user_id', ''))
         user.set_major(d.get('major', ''))
         user.set_position(d.get('position', ''))
         user.set_biography(d.get('bio', ''))
@@ -247,7 +248,7 @@ class DynamoDB:
                                                      'members')])
         return user
 
-    def retrieve_team(self, team_name):
+    def retrieve_team(self, team_id):
         """
         Retrieve team from teams table.
 
@@ -259,13 +260,14 @@ class DynamoDB:
         response = team_table.get_item(
             TableName=self.teams_table,
             Key={
-                'github_team_name': team_name
+                'github_team_id': team_id
             }
         )
+
         if 'Item' in response.keys():
             return self.team_from_dict(response['Item'])
         else:
-            raise LookupError('Team "{}" not found'.format(team_name))
+            raise LookupError('Team "{}" not found'.format(team_id))
 
     @staticmethod
     def team_from_dict(d):
@@ -274,7 +276,9 @@ class DynamoDB:
 
         :return: returns converted team model.
         """
-        team = Team(d['github_team_name'], d.get('display_name', ''))
+        team = Team(d['github_team_id'],
+                    d['github_team_name'],
+                    d.get('display_name', ''))
         team.set_platform(d.get('platform', ''))
         members = set(d.get('members', []))
         for member in members:
@@ -370,17 +374,19 @@ class DynamoDB:
             }
         )
 
-    def delete_team(self, team_name):
+    def delete_team(self, team_id):
         """
         Remove a team from the teams table.
 
-        :param team_name: the team_name of the team to be removed
+        To obtain the team github id, you have to retrieve the team first.
+
+        :param team_id: the team_id of the team to be removed
         """
         logging.info("Deleting team {} from table {}".
-                     format(team_name, self.teams_table))
+                     format(team_id, self.teams_table))
         team_table = self.ddb.Table(self.teams_table)
         team_table.delete_item(
             Key={
-                'github_team_name': team_name
+                'github_team_id': team_id
             }
         )
diff --git a/docs/Database.md b/docs/Database.md
index 24e4238..353b107 100644
--- a/docs/Database.md
+++ b/docs/Database.md
@@ -12,6 +12,7 @@ Attribute Name | Description
 `slack_id` | `String`; The user's slack id
 `email` | `String`; The user's email address
 `github` | `String`; The user's Github handler
+`github_user_id` | `String`; The user's Github user ID
 `major` | `String`; The subject major the user is in
 `position` | `String`; The user's position in _Launch Pad_
 `bio` | `String`; A short (auto)biography (about the user)
@@ -28,7 +29,8 @@ and are also listed here:
 
 Attribute Name | Description
 ---|---
-`github_team_name` | `String`; The team's github name
+`github_team_id` | `String`; The team's Github ID
+`github_team_name` | `String`; The team's Github name
 `display_name` | `String`; The teams's display
 `platform` | `String`; The team's working platform
-`members` | `String Set`; The team's set of members
+`members` | `String Set`; The team's set of members' Github IDs
diff --git a/model/team.py b/model/team.py
index 302d409..7e1d3af 100644
--- a/model/team.py
+++ b/model/team.py
@@ -4,11 +4,13 @@
 class Team:
     """Represent a team with related fields and methods."""
 
-    def __init__(self, github_team_name, display_name):
-        """Initialize the team.
+    def __init__(self, github_team_id, github_team_name, display_name):
+        """
+        Initialize the team.
 
-        Parameters are a valid Github team name and display name.
+        Parameters are a valid Github team ID, team name and display name.
         """
+        self.__github_team_id = github_team_id
         self.__github_team_name = github_team_name
         self.__display_name = display_name
         self.__platform = ""
@@ -21,11 +23,13 @@ class Team:
 
         Required fields for database to accept:
         - ``__github_team_name``
+        - ``__github_team_id``
 
         :param team: team to check
         :return: returns true if this team has no missing required fields
         """
-        return len(team.get_github_team_name()) > 0
+        return len(team.get_github_team_name()) > 0 and\
+            len(team.get_github_team_id()) > 0
 
     def __eq__(self, other):
         """Return true if this team has the same attributes as the other."""
@@ -35,6 +39,14 @@ class Team:
         """Return the opposite of what is returned in self.__eq__(other)."""
         return not (self == other)
 
+    def get_github_team_id(self):
+        """Return this team's unique Github team ID."""
+        return self.__github_team_id
+
+    def set_github_team_id(self, github_team_id):
+        """Set this team's unique Github team ID."""
+        self.__github_team_id = github_team_id
+
     def get_github_team_name(self):
         """Return this team's unique Github team name."""
         return self.__github_team_name
@@ -55,21 +67,21 @@ class Team:
         """Return this team's working platform."""
         return self.__platform
 
-    def add_member(self, slack_id):
-        """Add a new member's Slack ID to the team's set of members' IDs."""
-        self.__members.add(slack_id)
+    def add_member(self, github_user_id):
+        """Add a new member's Github ID to the team's set of members' IDs."""
+        self.__members.add(github_user_id)
 
-    def discard_member(self, slack_id):
-        """Discard the member of the team with the Slack ID in the argument."""
-        self.__members.discard(slack_id)
+    def discard_member(self, github_user_id):
+        """Discard the member of the team with Github ID in the argument."""
+        self.__members.discard(github_user_id)
 
     def get_members(self):
-        """Return the set of all members' Slack IDs."""
+        """Return the set of all members' Github IDs."""
         return self.__members
 
-    def is_member(self, slack_id):
+    def is_member(self, github_user_id):
         """Identify if any member has the ID specified in the argument."""
-        return slack_id in self.__members
+        return github_user_id in self.__members
 
     def __str__(self):
         """Print information on the team class."""
diff --git a/model/user.py b/model/user.py
index 2970597..d134dcc 100644
--- a/model/user.py
+++ b/model/user.py
@@ -99,9 +99,9 @@ class User:
         """Return this user's Github ID."""
         return self.__github_id
 
-    def set_github_id(self, guid):
+    def set_github_id(self, github_user_id):
         """Set this user's Github ID."""
-        self.__github_id = guid
+        self.__github_id = github_user_id
 
     def get_major(self):
         """Return this user's major."""


Test patch:
diff --git a/tests/db/dynamodb_test.py b/tests/db/dynamodb_test.py
index 030ae9b..2ddc61a 100644
--- a/tests/db/dynamodb_test.py
+++ b/tests/db/dynamodb_test.py
@@ -38,7 +38,7 @@ def test_store_invalid_user(ddb_connection):
 def test_store_invalid_team(ddb_connection):
     """Test handling of invalid team."""
     ddb = ddb_connection
-    team = Team('', 'Brussel Sprouts')
+    team = Team('1', '', 'Brussel Sprouts')
     success = ddb.store_team(team)
     assert not success
 
@@ -108,11 +108,11 @@ def test_retrieve_invalid_team(ddb_connection):
     """Test to see if we can retrieve a non-existent team."""
     ddb = ddb_connection
     try:
-        team = ddb.retrieve_team('rocket2.0')
+        team = ddb.retrieve_team('1')
 
         assert False
     except LookupError as e:
-        assert str(e) == 'Team "{}" not found'.format('rocket2.0')
+        assert str(e) == 'Team "{}" not found'.format('1')
 
 
 @pytest.mark.db
@@ -135,38 +135,38 @@ def test_update_user(ddb_connection):
 def test_update_team(ddb_connection):
     """Test to see if we can update a team."""
     ddb = ddb_connection
-    t = Team('brussel-sprouts', 'Brussel Sprouts')
+    t = Team('1', 'brussel-sprouts', 'Brussel Sprouts')
     ddb.store_team(t)
 
-    t = ddb.retrieve_team('brussel-sprouts')
+    t = ddb.retrieve_team('1')
     t.add_member('abc_123')
     t.add_member('123_abc')
     ddb.store_team(t)
 
-    assert len(ddb.retrieve_team('brussel-sprouts').get_members()) == 2
+    assert len(ddb.retrieve_team('1').get_members()) == 2
 
-    ddb.delete_team('brussel-sprouts')
+    ddb.delete_team('1')
 
 
 @pytest.mark.db
 def test_store_retrieve_team(ddb_connection):
     """Test to see if we can store and retrieve the same team."""
     ddb = ddb_connection
-    team = create_test_team('rocket2.0', 'Rocket 2.0')
+    team = create_test_team('1', 'rocket2.0', 'Rocket 2.0')
     assert ddb.store_team(team)
-    another_team = ddb.retrieve_team('rocket2.0')
+    another_team = ddb.retrieve_team('1')
 
     assert team == another_team
 
-    ddb.delete_team('rocket2.0')
+    ddb.delete_team('1')
 
 
 @pytest.mark.db
 def test_query_team(ddb_connection):
     """Test to see if we can store and query the same team."""
     ddb = ddb_connection
-    team = create_test_team('rocket2.0', 'Rocket 2.0')
-    team2 = create_test_team('lame-o', 'Lame-O Team')
+    team = create_test_team('1', 'rocket2.0', 'Rocket 2.0')
+    team2 = create_test_team('2', 'lame-o', 'Lame-O Team')
     team2.add_member('apple')
     ddb.store_team(team)
     ddb.store_team(team2)
@@ -185,8 +185,8 @@ def test_query_team(ddb_connection):
     assert team == multiple_queries[0]
     assert team2 == member_team[0]
 
-    ddb.delete_team('rocket2.0')
-    ddb.delete_team('lame-o')
+    ddb.delete_team('1')
+    ddb.delete_team('2')
 
 
 @pytest.mark.db
@@ -205,9 +205,9 @@ def test_delete_user(ddb_connection):
 def test_delete_team(ddb_connection):
     """Test to see if we can successfully delete a team."""
     ddb = ddb_connection
-    team = create_test_team('rocket-2.0', 'Rocket 2.0')
+    team = create_test_team('1', 'rocket-2.0', 'Rocket 2.0')
     ddb.store_team(team)
 
     assert len(ddb.query_team([])) == 1
-    ddb.delete_team('rocket-2.0')
+    ddb.delete_team('1')
     assert len(ddb.query_team([])) == 0
diff --git a/tests/db/facade_test.py b/tests/db/facade_test.py
index 0d225d2..9561052 100644
--- a/tests/db/facade_test.py
+++ b/tests/db/facade_test.py
@@ -40,7 +40,7 @@ def test_query_user(ddb):
 def test_store_team(ddb):
     """Test storing team calls correct functions."""
     dbf = DBFacade(ddb)
-    test_team = create_test_team('brussel-sprouts', 'Brussel Sprouts')
+    test_team = create_test_team('1', 'brussel-sprouts', 'Brussel Sprouts')
     dbf.store_team(test_team)
     ddb.store_team.assert_called_with(test_team)
 
diff --git a/tests/model/team_test.py b/tests/model/team_test.py
index c5ad2bd..c02a1f1 100644
--- a/tests/model/team_test.py
+++ b/tests/model/team_test.py
@@ -5,62 +5,75 @@ from tests.util import create_test_team
 
 def test_team_equality():
     """Test the Team class method __eq__() and __ne__()."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
-    team2 = Team("brussel-sprouts", "Brussel Sprouts")
-    team3 = Team("brussel-trouts", "Brussel Trouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
+    team2 = Team("1", "brussel-sprouts", "Brussel Sprouts")
+    team3 = Team("1", "brussel-trouts", "Brussel Trouts")
     assert team == team2
     assert team != team3
 
 
 def test_valid_team():
     """Test the Team static class method is_valid()."""
-    team = Team("", "Brussel Sprouts")
+    team = Team("1", "", "Brussel Sprouts")
     assert not Team.is_valid(team)
-    team = create_test_team("brussel-sprouts", "Brussel Sprouts")
+    team = create_test_team("1", "brussel-sprouts", "Brussel Sprouts")
     assert Team.is_valid(team)
 
 
+def test_get_github_team_id():
+    """Test the Team class method get_github_team_id()."""
+    team = Team('1', 'brussel-sprouts', 'Brussel Sprouts')
+    assert team.get_github_team_id() == '1'
+
+
+def test_set_github_team_id():
+    """Test the Team class method set_github_team_id()."""
+    team = Team('1', 'brussel-sprouts', 'Brussel Sprouts')
+    team.set_github_team_id('2')
+    assert team.get_github_team_id() == '2'
+
+
 def test_get_github_team_name():
     """Test the Team class method set_github_team_name()."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     assert team.get_github_team_name() == "brussel-sprouts"
 
 
 def test_get_display_name():
     """Test the Team class method get_display_name()."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     assert team.get_display_name() == "Brussel Sprouts"
 
 
 def test_set_display_name():
     """Test the Team class method set_display_name(display_name)."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     team.set_display_name("Corn Cobs")
     assert team.get_display_name() == "Corn Cobs"
 
 
 def test_get_platform():
     """Test the Team class method get_platform()."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     assert team.get_platform() == ""
 
 
 def test_set_platform():
     """Test the Team class method set_platform(platform)."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     team.set_platform("web")
     assert team.get_platform() == "web"
 
 
 def test_get_members():
     """Test the Team class method get_members()."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     assert team.get_members() == set()
 
 
 def test_add_member():
     """Test the Team class method add_member(uuid)."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     new_slack_id = "U0G9QF9C6"
     team.add_member(new_slack_id)
     assert new_slack_id in team.get_members()
@@ -68,7 +81,7 @@ def test_add_member():
 
 def test_discard_member():
     """Test the Team class method discard_member(slack_id)."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     new_slack_id = "U0G9QF9C6"
     team.add_member(new_slack_id)
     team.discard_member(new_slack_id)
@@ -77,7 +90,7 @@ def test_discard_member():
 
 def test_is_member():
     """Test the Team class method is_member(slack_id)."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     new_slack_id = "U0G9QF9C6"
     assert team.is_member(new_slack_id) is False
     team.add_member(new_slack_id)
@@ -86,11 +99,12 @@ def test_is_member():
 
 def test_print():
     """Test print team class."""
-    team = Team("brussel-sprouts", "Brussel Sprouts")
+    team = Team("1", "brussel-sprouts", "Brussel Sprouts")
     new_slack_id = "U0G9QF9C6"
     team.add_member(new_slack_id)
     team.set_platform("web")
-    assert str(team) == "{'_Team__github_team_name': 'brussel-sprouts'," \
+    assert str(team) == "{'_Team__github_team_id': '1'," \
+                        " '_Team__github_team_name': 'brussel-sprouts'," \
                         " '_Team__display_name': 'Brussel Sprouts'," \
                         " '_Team__platform': 'web'," \
                         " '_Team__members': {'U0G9QF9C6'}}"
diff --git a/tests/util.py b/tests/util.py
index 37bb6b7..bea3875 100644
--- a/tests/util.py
+++ b/tests/util.py
@@ -23,14 +23,14 @@ def create_test_user(slack_id):
     return u
 
 
-def create_test_team(team_name, display_name):
+def create_test_team(tid, team_name, display_name):
     """
     Create a test team with team name, and with all other attributes the same.
 
     :param team_name: The github team name string
     :return: returns a filled-in user model (no empty strings)
     """
-    t = Team(team_name, display_name)
+    t = Team(tid, team_name, display_name)
     t.set_platform('slack')
     t.add_member('abc_123')
     return t