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

Team contribution grading can now consider all branches

parent 2849e2fd
Branches
No related tags found
No related merge requests found
...@@ -667,7 +667,6 @@ class GitlabProject: ...@@ -667,7 +667,6 @@ class GitlabProject:
branch_names: List[str] = list(map(lambda b: b.name, branches)) branch_names: List[str] = list(map(lambda b: b.name, branches))
return branch_names return branch_names
def get_labels(self) -> Set[str]: def get_labels(self) -> Set[str]:
""" """
:return: set of label names :return: set of label names
......
...@@ -31,7 +31,7 @@ if __name__ == '__main__': ...@@ -31,7 +31,7 @@ if __name__ == '__main__':
projects.sort(key=lambda p: p.get_name()) projects.sort(key=lambda p: p.get_name())
for project in projects: for project in projects:
print(f'\n\n\t>>>> {project} <<<<') print(f'\n\n\t>>>> {project} <<<<')
master_branch_commits = project.get_commits() master_branch_commits = list(sorted(project.get_commits(), key=lambda c: c.get_timestamp()))
print(f'{len(master_branch_commits)} commits on the master branch') print(f'{len(master_branch_commits)} commits on the master branch')
all_branches_commits: Set[GitlabCommit] = set() all_branches_commits: Set[GitlabCommit] = set()
for branch in project.get_branch_names(): for branch in project.get_branch_names():
......
...@@ -105,24 +105,32 @@ def _combine_typed_contributions(typed_sizes: Tuple[Dict[str, int]]) -> Dict[str ...@@ -105,24 +105,32 @@ def _combine_typed_contributions(typed_sizes: Tuple[Dict[str, int]]) -> Dict[str
return combined_typed_size return combined_typed_size
def display_git_contributions(project: GitlabProject): def display_git_contributions(project: GitlabProject, all_branches: bool = False):
# TODO: recognize that this only works for projects in namespace; will need to ask whether to retrieve project. # TODO: recognize that this only works for projects in namespace; will need to ask whether to retrieve project.
binary_like_text_files = {"css", "csv", "fxml", "html"} # TODO soft-code this binary_like_text_files = {"css", "csv", "fxml", "html"} # TODO soft-code this
# noinspection SpellCheckingInspection # noinspection SpellCheckingInspection
binary_files = {"docx", "gif", "jpg", "jpeg", "pdf", "png", "pptx", "svg", "xlsx"} # TODO this, too binary_files = {"docx", "gif", "jpg", "jpeg", "pdf", "png", "pptx", "svg", "xlsx"} # TODO this, too
project_branches = project.get_branch_names() if all_branches else ['master']
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
project_commits: List[GitlabCommit] master_commits: List[GitlabCommit]
project_commits: Set[GitlabCommit] = set()
if assignment_start_date == '': if assignment_start_date == '':
project_commits = project.get_commits() master_commits = project.get_commits()
else: else:
project_commits = project.get_commits(after_date=assignment_start_date) master_commits = project.get_commits(after_date=assignment_start_date)
contributions: Dict[str, int] = {} # TODO: also broaden to multiple branches? for branch in project_branches:
if assignment_start_date == '':
project_commits = project_commits.union(set(project.get_commits(branch_name=branch)))
else:
project_commits = project_commits.union(set(project.get_commits(branch_name=branch,
after_date=assignment_start_date)))
contributions: Dict[str, int] = {}
typed_contributions: Dict[str, Dict[str, int]] = {} typed_contributions: Dict[str, Dict[str, int]] = {}
timestamps: Dict[str, List[datetime]] = {} timestamps: Dict[str, List[datetime]] = {}
contributors: Set[Tuple[str, str]] = set() contributors: Set[Tuple[str, str]] = set()
# noinspection PyShadowingNames # noinspection PyShadowingNames
for commit in project_commits: for commit in master_commits:
if not commit.is_merge(): if not commit.is_merge() and not commit.is_revert():
author = commit.get_author() author = commit.get_author()
contributors.add((author['name'], author['email'])) contributors.add((author['name'], author['email']))
email = author['email'] # TODO: manage aliases email = author['email'] # TODO: manage aliases
...@@ -137,7 +145,7 @@ def display_git_contributions(project: GitlabProject): ...@@ -137,7 +145,7 @@ def display_git_contributions(project: GitlabProject):
typed_sizes: Tuple[Dict[str, int]] = (typed_contributions[email], typed_size) typed_sizes: Tuple[Dict[str, int]] = (typed_contributions[email], typed_size)
typed_contributions[email] = _combine_typed_contributions(typed_sizes) typed_contributions[email] = _combine_typed_contributions(typed_sizes)
timestamps[email].append(commit.get_timestamp()) timestamps[email].append(commit.get_timestamp())
print(f'Contributions by each partner to {project} :') print(f'Contributions by each partner to {project} on the master branch:')
for contribution in contributions: for contribution in contributions:
contributor = list(filter(lambda c: c[1] == contribution, contributors))[0] contributor = list(filter(lambda c: c[1] == contribution, contributors))[0]
email = contributor[1] email = contributor[1]
...@@ -154,6 +162,43 @@ def display_git_contributions(project: GitlabProject): ...@@ -154,6 +162,43 @@ def display_git_contributions(project: GitlabProject):
print(f'\t\tFirst commit: {timestamps[email][0]}') print(f'\t\tFirst commit: {timestamps[email][0]}')
print(f'\t\tMedian commit: {timestamps[email][floor(number_of_commits/2)]}') print(f'\t\tMedian commit: {timestamps[email][floor(number_of_commits/2)]}')
print(f'\t\tLast commit: {timestamps[email][-1]}') print(f'\t\tLast commit: {timestamps[email][-1]}')
if all_branches:
# noinspection PyShadowingNames
for commit in project_commits:
if commit not in master_commits:
if not commit.is_merge() and not commit.is_revert():
author = commit.get_author()
contributors.add((author['name'], author['email']))
email = author['email'] # TODO: manage aliases
size = commit.get_diff_size()
typed_size = commit.get_diff_size_by_filetype()
if email != 'bohn@unl.edu': # TODO: un-hard-code this -- may not be necessary with a starting date
if email not in contributions:
contributions[email] = 0
typed_contributions[email] = {}
timestamps[email] = []
contributions[email] += size
typed_sizes: Tuple[Dict[str, int]] = (typed_contributions[email], typed_size)
typed_contributions[email] = _combine_typed_contributions(typed_sizes)
timestamps[email].append(commit.get_timestamp())
print(f'Contributions by each partner to {project} on {len(project_branches)} branches:')
for contribution in contributions:
contributor = list(filter(lambda c: c[1] == contribution, contributors))[0]
email = contributor[1]
typed_contribution = typed_contributions[email]
timestamps[email].sort()
number_of_commits = len(timestamps[email])
print(f'\t{contributor}')
print(
f'\t{str(contributions[contribution]).rjust(5)} total line changes in '
f'{str(number_of_commits).rjust(3)} commits')
for filetype in sorted(typed_contribution.keys()):
change_type = 'file' if filetype in binary_files.union(binary_like_text_files) else 'line'
print(f'\t\t\t{filetype} {str(typed_contribution[filetype]).rjust(10-len(filetype))} '
f'{change_type} changes')
print(f'\t\tFirst commit: {timestamps[email][0]}')
print(f'\t\tMedian commit: {timestamps[email][floor(number_of_commits/2)]}')
print(f'\t\tLast commit: {timestamps[email][-1]}')
# noinspection PyUnusedLocal # noinspection PyUnusedLocal
...@@ -195,6 +240,9 @@ if __name__ == '__main__': ...@@ -195,6 +240,9 @@ if __name__ == '__main__':
if option is options[1]: if option is options[1]:
print('Which group?') print('Which group?')
student_groups = [select_from_list(student_groups, 'student group')] student_groups = [select_from_list(student_groups, 'student group')]
print('Are you examining the master branch only, or are you examining all branches?')
options = ['master branch only', 'all branches']
examine_all_branches: bool = select_from_list(options, 'option') is options[1]
zero_padding: int = floor(log10(len(projects))) + 1 zero_padding: int = floor(log10(len(projects))) + 1
assignment_start_date = get_assignment_start() # TODO: only need this if grading git histories assignment_start_date = get_assignment_start() # TODO: only need this if grading git histories
for student_group in student_groups: # TODO: Skip past graded groups for student_group in student_groups: # TODO: Skip past graded groups
...@@ -214,10 +262,11 @@ if __name__ == '__main__': ...@@ -214,10 +262,11 @@ if __name__ == '__main__':
print(f'Could not location repository {custom_project}; please confirm path.') print(f'Could not location repository {custom_project}; please confirm path.')
custom_project = None custom_project = None
if len(custom_project) > 0: if len(custom_project) > 0:
display_git_contributions(repository) display_git_contributions(repository, examine_all_branches)
else: else:
project_name = f'{project_prefix}{student_group.get_name().split()[1]}'.zfill(zero_padding) project_name = f'{project_prefix}{student_group.get_name().split()[1]}'.zfill(zero_padding)
display_git_contributions(list(filter(lambda p: p.get_name() == project_name, projects))[0]) display_git_contributions(list(filter(lambda p: p.get_name() == project_name, projects))[0],
examine_all_branches)
if grading_peer_reviews: if grading_peer_reviews:
display_peer_reviews(peer_review_assignment, student_group.get_students()) display_peer_reviews(peer_review_assignment, student_group.get_students())
# TODO: Ask if you want to grade (keep track of groups that you don't grade) # TODO: Ask if you want to grade (keep track of groups that you don't grade)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment