Skip to content
Snippets Groups Projects
Select Git revision
  • c488cdac2a513375f9350fd8a0466b056a8e5fee
  • master default protected
2 results

composite_user.py

Blame
  • 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))