diff --git a/api/gitlab_classes.py b/api/gitlab_classes.py index 78906d7b347b5adc0e8428b35b791ab9b556ba02..4c2fd4ef2e4362509437fee8bceeed60f71e23fa 100644 --- a/api/gitlab_classes.py +++ b/api/gitlab_classes.py @@ -168,6 +168,61 @@ class GitlabIssue: # subscribed +class GitlabCommit: + def __init__(self, commit): + super().__init__() + self.gitlab_commit = commit + + def get_author(self): + return {'name': self.gitlab_commit.author_name, 'email': self.gitlab_commit.author_email} + + def get_timestamp(self): + return self.gitlab_commit.created_at + + def get_message(self): + return self.gitlab_commit.message + + def is_merge(self): + return len(self.gitlab_commit.parent_ids) > 1 + + def get_diffs(self): + diffs = [] + gitlab_diffs = self.gitlab_commit.diff() + for diff in gitlab_diffs: + diffs.append({'file': diff['new_path'], 'text': diff['diff'], + '+': diff['diff'].count('\n+'), '-': diff['diff'].count('\n-')}) + return diffs + + def get_diff_size(self): + insertions = 0 + deletions = 0 + if not self.is_merge(): + for diff in self.get_diffs(): + insertions += diff['+'] + deletions += diff['-'] + return max(insertions, deletions) + + # git_commit fields: + # comments + # discussions + # manager + # statuses + # attributes: + # id + # short_id + # created_at + # parent_ids + # title + # message + # author_name + # author_email + # authored_date + # committer_name + # committer_email + # committed_date + # project_id + + class GitlabProject: def __init__(self, project): """ @@ -320,6 +375,32 @@ 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'): + """ + :param branch_name: the branch to retrieve commits from; if an empty string (default) then retrieves commits + from all branches + :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 + having no latest-bound + :return: List of Commit objects representing the project's commits that meet the specified constraints + """ + filters = {} + if branch_name != '': + filters['ref_name'] = branch_name + if after_date != '1970-01-01': + filters['since'] = after_date + if before_date != '9999-12-31': + filters['until'] = before_date + if len(filters) == 0: + gitlab_commits = self.git_project.commits.list(all=True) + else: + gitlab_commits = self.git_project.commits.list(all=True, query_parameters=filters) + commits = [] + for commit in gitlab_commits: + commits.append(GitlabCommit(commit)) + return commits + def __repr__(self): return self.get_name_with_namespace() @@ -378,6 +459,18 @@ if __name__ == '__main__': print('All projects in sandbox:') for test_project in test_projects: print(test_project) + print('Selecting last project. Here are the commits:') + test_project = test_projects[-1] + commits = test_project.get_commits() + diff = commits[-5].get_diffs() + print(diff) + print(commits[0].get_author()) + for commit in commits: + print(commit.get_message()) + print(f'is a merge? {commit.is_merge()}') + print(f'size: {commit.get_diff_size()}') + print() + """ print('Selecting second project. Here are the members:') test_project = test_projects[1] members = test_project.get_users() @@ -401,3 +494,4 @@ if __name__ == '__main__': print(f'after culling, there are {new_number_of_projects} projects that were created in/after August 2019') print(f'including {test_projects[0]} created by {test_projects[0].get_creator()} at ' f'{test_projects[0].get_created_at()}.') + """ diff --git a/grade_team_contribution.py b/grade_team_contribution.py index 403d7efb3be58f195479b24c980c7684528bde65..ab61cb45bed607479c4b055be2ddd31147a7b208 100644 --- a/grade_team_contribution.py +++ b/grade_team_contribution.py @@ -1,6 +1,7 @@ import textwrap from api.canvas_classes import * +from api.gitlab_classes import * from course import Course @@ -34,28 +35,40 @@ def display_peer_reviews(assignment, students): print(f'\t\t{line}'.expandtabs(4)) -def display_git_contributions(): +def get_project_prefix(canvas_groups): + name_segments = canvas_groups[0].get_name().split() + prefix = input(f'What is the prefix of the gitlab project names? [{name_segments[0]}] ') + if prefix == '': + prefix = name_segments[0] + return prefix + + +def display_git_contributions(project): print('Review git contributions offline') +# TODO: recognize that this only works for projects in namespace; will need to ask whether project should be retrieved. def grade(assignment1, assignment2, students): - pass + print('Enter grades through Canvas gradebook') if __name__ == '__main__': course = CanvasCourse(Course.canvas_course_id) - print('First, select the Canvas assignment to review and grade.\n') + projects = GitlabProject.get_projects_by_group(Course.gitlab_namespace) + print('First, select the "peer review" assignment to review and grade.\n') assignment_groups = course.get_assignment_groups() assignment_group = select_from_list(assignment_groups, 'assignment group') print() assignments = assignment_group.get_assignments() peer_review_assignment = select_from_list(assignments, 'assignment') print(f'\nSelected {peer_review_assignment}.') + # TODO: select second assignment (git history) print('Now select the student groupset with the teams.\n') student_groupsets = course.get_user_groupsets() student_groupset = select_from_list(student_groupsets, 'groupset') print(f'\nSelected {student_groupset}.\n') student_groups = student_groupset.get_groups() + project_prefix = get_project_prefix(student_groups) print('Are you grading all groups, or are you revisiting a specific group?') options = ['All groups', 'Specific group'] option = select_from_list(options, 'option') @@ -65,7 +78,8 @@ if __name__ == '__main__': for student_group in student_groups: input(f'\n\nPress any key to grade {student_group}') display_peer_reviews(peer_review_assignment, student_group.get_students()) - display_git_contributions() + project_name = f'{project_prefix}{student_group.get_name().split()[1]}' + display_git_contributions(list(filter(lambda p: p.get_name() == project_name, projects))[0]) # TODO: Ask if you want to grade (keep track of groups that you don't grade) if True: grade(peer_review_assignment, None, student_group.get_students())