diff --git a/api/canvas_experiments.py b/api/canvas_experiments.py
index 4719dbc23434ececfc49d8b44b0b154106b9c355..d4c0fa51f3b5aed635b167656b7e76c8732e8c6d 100644
--- a/api/canvas_experiments.py
+++ b/api/canvas_experiments.py
@@ -42,6 +42,7 @@ if __name__ == '__main__':
recipients = canvas.search_recipients()
print(recipients)
"""
+ """
course = canvas.get_course(Course.canvas_course_id)
users = course.get_users()
for user in users:
@@ -63,3 +64,12 @@ if __name__ == '__main__':
users = foo2.get_users()
for user in users:
print(user.name)
+ """
+ user = canvas.get_user(30266045, 'sis_user_id') # can retrieve my own user
+ print(user)
+ # user = canvas.get_user(76390201, 'sis_user_id') # cannot retrieve arbitrary user
+ # print(user)
+ users = canvas.get_course(Course.canvas_course_id).get_users()
+ # user = list(filter(lambda s: s.sis_user_id == 76390201, users))[0]
+ user = list(filter(lambda s: s.login_id == 'mkluck2', users))[0]
+ print(user)
\ No newline at end of file
diff --git a/api/course.py b/api/course.py
index 5a907ff767a779cd184bb06c2c411b0b2ffc9460..b2ae0d9239150bbed495600aa82a32767e891e15 100644
--- a/api/course.py
+++ b/api/course.py
@@ -1,6 +1,7 @@
class Course:
# GitLab course information
- gitlab_namespace = 'csce_361/sandbox'
+ # gitlab_namespace = 'csce_361/sandbox'
+ gitlab_namespace = 'csce_361/fall2019'
# Canvas course information
# canvas_course_id = '73696' # Software Engineering Sandbox
diff --git a/canvas_classes.py b/canvas_classes.py
index 03f52a937fd52e325a11a6fec0c545de83ffdd8c..3b639de1162297e1c8635309b20c821339b0f395 100644
--- a/canvas_classes.py
+++ b/canvas_classes.py
@@ -40,7 +40,7 @@ class CanvasUser:
def __repr__(self):
username = self.get_username()
- return f'@{username}'
+ return f'{username}'
def __eq__(self, other):
if isinstance(other, CanvasUser):
@@ -102,7 +102,7 @@ class CanvasUser:
"""
-class GroupSet: # aka, group_category
+class CanvasGroupSet: # aka, group_category
def __init__(self, group_category):
super().__init__()
self.canvas_group_category = group_category
@@ -114,19 +114,19 @@ class GroupSet: # aka, group_category
canvas_groups = self.canvas_group_category.get_groups()
groups = []
for group in canvas_groups:
- groups.append(Group(group))
+ groups.append(CanvasGroup(group))
return groups
def create_group(self, group_name):
canvas_group = self.canvas_group_category.create_group(name=group_name)
- return Group(canvas_group)
+ return CanvasGroup(canvas_group)
def create_groups(self, number_of_groups):
base_name = self.get_name()
groups = []
for group_number in range(1, number_of_groups+1):
canvas_group = self.create_group(f'{base_name} {group_number}')
- groups.append(Group(canvas_group))
+ groups.append(CanvasGroup(canvas_group))
return groups
def __repr__(self):
@@ -178,7 +178,7 @@ class GroupSet: # aka, group_category
"""
-class Group:
+class CanvasGroup:
def __init__(self, group):
super().__init__()
self.canvas_group = group
@@ -261,7 +261,7 @@ class Group:
"""
-class Course:
+class CanvasCourse:
def __init__(self, course_id):
self.canvas_course = CanvasSession.get_session().get_course(course_id)
@@ -290,19 +290,19 @@ class Course:
canvas_groups = self.canvas_course.get_groups()
groups = []
for group in canvas_groups:
- groups.append(Group(group))
+ groups.append(CanvasGroup(group))
return groups
def get_group_sets(self):
canvas_group_categories = self.canvas_course.get_group_categories()
group_sets = []
for group_category in canvas_group_categories:
- group_sets.append(GroupSet(group_category))
+ group_sets.append(CanvasGroupSet(group_category))
return group_sets
def create_groupset(self, groupset_name):
group_category = self.canvas_course.create_group_category(groupset_name)
- return GroupSet(group_category)
+ return CanvasGroupSet(group_category)
def __repr__(self):
return f'{self.canvas_course.course_code}: {self.canvas_course.name}'
diff --git a/composite_user.py b/composite_user.py
index bf2d485f10f9ce5b185006dfefc78a47b5d057e1..802127e7519bc5bacf57e85444c4b18bd4e20119 100644
--- a/composite_user.py
+++ b/composite_user.py
@@ -1,5 +1,7 @@
from canvas_classes import CanvasUser
+from canvas_classes import CanvasCourse
from gitlab_classes import GitlabUser
+from course import Course
import csv
NO_PARTNERING_LIST_MAXIMUM = 10
@@ -13,7 +15,7 @@ class CompositeUser:
self.gitlab_user = None
self.sortable_name = student_dictionary['SortableName']
self.readable_name = student_dictionary['ReadableName']
- self.NUID = student_dictionary['NUID']
+ self.NUID = int(student_dictionary['NUID'])
self.canvas_username = student_dictionary['CanvasUsername']
self.gitlab_username = student_dictionary['GitlabUsername']
self.canvas_email = student_dictionary['CanvasEmail']
@@ -28,12 +30,12 @@ class CompositeUser:
def get_canvas_user(self):
if self.canvas_user is None:
- self.canvas_user = CanvasUser(self.NUID)
+ self.canvas_user = CanvasUser(self.NUID) # n.b., can retrieve own user but not arbitrary user
return self.canvas_user
def get_gitlab_user(self):
if self.gitlab_user is None:
- self.gitlab_user = GitlabUser(self.NUID)
+ self.gitlab_user = GitlabUser(self.gitlab_username)
return self.gitlab_user
def tentatively_pair_with(self, username):
@@ -52,10 +54,10 @@ class CompositeUser:
return len(self.blacklist) > 0
def is_blacklist_compatible(self, other):
- return other not in self.blacklist
+ return other not in self.blacklist and self not in other.blacklist
def is_graylist_compatible(self, other):
- return other not in self.graylist
+ return other not in self.graylist and self not in other.graylist
def __repr__(self):
if self.canvas_email == self.gitlab_email:
@@ -72,6 +74,9 @@ class CompositeUser:
def __ne__(self, other):
return not self.__eq__(other)
+ def __hash__(self) -> int:
+ return hash(self.canvas_username)
+
@staticmethod
def get_user(username_or_email):
return CompositeUser.instances[username_or_email]
@@ -81,18 +86,22 @@ class CompositeUser:
students = set()
with open(filename, mode='r') as csv_file:
csv_reader = csv.DictReader(csv_file)
- for student in csv_reader:
+ for csv_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}']
+ former_partner = csv_student[f'Graylist{count}']
+ undesired_partner = csv_student[f'Blacklist{count}']
if former_partner != "":
graylist.add(former_partner)
if undesired_partner != "":
blacklist.add(undesired_partner)
- student = CompositeUser(student, graylist, blacklist)
+ student = CompositeUser(csv_student, graylist, blacklist)
students.add(student)
+ canvas_students = CanvasCourse(Course.canvas_course_id).get_students()
+ for canvas_student in canvas_students:
+ composite_student = list(filter(lambda s: s.canvas_username == canvas_student.get_username(), students))[0]
+ composite_student.canvas_user = canvas_student
return students
@staticmethod
diff --git a/gitlab_classes.py b/gitlab_classes.py
index cc3d2cf965dee3e032febadd731ece09aad78cda..18ee72cace506ba336bb7a41e8cd14aca8dba11f 100644
--- a/gitlab_classes.py
+++ b/gitlab_classes.py
@@ -55,7 +55,7 @@ class GitlabUser:
return not self.__eq__(other)
-class Issue:
+class GitlabIssue:
def __init__(self, issue):
"""
Creates an Issue object, populating the backing git_issue instance with the appropriate gitlab.Issue object
@@ -168,7 +168,7 @@ class Issue:
# subscribed
-class Project:
+class GitlabProject:
def __init__(self, project):
"""
Creates a Project object, populating the backing git_project instance with the appropriate gitlab.Project object
@@ -196,7 +196,7 @@ class Project:
gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
projects = []
for project in gitlab_projects:
- projects.append(Project(project))
+ projects.append(GitlabProject(project))
return projects
@staticmethod
@@ -204,17 +204,17 @@ class Project:
gitlab_projects = GitlabSession.get_session().projects.list(search=search_term, all=True)
projects = []
for project in gitlab_projects:
- projects.append(Project(project))
+ projects.append(GitlabProject(project))
return projects
@staticmethod
def create_project(project_name):
- return GitlabSession.get_session().projects.create({'name': project_name})
+ return GitlabProject(GitlabSession.get_session().projects.create({'name': project_name}))
@staticmethod
def create_project_in_group(group_name, project_name):
group_id = GitlabSession.get_session().groups.get(group_name).id
- return GitlabSession.get_session().projects.create({'name': project_name, 'namespace_id': group_id})
+ return GitlabProject(GitlabSession.get_session().projects.create({'name': project_name, 'namespace_id': group_id}))
def get_project_id(self):
return self.git_project.id
@@ -312,12 +312,12 @@ class Project:
gitlab_issues = self.git_project.issues.list(order_by='created_at', sort='asc', all=True)
issues = []
for issue in gitlab_issues:
- issues.append(Issue(issue))
+ issues.append(GitlabIssue(issue))
return issues
def create_issue(self, title, description):
gitlab_issue = self.git_project.issues.create({'title': title, 'description': description})
- return Issue(gitlab_issue)
+ return GitlabIssue(gitlab_issue)
def __repr__(self):
return self.get_name_with_namespace()
@@ -373,7 +373,7 @@ class Project:
if __name__ == '__main__':
namespace = 'csce_361/sandbox'
- test_projects = Project.get_projects_by_group(namespace)
+ test_projects = GitlabProject.get_projects_by_group(namespace)
print('All projects in sandbox:')
for test_project in test_projects:
print(test_project)
@@ -391,7 +391,7 @@ if __name__ == '__main__':
for test_issue in test_issues:
creation = test_issue.get_created_at()
print(f'{test_issue}\tcreated at {creation}.')
- test_projects = Project.get_projects_by_keyword('csce361-homework')
+ test_projects = GitlabProject.get_projects_by_keyword('csce361-homework')
number_of_projects = len(test_projects)
print(f'retrieved {number_of_projects} projects matching \'csce361-homework\'')
start_date = datetime(2019, 8, 1, tzinfo=timezone.utc)
diff --git a/prep_assignment.py b/prep_assignment.py
new file mode 100644
index 0000000000000000000000000000000000000000..edf24f62d37ab905f8a63e66f12a2bae77b4ce69
--- /dev/null
+++ b/prep_assignment.py
@@ -0,0 +1,97 @@
+import random
+import subprocess
+from composite_user import CompositeUser
+from canvas_classes import *
+from gitlab_classes import *
+from course import Course
+
+
+def create_pairs(filename):
+ # only works when there are an even number of students
+ students = CompositeUser.read_student_csv(filename)
+ students_with_blacklist = sorted(list(filter(lambda s: s.has_blacklist(), students)),
+ key=lambda t: len(t.blacklist), reverse=True)
+ unassigned_students = set(students)
+ pair_number = 0
+ pairs = []
+ for student in students_with_blacklist:
+ pair_number += 1
+ unassigned_students.remove(student)
+ potential_partner = random.choice(tuple(unassigned_students))
+ while not (student.is_blacklist_compatible(potential_partner) and
+ student.is_graylist_compatible(potential_partner)):
+ # has the potential to run infinitely
+ potential_partner = random.choice(tuple(unassigned_students))
+ unassigned_students.remove(potential_partner)
+ pairs.append((pair_number, student, potential_partner))
+ while unassigned_students:
+ pair_number += 1
+ student = random.choice(tuple(unassigned_students))
+ unassigned_students.remove(student)
+ attempts = 1
+ potential_partner = random.choice(tuple(unassigned_students))
+ while not (student.is_graylist_compatible(potential_partner) and attempts <= len(unassigned_students)):
+ attempts += 1
+ potential_partner = random.choice(tuple(unassigned_students))
+ unassigned_students.remove(potential_partner)
+ pairs.append((pair_number, student, potential_partner))
+ return pairs
+
+
+def save_pairs(assignment_number, student_pairs):
+ filename = f'{assignment_number}-pairs.md'
+ with open(filename, mode='w') as pair_file:
+ pair_file.write(f'# PARTNERS FOR ASSIGNMENT {assignment_number}\n\n')
+ for pair in student_pairs:
+ pair_file.write(f'- {assignment_number}pair {pair[0]}\n')
+ pair_file.write(f' - {pair[1]}\n')
+ pair_file.write(f' - {pair[2]}\n')
+
+
+def create_repositories(assignment_number, student_pairs):
+ filename = f'{assignment_number}-clone.sh'
+ with open(filename, mode='w') as clone_file:
+ clone_file.write('#!/bin/bash\n\n')
+ clone_file.write('# Auto-generated clone script.\n')
+ for pair in student_pairs:
+ project = GitlabProject.create_project_in_group(Course.gitlab_namespace, f'{assignment_number}pair{pair[0]}')
+ project.add_user_as_maintainer(pair[1].get_gitlab_user())
+ project.add_user_as_maintainer(pair[2].get_gitlab_user())
+ repo_url = project.get_cloning_url()
+ clone_file.write(f'git clone {repo_url}\n')
+ subprocess.call(['chmod', '+x', filename])
+
+
+def create_groups(assignment_number, student_pairs):
+ course = CanvasCourse(Course.canvas_course_id)
+ group_set = course.create_groupset(f'{assignment_number}pairs')
+ for pair in student_pairs:
+ group = group_set.create_group(f'{assignment_number}pair {pair[0]}')
+ group.add_student(pair[1].get_canvas_user())
+ group.add_student(pair[2].get_canvas_user())
+
+
+if __name__ == '__main__':
+ assignment = 10
+ pairs = create_pairs('2019-08.csv')
+ save_pairs(assignment, pairs)
+ print('Pairs created')
+
+ # pair = list(pairs)[0]
+ # print(f'{pair[1].get_gitlab_user()}, {pair[2].get_gitlab_user()}')
+ # project = GitlabProject.create_project_in_group(Course.gitlab_namespace, f'{assignment_number}pairx{pair[0]}')
+ # project.add_user_as_maintainer(pair[1].get_gitlab_user())
+ # project.add_user_as_maintainer(pair[2].get_gitlab_user())
+
+ create_repositories(assignment, pairs)
+ print('Repositories created')
+ create_groups(assignment, pairs)
+ print('Canvas groups created')
+
+ print('TODO:\tAdd issues')
+ print('\tCommit starter code')
+
+ """
+ BALLS! I forgot to update the graylists
+ I also forgot newlines in the cloning script - fixed code, need to fix script
+ """