diff --git a/canvas_classes.py b/canvas_classes.py
index 8be15a5fb24ef41b2f67990fb79fcd058976c04f..3715e27b9c77ed087d22869ec039fe850cf07de4 100644
--- a/canvas_classes.py
+++ b/canvas_classes.py
@@ -15,7 +15,10 @@ class CanvasSession:
 class CanvasUser:
     def __init__(self, user):
         super().__init__()
-        self.canvas_user = user
+        if isinstance(user, int):               # by NUID
+            self.canvas_user = CanvasSession.get_session().get_user(user, 'sis_user_id')
+        else:
+            self.canvas_user = user
 
     def get_name(self):
         return self.canvas_user.name
diff --git a/composite_user.py b/composite_user.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2fc2bd1fddd53605db2753636a782e4be309c6c
--- /dev/null
+++ b/composite_user.py
@@ -0,0 +1,98 @@
+from canvas_classes import CanvasUser
+from gitlab_classes import GitlabUser
+import csv
+
+NO_PARTNERING_LIST_MAXIMUM = 10
+
+
+class CompositeUser:
+    def __init__(self, student_dictionary, graylist, blacklist):
+        self.canvas_user = None
+        self.gitlab_user = None
+        self.sortable_name = student_dictionary['SortableName']
+        self.readable_name = student_dictionary['ReadableName']
+        self.NUID = student_dictionary['NUID']
+        self.canvas_username = student_dictionary['CanvasUsername']
+        self.gitlab_username = student_dictionary['GitlabUsername']
+        self.canvas_email = student_dictionary['CanvasEmail']
+        self.gitlab_email = student_dictionary['GitlabEmail']
+        self.graylist = set(graylist)
+        self.blacklist = set(blacklist)
+        self.candidate_teammates = None
+
+    def get_canvas_user(self):
+        if self.canvas_user is None:
+            self.canvas_user = CanvasUser(self.NUID)
+        return self.canvas_user
+
+    def get_gitlab_user(self):
+        if self.gitlab_user is None:
+            self.gitlab_user = GitlabUser(self.NUID)
+        return self.gitlab_user
+
+    def tentatively_pair_with(self, username):
+        self.candidate_teammates = {username}
+
+    def tentatively_team_with(self, usernames):
+        self.candidate_teammates = set(usernames)
+
+    def commit_team(self):
+        self.graylist = self.graylist.union(self.candidate_teammates)
+
+    def discard_team(self):
+        self.candidate_teammates = None
+
+    @staticmethod
+    def read_student_csv(filename):
+        students = set()
+        with open(filename, mode='r') as csv_file:
+            csv_reader = csv.DictReader(csv_file)
+            for student in csv_reader:
+                graylist = set()
+                blacklist = set()
+                for count in range(NO_PARTNERING_LIST_MAXIMUM):
+                    former_partner = student[f'Graylist{count}']
+                    undesired_partner = student[f'Blacklist{count}']
+                    if former_partner != "":
+                        graylist.add(former_partner)
+                    if undesired_partner != "":
+                        blacklist.add(undesired_partner)
+                student = CompositeUser(student, graylist, blacklist)
+                students.add(student)
+        return students
+
+    @staticmethod
+    def write_student_csv(students, filename):
+        with open(filename, mode='w') as csv_file:
+            fieldnames = ['SortableName', 'ReadableName', 'NUID',
+                          'CanvasUsername', 'CanvasEmail',
+                          'GitlabUsername', 'GitlabEmail']
+            for count in range(NO_PARTNERING_LIST_MAXIMUM):
+                fieldnames.append(f'Graylist{count}')
+            for count in range(NO_PARTNERING_LIST_MAXIMUM):
+                fieldnames.append(f'Blacklist{count}')
+            writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
+            writer.writeheader()
+            for student in students:
+                student_dictionary = {'SortableName':   student.sortable_name.strip(),
+                                      'ReadableName':   student.readable_name.strip(),
+                                      'NUID':           student.NUID,
+                                      'CanvasUsername': student.canvas_username,
+                                      'CanvasEmail':    student.canvas_email,
+                                      'GitlabUsername': student.gitlab_username,
+                                      'GitlabEmail':    student.gitlab_email}
+                count = 0
+                for former_partner in student.graylist:
+                    student_dictionary.update({f'Graylist{count}': former_partner})
+                    count += 1
+                while count < NO_PARTNERING_LIST_MAXIMUM:
+                    student_dictionary.update({f'Graylist{count}': ''})
+                    count += 1
+                count = 0
+                for undesired_partner in student.blacklist:
+                    student_dictionary.update({f'Blacklist{count}': undesired_partner})
+                    count += 1
+                while count < NO_PARTNERING_LIST_MAXIMUM:
+                    student_dictionary.update({f'Blacklist{count}': ''})
+                    count += 1
+                writer.writerow(student_dictionary)
diff --git a/gitlab_classes.py b/gitlab_classes.py
index 2be43b1921ebcdfe2c2872e77af180ca2fd4127c..7469f46aa8deea9f316679597ba0a895b9d2a5ed 100644
--- a/gitlab_classes.py
+++ b/gitlab_classes.py
@@ -4,17 +4,17 @@ import gitlab
 from config import Config
 
 
-class Session:
+class GitlabSession:
     __instance = None
 
     @staticmethod
     def get_session():
-        if Session.__instance is None:
-            Session.__instance = gitlab.Gitlab(Config.gitlab_url, private_token=Config.gitlab_api_key)
-        return Session.__instance
+        if GitlabSession.__instance is None:
+            GitlabSession.__instance = gitlab.Gitlab(Config.gitlab_url, private_token=Config.gitlab_api_key)
+        return GitlabSession.__instance
 
 
-class User:
+class GitlabUser:
     def __init__(self, user):
         """
         Creates a User object, populating the backing git_user instance with the appropriate gitlab.User object
@@ -23,9 +23,9 @@ class User:
         """
         super().__init__()
         if isinstance(user, int):               # by user id
-            self.git_user = Session.get_session().users.get(user)
+            self.git_user = GitlabSession.get_session().users.get(user)
         elif isinstance(user, str):             # by username
-            self.git_user = Session.get_session().users.list(username=user)[0]
+            self.git_user = GitlabSession.get_session().users.list(username=user)[0]
         else:
             self.git_user = user
 
@@ -168,12 +168,12 @@ class Project:
         """
         super().__init__()
         if isinstance(project, int):            # by project id
-            self.git_project = Session.get_session().projects.get(project)
+            self.git_project = GitlabSession.get_session().projects.get(project)
         elif isinstance(project, str):          # by path
-            self.git_project = Session.get_session().projects.get(project)
+            self.git_project = GitlabSession.get_session().projects.get(project)
         else:
             # self.git_project = project        # for some reason, many attributes (including members) might be lost
-            self.git_project = Session.get_session().projects.get(project.id)
+            self.git_project = GitlabSession.get_session().projects.get(project.id)
 
     @staticmethod
     def get_projects_by_group(group):
@@ -182,9 +182,9 @@ class Project:
         :return: list of projects in the specified group
         """
         if isinstance(group, int):              # by group id
-            gitlab_projects = Session.get_session().groups.get(group).projects.list(all=True)
+            gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
         else:  # isinstance(group, str):        # by path
-            gitlab_projects = Session.get_session().groups.get(group).projects.list(all=True)
+            gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
         projects = []
         for project in gitlab_projects:
             projects.append(Project(project))
@@ -192,7 +192,7 @@ class Project:
 
     @staticmethod
     def get_projects_by_keyword(search_term):
-        gitlab_projects = Session.get_session().projects.list(search=search_term, all=True)
+        gitlab_projects = GitlabSession.get_session().projects.list(search=search_term, all=True)
         projects = []
         for project in gitlab_projects:
             projects.append(Project(project))
@@ -200,12 +200,12 @@ class Project:
 
     @staticmethod
     def create_project(project_name):
-        return Session.get_session().projects.create({'name': project_name})
+        return GitlabSession.get_session().projects.create({'name': project_name})
 
     @staticmethod
     def create_project_in_group(group_name, project_name):
-        group_id = Session.get_session().groups.get(group_name).id
-        return Session.get_session().projects.create({'name': project_name, 'namespace_id': group_id})
+        group_id = GitlabSession.get_session().groups.get(group_name).id
+        return GitlabSession.get_session().projects.create({'name': project_name, 'namespace_id': group_id})
 
     def get_project_id(self):
         return self.git_project.id
@@ -262,7 +262,7 @@ class Project:
         """
         :return: User object backed by the gitlab.User object representing the user who created the repo
         """
-        return User(self.git_project.creator_id)
+        return GitlabUser(self.git_project.creator_id)
 
     def get_created_at(self):
         """
@@ -280,7 +280,7 @@ class Project:
         gitlab_users = self.git_project.members.list(all=True)
         users = []
         for user in gitlab_users:
-            users.append(User(user))
+            users.append(GitlabUser(user))
         return users
 
     def get_all_users(self):
@@ -290,7 +290,7 @@ class Project:
         gitlab_users = self.git_project.members.all(all=True)
         users = []
         for user in gitlab_users:
-            users.append(User(user))
+            users.append(GitlabUser(user))
         return users
 
     def add_user_as_maintainer(self, user):