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

added flexibility to grade peer review, git history, both, or neither

parent 674ddb4b
Branches
No related tags found
No related merge requests found
...@@ -432,7 +432,6 @@ class GitlabProject: ...@@ -432,7 +432,6 @@ class GitlabProject:
commits.append(GitlabCommit(commit)) commits.append(GitlabCommit(commit))
return commits return commits
def get_labels(self) -> Set[str]: def get_labels(self) -> Set[str]:
""" """
:return: set of label names :return: set of label names
......
...@@ -4,12 +4,31 @@ from typing import List, Optional, TypeVar ...@@ -4,12 +4,31 @@ from typing import List, Optional, TypeVar
ChoiceType = TypeVar("ChoiceType") ChoiceType = TypeVar("ChoiceType")
def select_from_list(choices: List[ChoiceType], choice_name: str) -> ChoiceType: def select_from_list(choices: List[ChoiceType], choice_name: str, none_is_option: bool = False) -> ChoiceType:
query_user = True
return_value: ChoiceType = None
while query_user:
print(f'Choose the {choice_name} from this list:') print(f'Choose the {choice_name} from this list:')
for i in range(len(choices)): number_of_choices = len(choices)
for i in range(number_of_choices):
print(f'{i+1})\t{choices[i]}'.expandtabs(4)) print(f'{i+1})\t{choices[i]}'.expandtabs(4))
if none_is_option:
print('0)\tNone of the above'.expandtabs(4))
selection = input('Enter selection: ') selection = input('Enter selection: ')
return choices[int(selection) - 1] try:
selection_value = int(selection)
if none_is_option and selection_value == 0:
return_value = None
query_user = False
elif selection_value < 1 or selection_value > number_of_choices:
raise ValueError('Selection out of range.')
else:
return_value = choices[selection_value - 1]
query_user = False
except ValueError:
print(f'\tSelection must be an integer between {0 if none_is_option else 1} '
f'and {number_of_choices}, inclusive!')
return return_value
def strip_html(text: Optional[str]) -> Optional[str]: def strip_html(text: Optional[str]) -> Optional[str]:
......
import textwrap import textwrap
from datetime import date
from math import ceil, log10 from math import ceil, log10
from typing import Tuple from typing import Tuple
...@@ -7,6 +8,58 @@ from api.gitlab_classes import * ...@@ -7,6 +8,58 @@ from api.gitlab_classes import *
from common_functions import select_from_list, strip_html from common_functions import select_from_list, strip_html
from course import Course from course import Course
assignment_start_date: str = ''
def get_assignment_start():
input_is_invalid: bool = True
user_input: str = ''
while input_is_invalid:
user_input = input(
'Enter the assignment start date (yyyy-mm-dd or yyyymmdd) or hit Enter to consider all commits: ')
# validate input
if len(user_input) == 8:
user_input = f'{user_input[:4]}-{user_input[4:6]}-{user_input[-2:]}'
if len(user_input) == 10:
if user_input[:4].isnumeric() and user_input[4] == '-' and user_input[5:7].isnumeric() and \
user_input[-3] == '-' and user_input[-2:].isnumeric():
year = int(user_input[:4])
month = int(user_input[5:7])
day = int(user_input[-2:])
if year < 1946:
print('\tThe year must be within modern the era of computing.')
elif year < date.today().year:
if 1 <= month <= 12:
if 1 <= day <= 31: # not worth my time to validate 28/29/30-day months
input_is_invalid = False
else:
print('\tThe day must be between 01 and 31, inclusive.')
else:
print('\tThe month must be between 01 and 12, inclusive.')
elif year == date.today().year:
if month < 1:
print('\tThe month must be between 01 and 12, inclusive.')
elif month < date.today().month:
input_is_invalid = False
elif month == date.today().month:
if day < 1:
print('\tThe day must be between 01 and 31, inclusive.')
elif day <= date.today().day:
input_is_invalid = False
else:
print('\tThe date cannot be later than today.')
else:
print('\tThe date cannot be later than today.')
else:
print('\tThe date cannot be later than today.')
else:
print('\tValidation failed due to non-numeric input.')
elif len(user_input) == 0:
input_is_invalid = False
else:
print('\tValidation failed due to incorrect number of characters.')
return user_input
def structure_text(text: str) -> List[str]: def structure_text(text: str) -> List[str]:
return textwrap.wrap(strip_html(text)) return textwrap.wrap(strip_html(text))
...@@ -31,7 +84,8 @@ def display_peer_reviews(assignment, students): ...@@ -31,7 +84,8 @@ def display_peer_reviews(assignment, students):
def get_project_prefix(canvas_groups): def get_project_prefix(canvas_groups):
name_segments = canvas_groups[0].get_name().split() name_segments = canvas_groups[0].get_name().split()
prefix = input(f'What is the prefix of the gitlab project names? [{name_segments[0]}] ') prefix = input(f'What is the prefix of the gitlab project names (enter * if no common prefix exists)? '
f'[{name_segments[0]}] ')
if prefix == '': if prefix == '':
prefix = name_segments[0] prefix = name_segments[0]
return prefix return prefix
...@@ -39,11 +93,16 @@ def get_project_prefix(canvas_groups): ...@@ -39,11 +93,16 @@ def get_project_prefix(canvas_groups):
def display_git_contributions(project: GitlabProject): def display_git_contributions(project: GitlabProject):
# 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.
commits: List[GitlabCommit] = project.get_commits() # TODO: narrow the selection # noinspection PyUnusedLocal
project_commits: List[GitlabCommit]
if assignment_start_date == '':
project_commits = project.get_commits()
else:
project_commits = project.get_commits(after_date=assignment_start_date)
contributions: Dict[str, int] = {} # TODO: also broaden to multiple branches? contributions: Dict[str, int] = {} # TODO: also broaden to multiple branches?
contributors: Set[Tuple[str, str]] = set() contributors: Set[Tuple[str, str]] = set()
# noinspection PyShadowingNames # noinspection PyShadowingNames
for commit in commits: for commit in project_commits:
if not commit.is_merge(): if not commit.is_merge():
author = commit.get_author() author = commit.get_author()
contributors.add((author['name'], author['email'])) contributors.add((author['name'], author['email']))
...@@ -59,27 +118,39 @@ def display_git_contributions(project: GitlabProject): ...@@ -59,27 +118,39 @@ def display_git_contributions(project: GitlabProject):
print(f'\t{str(contributions[contribution]).rjust(5)}\t{contributor}') print(f'\t{str(contributions[contribution]).rjust(5)}\t{contributor}')
# noinspection PyUnusedLocal
def grade(assignment1, assignment2, students): def grade(assignment1, assignment2, students):
print('Enter grades through Canvas gradebook') print('Enter grades through Canvas gradebook')
if __name__ == '__main__': if __name__ == '__main__':
print('Establishing connections to Canvas and Gitlab...')
course = CanvasCourse(Course.canvas_course_id) course = CanvasCourse(Course.canvas_course_id)
projects = GitlabProject.get_projects_by_group(Course.gitlab_namespace) projects = GitlabProject.get_projects_by_group(Course.gitlab_namespace)
print('First, select the "peer review" assignment to review and grade.\n') print('Connections established.')
print('First, select the "peer review" assignment to review and grade (select 0 if not grading peer reviews).\n')
assignment_groups = course.get_assignment_groups() assignment_groups = course.get_assignment_groups()
assignment_group = select_from_list(assignment_groups, 'assignment group') assignment_group = select_from_list(assignment_groups, 'assignment group', True)
grading_peer_reviews = assignment_group != 0
print() print()
peer_review_assignment = None
if grading_peer_reviews:
assignments = assignment_group.get_assignments() assignments = assignment_group.get_assignments()
peer_review_assignment = select_from_list(assignments, 'assignment') peer_review_assignment = select_from_list(assignments, 'assignment')
print(f'\nSelected {peer_review_assignment}.') print(f'\nSelected {peer_review_assignment}.')
# TODO: select second assignment (git history) # TODO: select second assignment (git history)
print('Are you grading git histories?')
options = ['Yes', 'No']
option = select_from_list(options, 'option')
grading_git_histories = option == 1
print()
print('Now select the student groupset with the teams.\n') print('Now select the student groupset with the teams.\n')
student_groupsets = course.get_user_groupsets() student_groupsets = course.get_user_groupsets()
student_groupset = select_from_list(student_groupsets, 'groupset') student_groupset = select_from_list(student_groupsets, 'groupset')
print(f'\nSelected {student_groupset}.\n') print(f'\nSelected {student_groupset}.\n')
student_groups = student_groupset.get_groups() student_groups = student_groupset.get_groups()
project_prefix = get_project_prefix(student_groups) project_prefix = get_project_prefix(student_groups)
no_common_prefix: bool = True if project_prefix == '*' else False
print('Are you grading all groups, or are you revisiting a specific group?') print('Are you grading all groups, or are you revisiting a specific group?')
options = ['All groups', 'Specific group'] options = ['All groups', 'Specific group']
option = select_from_list(options, 'option') option = select_from_list(options, 'option')
...@@ -87,11 +158,18 @@ if __name__ == '__main__': ...@@ -87,11 +158,18 @@ if __name__ == '__main__':
print('Which group?') print('Which group?')
student_groups = [select_from_list(student_groups, 'student group')] student_groups = [select_from_list(student_groups, 'student group')]
zero_padding: int = ceil(log10(len(projects))) zero_padding: int = ceil(log10(len(projects)))
assignment_start_date = get_assignment_start()
for student_group in student_groups: # TODO: Skip past graded groups for student_group in student_groups: # TODO: Skip past graded groups
input(f'\n\nPress Enter to grade {student_group}') input(f'\n\nPress Enter to grade {student_group}')
print() print()
if grading_git_histories:
if no_common_prefix:
custom_project = input(f'Enter path to {student_group.get_name()}\'s repository: ')
display_git_contributions(GitlabProject(custom_project))
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])
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)
if True: if True:
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment