Skip to content
Snippets Groups Projects
Commit c6c64c04 authored by Christopher Bohn's avatar Christopher Bohn :thinking:
Browse files

Wrote code to prepare an assignment.

This first iteration is limited to working with an even number of
students, does not yet add Issues to the Gitlab Issue Tracker, and
does not yet update the graylists in the student CSV.
parent dd20cb6f
No related branches found
No related tags found
No related merge requests found
...@@ -42,6 +42,7 @@ if __name__ == '__main__': ...@@ -42,6 +42,7 @@ if __name__ == '__main__':
recipients = canvas.search_recipients() recipients = canvas.search_recipients()
print(recipients) print(recipients)
""" """
"""
course = canvas.get_course(Course.canvas_course_id) course = canvas.get_course(Course.canvas_course_id)
users = course.get_users() users = course.get_users()
for user in users: for user in users:
...@@ -63,3 +64,12 @@ if __name__ == '__main__': ...@@ -63,3 +64,12 @@ if __name__ == '__main__':
users = foo2.get_users() users = foo2.get_users()
for user in users: for user in users:
print(user.name) 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
class Course: class Course:
# GitLab course information # GitLab course information
gitlab_namespace = 'csce_361/sandbox' # gitlab_namespace = 'csce_361/sandbox'
gitlab_namespace = 'csce_361/fall2019'
# Canvas course information # Canvas course information
# canvas_course_id = '73696' # Software Engineering Sandbox # canvas_course_id = '73696' # Software Engineering Sandbox
......
...@@ -40,7 +40,7 @@ class CanvasUser: ...@@ -40,7 +40,7 @@ class CanvasUser:
def __repr__(self): def __repr__(self):
username = self.get_username() username = self.get_username()
return f'@{username}' return f'{username}'
def __eq__(self, other): def __eq__(self, other):
if isinstance(other, CanvasUser): if isinstance(other, CanvasUser):
...@@ -102,7 +102,7 @@ class CanvasUser: ...@@ -102,7 +102,7 @@ class CanvasUser:
""" """
class GroupSet: # aka, group_category class CanvasGroupSet: # aka, group_category
def __init__(self, group_category): def __init__(self, group_category):
super().__init__() super().__init__()
self.canvas_group_category = group_category self.canvas_group_category = group_category
...@@ -114,19 +114,19 @@ class GroupSet: # aka, group_category ...@@ -114,19 +114,19 @@ class GroupSet: # aka, group_category
canvas_groups = self.canvas_group_category.get_groups() canvas_groups = self.canvas_group_category.get_groups()
groups = [] groups = []
for group in canvas_groups: for group in canvas_groups:
groups.append(Group(group)) groups.append(CanvasGroup(group))
return groups return groups
def create_group(self, group_name): def create_group(self, group_name):
canvas_group = self.canvas_group_category.create_group(name=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): def create_groups(self, number_of_groups):
base_name = self.get_name() base_name = self.get_name()
groups = [] groups = []
for group_number in range(1, number_of_groups+1): for group_number in range(1, number_of_groups+1):
canvas_group = self.create_group(f'{base_name} {group_number}') canvas_group = self.create_group(f'{base_name} {group_number}')
groups.append(Group(canvas_group)) groups.append(CanvasGroup(canvas_group))
return groups return groups
def __repr__(self): def __repr__(self):
...@@ -178,7 +178,7 @@ class GroupSet: # aka, group_category ...@@ -178,7 +178,7 @@ class GroupSet: # aka, group_category
""" """
class Group: class CanvasGroup:
def __init__(self, group): def __init__(self, group):
super().__init__() super().__init__()
self.canvas_group = group self.canvas_group = group
...@@ -261,7 +261,7 @@ class Group: ...@@ -261,7 +261,7 @@ class Group:
""" """
class Course: class CanvasCourse:
def __init__(self, course_id): def __init__(self, course_id):
self.canvas_course = CanvasSession.get_session().get_course(course_id) self.canvas_course = CanvasSession.get_session().get_course(course_id)
...@@ -290,19 +290,19 @@ class Course: ...@@ -290,19 +290,19 @@ class Course:
canvas_groups = self.canvas_course.get_groups() canvas_groups = self.canvas_course.get_groups()
groups = [] groups = []
for group in canvas_groups: for group in canvas_groups:
groups.append(Group(group)) groups.append(CanvasGroup(group))
return groups return groups
def get_group_sets(self): def get_group_sets(self):
canvas_group_categories = self.canvas_course.get_group_categories() canvas_group_categories = self.canvas_course.get_group_categories()
group_sets = [] group_sets = []
for group_category in canvas_group_categories: for group_category in canvas_group_categories:
group_sets.append(GroupSet(group_category)) group_sets.append(CanvasGroupSet(group_category))
return group_sets return group_sets
def create_groupset(self, groupset_name): def create_groupset(self, groupset_name):
group_category = self.canvas_course.create_group_category(groupset_name) group_category = self.canvas_course.create_group_category(groupset_name)
return GroupSet(group_category) return CanvasGroupSet(group_category)
def __repr__(self): def __repr__(self):
return f'{self.canvas_course.course_code}: {self.canvas_course.name}' return f'{self.canvas_course.course_code}: {self.canvas_course.name}'
......
from canvas_classes import CanvasUser from canvas_classes import CanvasUser
from canvas_classes import CanvasCourse
from gitlab_classes import GitlabUser from gitlab_classes import GitlabUser
from course import Course
import csv import csv
NO_PARTNERING_LIST_MAXIMUM = 10 NO_PARTNERING_LIST_MAXIMUM = 10
...@@ -13,7 +15,7 @@ class CompositeUser: ...@@ -13,7 +15,7 @@ class CompositeUser:
self.gitlab_user = None self.gitlab_user = None
self.sortable_name = student_dictionary['SortableName'] self.sortable_name = student_dictionary['SortableName']
self.readable_name = student_dictionary['ReadableName'] self.readable_name = student_dictionary['ReadableName']
self.NUID = student_dictionary['NUID'] self.NUID = int(student_dictionary['NUID'])
self.canvas_username = student_dictionary['CanvasUsername'] self.canvas_username = student_dictionary['CanvasUsername']
self.gitlab_username = student_dictionary['GitlabUsername'] self.gitlab_username = student_dictionary['GitlabUsername']
self.canvas_email = student_dictionary['CanvasEmail'] self.canvas_email = student_dictionary['CanvasEmail']
...@@ -28,12 +30,12 @@ class CompositeUser: ...@@ -28,12 +30,12 @@ class CompositeUser:
def get_canvas_user(self): def get_canvas_user(self):
if self.canvas_user is None: 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 return self.canvas_user
def get_gitlab_user(self): def get_gitlab_user(self):
if self.gitlab_user is None: if self.gitlab_user is None:
self.gitlab_user = GitlabUser(self.NUID) self.gitlab_user = GitlabUser(self.gitlab_username)
return self.gitlab_user return self.gitlab_user
def tentatively_pair_with(self, username): def tentatively_pair_with(self, username):
...@@ -52,10 +54,10 @@ class CompositeUser: ...@@ -52,10 +54,10 @@ class CompositeUser:
return len(self.blacklist) > 0 return len(self.blacklist) > 0
def is_blacklist_compatible(self, other): 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): 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): def __repr__(self):
if self.canvas_email == self.gitlab_email: if self.canvas_email == self.gitlab_email:
...@@ -72,6 +74,9 @@ class CompositeUser: ...@@ -72,6 +74,9 @@ class CompositeUser:
def __ne__(self, other): def __ne__(self, other):
return not self.__eq__(other) return not self.__eq__(other)
def __hash__(self) -> int:
return hash(self.canvas_username)
@staticmethod @staticmethod
def get_user(username_or_email): def get_user(username_or_email):
return CompositeUser.instances[username_or_email] return CompositeUser.instances[username_or_email]
...@@ -81,18 +86,22 @@ class CompositeUser: ...@@ -81,18 +86,22 @@ class CompositeUser:
students = set() students = set()
with open(filename, mode='r') as csv_file: with open(filename, mode='r') as csv_file:
csv_reader = csv.DictReader(csv_file) csv_reader = csv.DictReader(csv_file)
for student in csv_reader: for csv_student in csv_reader:
graylist = set() graylist = set()
blacklist = set() blacklist = set()
for count in range(NO_PARTNERING_LIST_MAXIMUM): for count in range(NO_PARTNERING_LIST_MAXIMUM):
former_partner = student[f'Graylist{count}'] former_partner = csv_student[f'Graylist{count}']
undesired_partner = student[f'Blacklist{count}'] undesired_partner = csv_student[f'Blacklist{count}']
if former_partner != "": if former_partner != "":
graylist.add(former_partner) graylist.add(former_partner)
if undesired_partner != "": if undesired_partner != "":
blacklist.add(undesired_partner) blacklist.add(undesired_partner)
student = CompositeUser(student, graylist, blacklist) student = CompositeUser(csv_student, graylist, blacklist)
students.add(student) 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 return students
@staticmethod @staticmethod
......
...@@ -55,7 +55,7 @@ class GitlabUser: ...@@ -55,7 +55,7 @@ class GitlabUser:
return not self.__eq__(other) return not self.__eq__(other)
class Issue: class GitlabIssue:
def __init__(self, issue): def __init__(self, issue):
""" """
Creates an Issue object, populating the backing git_issue instance with the appropriate gitlab.Issue object Creates an Issue object, populating the backing git_issue instance with the appropriate gitlab.Issue object
...@@ -168,7 +168,7 @@ class Issue: ...@@ -168,7 +168,7 @@ class Issue:
# subscribed # subscribed
class Project: class GitlabProject:
def __init__(self, project): def __init__(self, project):
""" """
Creates a Project object, populating the backing git_project instance with the appropriate gitlab.Project object Creates a Project object, populating the backing git_project instance with the appropriate gitlab.Project object
...@@ -196,7 +196,7 @@ class Project: ...@@ -196,7 +196,7 @@ class Project:
gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True) gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True)
projects = [] projects = []
for project in gitlab_projects: for project in gitlab_projects:
projects.append(Project(project)) projects.append(GitlabProject(project))
return projects return projects
@staticmethod @staticmethod
...@@ -204,17 +204,17 @@ class Project: ...@@ -204,17 +204,17 @@ class Project:
gitlab_projects = GitlabSession.get_session().projects.list(search=search_term, all=True) gitlab_projects = GitlabSession.get_session().projects.list(search=search_term, all=True)
projects = [] projects = []
for project in gitlab_projects: for project in gitlab_projects:
projects.append(Project(project)) projects.append(GitlabProject(project))
return projects return projects
@staticmethod @staticmethod
def create_project(project_name): 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 @staticmethod
def create_project_in_group(group_name, project_name): def create_project_in_group(group_name, project_name):
group_id = GitlabSession.get_session().groups.get(group_name).id 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): def get_project_id(self):
return self.git_project.id return self.git_project.id
...@@ -312,12 +312,12 @@ class Project: ...@@ -312,12 +312,12 @@ class Project:
gitlab_issues = self.git_project.issues.list(order_by='created_at', sort='asc', all=True) gitlab_issues = self.git_project.issues.list(order_by='created_at', sort='asc', all=True)
issues = [] issues = []
for issue in gitlab_issues: for issue in gitlab_issues:
issues.append(Issue(issue)) issues.append(GitlabIssue(issue))
return issues return issues
def create_issue(self, title, description): def create_issue(self, title, description):
gitlab_issue = self.git_project.issues.create({'title': title, 'description': description}) gitlab_issue = self.git_project.issues.create({'title': title, 'description': description})
return Issue(gitlab_issue) return GitlabIssue(gitlab_issue)
def __repr__(self): def __repr__(self):
return self.get_name_with_namespace() return self.get_name_with_namespace()
...@@ -373,7 +373,7 @@ class Project: ...@@ -373,7 +373,7 @@ class Project:
if __name__ == '__main__': if __name__ == '__main__':
namespace = 'csce_361/sandbox' 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:') print('All projects in sandbox:')
for test_project in test_projects: for test_project in test_projects:
print(test_project) print(test_project)
...@@ -391,7 +391,7 @@ if __name__ == '__main__': ...@@ -391,7 +391,7 @@ if __name__ == '__main__':
for test_issue in test_issues: for test_issue in test_issues:
creation = test_issue.get_created_at() creation = test_issue.get_created_at()
print(f'{test_issue}\tcreated at {creation}.') 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) number_of_projects = len(test_projects)
print(f'retrieved {number_of_projects} projects matching \'csce361-homework\'') print(f'retrieved {number_of_projects} projects matching \'csce361-homework\'')
start_date = datetime(2019, 8, 1, tzinfo=timezone.utc) start_date = datetime(2019, 8, 1, tzinfo=timezone.utc)
......
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
"""
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment