Select Git revision
composite_user.py
-
Christopher Bohn authoredChristopher Bohn authored
composite_user.py 8.96 KiB
import csv
from typing import ClassVar, Collection, Dict, List, Set
from api.canvas_classes import CanvasUser
from api.canvas_classes import CanvasCourse
from api.gitlab_classes import GitlabUser
from course import Course
NO_PARTNERING_LIST_MAXIMUM = 10
class CompositeUser:
canvas_user: CanvasUser
gitlab_user: GitlabUser
sortable_name: str
readable_name: str
NUID: int
canvas_username: str
gitlab_username: str
canvas_email: str
gitlab_email: str
graylist: Dict[str, Set[str]]
blacklist: Set[str]
candidate_teammates: Set[str]
# assignments: ClassVar[List[str]] = [] # TODO: make use of this!!
instances: ClassVar[Dict[str, "CompositeUser"]] = {}
basic_fields = ['SortableName', 'ReadableName', 'NUID',
'CanvasUsername', 'CanvasEmail',
'GitlabUsername', 'GitlabEmail', 'Blacklist']
def __init__(self, student_dictionary: Dict[str, str], graylist: Dict[str, Set[str]], blacklist: Collection[str]):
self.canvas_user: CanvasUser = None
self.gitlab_user: GitlabUser = None
self.sortable_name = student_dictionary['SortableName']
self.readable_name = student_dictionary['ReadableName']
self.NUID = int(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 = graylist
self.blacklist = set(blacklist)
self.candidate_teammates: Set[str] = None
CompositeUser.instances[self.canvas_email] = self
CompositeUser.instances[self.gitlab_email] = self
CompositeUser.instances[self.canvas_username] = self
CompositeUser.instances[self.gitlab_username] = self
@classmethod
def initialize_composite_user(cls, canvas_student: CanvasUser, gitlab_student: GitlabUser) -> "CompositeUser":
student: Dict[str, str] = {'SortableName': canvas_student.get_sortable_name(),
'ReadableName': canvas_student.get_name(),
'NUID': str(canvas_student.get_nuid()),
'CanvasUsername': canvas_student.get_username(),
'CanvasEmail': canvas_student.get_email(),
'GitlabUsername': gitlab_student.get_username(),
'GitlabEmail': gitlab_student.get_email()}
return CompositeUser(student, {}, set())
def get_name(self) -> str:
return self.readable_name
def get_canvas_username(self) -> str:
return self.canvas_username
def get_canvas_user(self) -> CanvasUser:
if self.canvas_user is None:
# self.canvas_user = CanvasUser(self.NUID) # n.b., can retrieve own user but not arbitrary user
all_students: List[CanvasUser] = CanvasCourse(Course.canvas_course_id).get_students()
self.canvas_user = list(filter(lambda s: s.get_username() == self.canvas_username, all_students))[0]
# this still seems to be a teensy bit flaky
return self.canvas_user
def get_gitlab_user(self) -> GitlabUser:
if self.gitlab_user is None:
self.gitlab_user = GitlabUser(self.gitlab_username)
return self.gitlab_user
def set_gitlab_email(self, email: str) -> None:
self.gitlab_email = email
def assign_partners(self, assignment: str, partners: Collection[str]):
self.graylist[assignment] = set(partners)
def discard_team(self) -> None:
self.candidate_teammates = None
def has_blacklist(self) -> bool:
return len(self.blacklist) > 0
def is_blacklist_compatible(self, other: "CompositeUser") -> bool:
return other.get_canvas_user().get_name() not in self.blacklist and \
self.get_canvas_user().get_name() not in other.blacklist
def is_graylist_compatible(self, other: "CompositeUser") -> bool:
my_past_partners: Set[str] = set()
your_past_partners: Set[str] = set()
for assignment in self.graylist:
if isinstance(self.graylist[assignment], set):
my_past_partners.union(self.graylist[assignment])
elif isinstance(self.graylist[assignment], str):
my_past_partners.add(str(self.graylist[assignment]))
else:
print(f'Weird. {self}\'s partners for {assignment} '
f'are recorded as a {self.graylist[assignment].__class__}')
for assignment in other.graylist:
if isinstance(other.graylist[assignment], set):
your_past_partners.union(other.graylist[assignment])
elif isinstance(other.graylist[assignment], str):
your_past_partners.add(str(other.graylist[assignment]))
else:
print(f'Weird. {other}\'s partners for {assignment} '
f'are recorded as a {other.graylist[assignment].__class__}')
i_am_okay = other.get_canvas_user().get_username() not in my_past_partners
you_are_okay = self.get_canvas_user().get_username() not in your_past_partners
return i_am_okay and you_are_okay
def __repr__(self) -> str:
if self.canvas_email == self.gitlab_email:
return f'{self.readable_name}, gitlab @{self.gitlab_username}, email <{self.canvas_email}>'
else:
return f'{self.readable_name}, gitlab @{self.gitlab_username},' \
f' email <{self.canvas_email}> <{self.gitlab_email}>'
def __eq__(self, other: "CompositeUser") -> bool:
# if isinstance(other, CompositeUser):
return self.canvas_username == other.canvas_username
# else:
# return False
def __ne__(self, other: "CompositeUser") -> bool:
return not self.__eq__(other)
def __hash__(self) -> int:
return hash(self.canvas_username)
@classmethod
def get_user(cls, username_or_email: str) -> "CompositeUser":
return cls.instances[username_or_email]
@staticmethod
def set_to_string(the_set: Set[str]) -> str:
the_string = ''
for word in the_set:
the_string += ' ' + word
return the_string.strip()
@staticmethod
def string_to_set(the_string: str) -> Set[str]:
return set(the_string.split())
def to_dict(self, assignments) -> Dict[str, str]:
student_dictionary = {'SortableName': self.sortable_name.strip(),
'ReadableName': self.readable_name.strip(),
'NUID': self.NUID,
'CanvasUsername': self.canvas_username,
'CanvasEmail': self.canvas_email,
'GitlabUsername': self.gitlab_username,
'GitlabEmail': self.gitlab_email,
'Blacklist': CompositeUser.set_to_string(self.blacklist)}
for assignment in assignments:
student_dictionary[assignment] = CompositeUser.set_to_string(self.graylist[assignment])
return student_dictionary
# TODO: add CompositeUser.from_dict(Dict[str, str]) -> CompositeUser
@staticmethod
def read_student_csv(filename: str) -> Set["CompositeUser"]:
students: Set[CompositeUser] = set()
with open(filename, mode='r') as csv_file:
csv_reader = csv.DictReader(csv_file)
for csv_student in csv_reader:
blacklist: Set[str] = CompositeUser.string_to_set(csv_student['Blacklist'])
graylist: Dict[str, Set[str]] = {}
handled_fields = set(CompositeUser.basic_fields)
for field in set(csv_student.keys()) - handled_fields:
graylist[field] = CompositeUser.string_to_set(csv_student[field])
student = CompositeUser(csv_student, graylist, blacklist)
students.add(student)
canvas_students: List[CanvasUser] = 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
def write_student_csv(students: Set["CompositeUser"], filename: str) -> None:
fieldnames = list(CompositeUser.basic_fields)
random_student = students.pop()
student_assignments = random_student.graylist.keys()
students.add(random_student)
fieldnames.extend(sorted(student_assignments))
with open(filename, mode='w') as csv_file:
# fieldnames.extend(CompositeUser.assignments)
writer = csv.DictWriter(csv_file, fieldnames=fieldnames)
writer.writeheader()
for student in list(sorted(students, key=lambda s: s.sortable_name)):
writer.writerow(student.to_dict(student_assignments))