diff --git a/Timeline.py b/Timeline.py new file mode 100644 index 0000000000000000000000000000000000000000..411cfa985bb0ca4dc55bc5f8d8bb303c3e945bbb --- /dev/null +++ b/Timeline.py @@ -0,0 +1,77 @@ +import json +from pathlib import Path + +from api.gitlab_classes import * +from course import Course + + +class Timeline: + # noinspection PyShadowingNames + def __init__(self, project, filename): + self.project = project + self.save_file = filename + if Path(self.save_file).exists(): + with open(self.save_file, mode='r') as json_file: + this_dict = json.load(json_file) + self.start_time = this_dict['start_time'] + self.issues = this_dict['issues'] + self.observational_periods = this_dict['observational_periods'] + else: + self.project = project + # self.commits = project.get_commits() # we'll worry about commits later + self.start_time = datetime.now().isoformat() + self.observational_periods = [] + self.issues = [] + self.observational_periods.append({'from': datetime.now().isoformat(), 'to': datetime.now().isoformat()}) + self.issues = list(map(lambda issue: self.update_issue_timeline(issue), project.get_issues())) + + def save(self): + this_dict = {'project': self.project.get_path_with_namespace(), + 'issues': self.issues, + 'start_time': self.start_time, + 'observational_periods': self.observational_periods + } + path = Path(self.save_file) + backup_path = Path(f'{self.save_file}.bk') + if path.exists(): + path.rename(backup_path) + with open(self.save_file, mode='w') as json_file: + json.dump(this_dict, json_file, indent=4) + + def update_issue_timeline(self, issue): + possible_issue_timeline = list(filter(lambda i: i['number'] == issue.get_project_issue_id(), self.issues)) + if len(possible_issue_timeline) == 0: + issue_timeline = {'number': issue.get_project_issue_id(), + 'opened': issue.get_created_at().isoformat(), + 'events': []} + old_labels = set() + else: + issue_timeline = possible_issue_timeline[0] + old_labels = set(issue_timeline['current_labels']) + new_labels = issue.get_labels() + all_labels = old_labels.union(new_labels) + for label in all_labels: + if label in old_labels.difference(new_labels): + issue_timeline['events'].append(('Remove Label', label, datetime.now().isoformat())) + if label in new_labels.difference(old_labels): + issue_timeline['events'].append(('Add Label', label, datetime.now().isoformat())) + issue_timeline['current_labels'] = list(issue.get_labels()) + if issue.get_closed_at() is not None: + issue_timeline['closed'] = issue.get_closed_at().isoformat() + else: + issue_timeline['closed'] = None + return issue_timeline + + +if __name__ == '__main__': + # A handy project to work from for now + projects = GitlabProject.get_projects_by_group(Course.gitlab_namespace) + project = list(filter(lambda p: p.get_name() == 'Chess 4', projects))[0] + print(project) + timeline = Timeline(project, 'chess4.json') + # print(len(project.get_commits(branch_name = 'staging'))) + # print(len(project.git_project.commits.list(all=True))) + timeline.save() + new_timeline = Timeline(project, 'chess4.json') + print(new_timeline.project) + new_timeline.save() diff --git a/api/gitlab_classes.py b/api/gitlab_classes.py index 4c2fd4ef2e4362509437fee8bceeed60f71e23fa..9b12b76265d8a629b7909bad5e40a1ec7c0449b3 100644 --- a/api/gitlab_classes.py +++ b/api/gitlab_classes.py @@ -1,6 +1,7 @@ from datetime import datetime -from datetime import timezone + import gitlab + from config import Config @@ -22,9 +23,9 @@ class GitlabUser: the username """ super().__init__() - if isinstance(user, int): # by user id + if isinstance(user, int): # by user id self.git_user = GitlabSession.get_session().users.get(user) - elif isinstance(user, str): # by username + elif isinstance(user, str): # by username self.git_user = GitlabSession.get_session().users.list(username=user)[0] else: self.git_user = user @@ -99,7 +100,7 @@ class GitlabIssue: :return: an "aware" datetime object representing the creation date/time """ created_at = self.git_issue.created_at - if created_at[-1] in ('z', 'Z'): # Didn't encounter this problem with created_at + if created_at[-1] in ('z', 'Z'): # Didn't encounter this problem with created_at created_at = created_at[:-1] + '+00:00' return datetime.fromisoformat(created_at) @@ -108,7 +109,7 @@ class GitlabIssue: :return: an "aware" datetime object representing the last date/time the issue was updated """ updated_at = self.git_issue.updated_at - if updated_at[-1] in ('z', 'Z'): # Didn't encounter this problem with updated_at + if updated_at[-1] in ('z', 'Z'): # Didn't encounter this problem with updated_at updated_at = updated_at[:-1] + '+00:00' return datetime.fromisoformat(updated_at) @@ -121,15 +122,16 @@ class GitlabIssue: if closed_at is None: return None else: - if closed_at[-1] in ('z', 'Z'): # Did encounter this problem with closed_at + if closed_at[-1] in ('z', 'Z'): # Did encounter this problem with closed_at closed_at = closed_at[:-1] + '+00:00' return datetime.fromisoformat(closed_at) def get_labels(self): """ - :return: list of labels + :return: set of label names """ - return self.git_issue.labels.list(all=True) + return set(self.git_issue.labels) + # return self.git_issue.labels.list(all=True) def get_page(self): """ @@ -169,6 +171,7 @@ class GitlabIssue: class GitlabCommit: + # noinspection PyShadowingNames def __init__(self, commit): super().__init__() self.gitlab_commit = commit @@ -185,6 +188,7 @@ class GitlabCommit: def is_merge(self): return len(self.gitlab_commit.parent_ids) > 1 + # noinspection PyShadowingNames def get_diffs(self): diffs = [] gitlab_diffs = self.gitlab_commit.diff() @@ -193,6 +197,7 @@ class GitlabCommit: '+': diff['diff'].count('\n+'), '-': diff['diff'].count('\n-')}) return diffs + # noinspection PyShadowingNames def get_diff_size(self): insertions = 0 deletions = 0 @@ -231,9 +236,9 @@ class GitlabProject: containing the project's path (namespace and name, such as 'csce_361/sandbox/HelloWorld') """ super().__init__() - if isinstance(project, int): # by project id + if isinstance(project, int): # by project id self.git_project = GitlabSession.get_session().projects.get(project) - elif isinstance(project, str): # by path + elif isinstance(project, str): # by path self.git_project = GitlabSession.get_session().projects.get(project) else: # self.git_project = project # for some reason, many attributes (including members) might be lost @@ -245,7 +250,7 @@ class GitlabProject: :param group: must be either an integer representing the group ID, or a string containing the group's namespace :return: list of projects in the specified group """ - if isinstance(group, int): # by group id + if isinstance(group, int): # by group id gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True) else: # isinstance(group, str): # by path gitlab_projects = GitlabSession.get_session().groups.get(group).projects.list(all=True) @@ -375,10 +380,11 @@ class GitlabProject: gitlab_issue = self.git_project.issues.create({'title': title, 'description': description}) return GitlabIssue(gitlab_issue) - def get_commits(self, branch_name = '', after_date ='1970-01-01', before_date ='9999-12-31'): + # noinspection PyShadowingNames + def get_commits(self, branch_name='', after_date='1970-01-01', before_date='9999-12-31'): """ :param branch_name: the branch to retrieve commits from; if an empty string (default) then retrieves commits - from all branches + from all branches <-- NO, RETRIEVES FROM DEFAULT BRANCH; WILL NEED TO FIX THAT #TODO :param after_date: the earliest date of any retrieved commit; if '1970-01-01' (Unix epoch) then treated as having no earliest-bound :param before_date: the latest date of any retrieved commit; if '9999-12-31' (Y10K problem) then treated a @@ -401,6 +407,12 @@ class GitlabProject: commits.append(GitlabCommit(commit)) return commits + def get_labels(self): + """ + :return: set of label names + """ + return set(map(lambda label: label.name, self.git_project.labels.list())) + def __repr__(self): return self.get_name_with_namespace() diff --git a/prep_assignment.py b/prep_assignment.py index f9d5ab4830aa1dedaef09d871b72a85f8cf29308..bfa6094d64e9ec86241b76927854005086f190f7 100644 --- a/prep_assignment.py +++ b/prep_assignment.py @@ -81,7 +81,7 @@ def create_groups(assignment_number, student_pairs): if __name__ == '__main__': - assignment = '21b' + assignment = '28' pairs = create_pairs('2019-08.csv') save_pairs(assignment, pairs) print('Pairs created') @@ -94,3 +94,25 @@ if __name__ == '__main__': print('TODO:\tAdd issues') print('\tCommit starter code') print('\tUpdate graylists (also, please update the code to update the graylists)') + +""" +had a graylist violation on 28pair 4 + +encountered this on 28pair 18: + + Traceback (most recent call last): + File "/Users/cabohn/courses/csce361/scripts/prep_assignment.py", line 91, in <module> + create_groups(assignment, pairs) + File "/Users/cabohn/courses/csce361/scripts/prep_assignment.py", line 79, in create_groups + group.add_student(pair[1].get_canvas_user()) + File "/Users/cabohn/courses/csce361/scripts/api/composite_user.py", line 33, in get_canvas_user + self.canvas_user = CanvasUser(self.NUID) # n.b., can retrieve own user but not arbitrary user + File "/Users/cabohn/courses/csce361/scripts/api/canvas_classes.py", line 22, in __init__ + self.canvas_user = CanvasSession.get_session().get_user(user, 'sis_user_id') + File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/canvasapi/canvas.py", line 1110, in get_user + response = self.__requester.request("GET", uri) + File "/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/canvasapi/requester.py", line 227, in request + raise Unauthorized(response.json()) + canvasapi.exceptions.Unauthorized: [{'message': 'user not authorized to perform that action'}] + +""" \ No newline at end of file