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

accumulated updates

parent b8be7104
No related branches found
No related tags found
No related merge requests found
import calendar
import csv
import time
from typing import Dict, List, Set, Union
def get_log_entry(line: str) -> Dict[str, Union[time.struct_time, str, int]]:
log_entry: Dict[str, Union[time.struct_time, str, int]] = {}
fields: List[str] = line.split('|')
log_entry['timestamp'] = time.strptime(fields[1])
log_entry['username'] = fields[2]
bomb_info: List[str] = fields[-1].split(':')
log_entry['bomb'] = int(bomb_info[0])
log_entry['result'] = bomb_info[1]
log_entry['phase'] = int(bomb_info[2])
return log_entry
def parse_log(filename: str) -> List[Dict[str, Union[time.struct_time, str, int]]]:
log_entries: List[Dict[str, Union[time.struct_time, str, int]]] = []
with open(filename, mode='r') as log_file:
for line in log_file:
log_entry: Dict[str, Union[time.struct_time, str, int]] = get_log_entry(line)
log_entries.append(log_entry)
return log_entries
def parse_status(filename: str) -> List[Dict[str, Union[time.struct_time, str, int]]]:
status_entries: List[Dict[str, Union[time.struct_time, str, int]]] = []
with open(filename, mode='r') as status_file:
for line in status_file:
if 'Sent bomb' in line:
status_entry: Dict[str, Union[time.struct_time, str, int]] = {}
fields: List[str] = line.split(':bomblab-requestd.pl:')
status_entry['timestamp'] = time.strptime(fields[0])
status_entry['bomb'] = int(fields[1].split(' ')[2])
status_entry['username'] = fields[1].split(':')[1]
status_entries.append(status_entry)
return status_entries
def report_students_with_multiple_scores(score_data: List[Dict[str, Union[time.struct_time, str, int]]]) -> None:
users: Set[str] = {entry['username'] for entry in score_data}
for user in users:
bombs: Set[int] = {entry['bomb'] for entry in score_data if entry['username'] == user}
if len(bombs) > 1:
print(f'{user} has {len(bombs)} bombs: {bombs}')
def create_bomb_histories(creation_entries: List[Dict[str, Union[time.struct_time, str, int]]],
log_entries: List[Dict[str, Union[time.struct_time, str, int]]]) -> List[List[int]]:
bombs: List[List[int]] = [[]] * len(creation_entries)
for creation in creation_entries:
creation_time: time.struct_time = creation['timestamp']
timestamps: List[time.struct_time] = [creation_time, creation_time, creation_time, creation_time,
creation_time, creation_time, creation_time, creation_time]
bomb_number: int = creation['bomb']
for defusion in [entry for entry in log_entries
if entry['bomb'] == bomb_number and entry['result'] == 'defused']:
phase: int = defusion['phase']
if timestamps[phase] == creation_time:
timestamps[phase] = defusion['timestamp']
elif defusion['timestamp'] < timestamps[phase]:
timestamps[phase] = defusion['timestamp']
for i in range(1, len(timestamps)):
if timestamps[i] < timestamps[i - 1]:
timestamps[i] = timestamps[i - 1]
bombs[bomb_number - 1] = [calendar.timegm(timestamp) for timestamp in timestamps]
return bombs
def save_results(timestamps: List[List[int]], filename: str) -> None:
for i in range(len(timestamps)):
timestamps[i].insert(0, i + 1)
with open(filename, mode='w') as csv_file:
writer = csv.writer(csv_file)
writer.writerow(['bomb', 'created', 'phase 1', 'phase 2', 'phase 3', 'phase 4', 'phase 5', 'phase 6', 'secret phase'])
writer.writerows(timestamps)
if __name__ == '__main__':
log_name: str = input("What file has the BombLab log? ")
status_name: str = input("What file has the BombLab server status? ")
result_name: str = input("What file shall the results be saved to? ")
log: List[Dict[str, Union[time.struct_time, str, int]]] = parse_log(log_name)
creations: List[Dict[str, Union[time.struct_time, str, int]]] = parse_status(status_name)
report_students_with_multiple_scores(log)
histories: List[List[int]] = create_bomb_histories(creations, log)
save_results(histories, result_name)
from typing import Tuple
from api.canvas_classes import *
from common_functions import select_from_list
def get_student_scores(students: List[CanvasUser]) -> Tuple[Dict[CanvasUser, int], Dict[str, int]]:
student_usernames: Dict[str, CanvasUser] = {student.get_username(): student for student in students}
student_nuids: Dict[int, CanvasUser] = {student.get_nuid(): student for student in students}
student_emails: Dict[str, CanvasUser] = {student.get_email(): student for student in students}
recognized_student_scores: Dict[CanvasUser, int] = {}
unrecognized_student_scores: Dict[str, int] = {}
with open(filename, mode='r') as score_file:
for line in score_file:
tokens: List[str] = line.split()
if tokens[0] in student_usernames.keys():
recognized_student_scores[student_usernames[tokens[0]]] = int(tokens[1])
elif tokens[0] in student_emails.keys():
recognized_student_scores[student_emails[tokens[0]]] = int(tokens[1])
elif tokens[0].isdigit() and int(tokens[0]) in student_nuids.keys():
recognized_student_scores[student_nuids[int(tokens[0])]] = int(tokens[1])
elif '@' in tokens[0] and tokens[0].split('@')[0] in student_usernames.keys():
recognized_student_scores[student_usernames[tokens[0].split('@')[0]]] = int(tokens[1])
else:
unrecognized_student_scores[tokens[0]] = int(tokens[1])
return recognized_student_scores, unrecognized_student_scores
def grade_students(scores: Dict[CanvasUser, int], students: List[CanvasUser], assignment: CanvasAssignment) \
-> List[CanvasUser]:
students_to_be_graded: List[CanvasUser] = students[:]
for student in students:
submission: Submission = assignment.canvas_assignment.get_submission(student.get_canvas_id())
try:
submission.edit(submission={'posted_grade': scores[student]})
students_to_be_graded.remove(student)
except KeyError:
print(f'No score for {student}')
return students_to_be_graded
if __name__ == '__main__':
canvas_course_id: int = int(input("What is the Canvas course ID? "))
filename: str = input("What file has the BombLab scores? ")
course: CanvasCourse = CanvasCourse(canvas_course_id)
section: CanvasCourseSection = select_from_list(course.get_sections(), "Which section are we grading? ")
all_students: List[CanvasUser] = course.get_students()
print(f'{len(all_students)} students retrieved from {course}')
section_students: List[CanvasUser] = section.get_students()
print(f'{len(section_students)} students retrieved from {section}')
bomblab: CanvasAssignment = [assignment for assignment in course.get_assignments()
if assignment.get_name() == 'BombLab'][0]
student_scores: Dict[CanvasUser, int]
unmatched_scores: Dict[str, int]
student_scores, unmatched_scores = get_student_scores(all_students)
ungraded_students: List[CanvasUser] = grade_students(student_scores, section_students, bomblab)
print()
print('Ungraded students:')
print(ungraded_students)
print()
print('Unmatched scores:')
print(unmatched_scores)
import csv
import functools
from typing import Tuple
from api.canvas_classes import *
# noinspection PyShadowingNames
def get_scores(student: CanvasUser,
integer_lab_submissions: Iterable[Submission],
float_lab_submissions: Iterable[Submission],
exam_assignment: CanvasAssignment) -> Tuple[CanvasUser, Dict[str, Optional[float]]]:
print(student)
integer_lab_score: float = [submission.score for submission in integer_lab_submissions
if submission.user_id == student.get_canvas_id()][0]
float_lab_score: float = [submission.score for submission in float_lab_submissions
if submission.user_id == student.get_canvas_id()][0]
exam_submission: Submission = \
(exam_assignment.canvas_assignment.get_submission(student.get_canvas_id(),
include=['submission_history']).submission_history)[0]
integer_exam_score: Optional[float] = None
float_exam_score: Optional[float] = None
if 'submission_data' in exam_submission:
questions = exam_submission['submission_data']
integer_exam_score = functools.reduce(lambda x, y: x + y, [question['points'] for question in questions[2:8]])
float_exam_score = functools.reduce(lambda x, y: x + y, [question['points'] for question in questions[8:10]])
assignment_scores: Dict[str, Optional[float]] = {'IntegerLab': integer_lab_score, 'FloatLab': float_lab_score,
'IntegerExam': integer_exam_score, 'FloatExam': float_exam_score}
return student, assignment_scores
if __name__ == '__main__':
canvas_course_id: int = int(input("What is the Canvas course ID? "))
filename: str = input("What file do you want to save the scores to? ")
course: CanvasCourse = CanvasCourse(canvas_course_id)
students: List[CanvasUser] = course.get_students()
assignments: List[CanvasAssignment] = course.get_assignments()
integer_lab: Iterable[Submission] = \
([assignment for assignment in assignments if assignment.get_name() == 'IntegerLab'][0]
.canvas_assignment.get_submissions())
float_lab: Iterable[Submission] = \
([assignment for assignment in assignments if assignment.get_name() == 'FloatLab'][0]
.canvas_assignment.get_submissions())
exam: CanvasAssignment = \
[assignment for assignment in assignments if 'Exam 1' in assignment.get_name()][0]
scores: List[Tuple[CanvasUser, Dict[str, Optional[float]]]] = \
[get_scores(student, integer_lab, float_lab, exam) for student in students]
fields = ['IntegerLab', 'IntegerExam', 'FloatLab', 'FloatExam']
with open(filename, mode='w') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=fields + ['Student'])
writer.writeheader()
for student_scores in scores:
row = {field: student_scores[1][field] for field in fields}
row['Student'] = student_scores[0].get_name()
writer.writerow(row)
import csv
import functools
import os
from typing import Tuple
from canvasapi.file import File
from api.canvas_classes import *
def save_files(submissions: Iterable[Submission],
student_groups: List[CanvasUserGroup],
directory: str, filenames: List[str]) -> None:
for student_group in student_groups:
students = student_group.get_students()
if students:
group_submissions = [submission for submission in submissions
if submission.user_id == students[0].get_canvas_id()]
if group_submissions:
submission = group_submissions[0]
print(f'Retrieving files for "{student_group}"...')
files: List[File] = submission.attachments
if files:
print(f'\tDownloading files for "{student_group}"...')
os.mkdir(f'{directory}/Group{student_group.get_name().split()[-1]}')
for file in files:
candidate_filenames = [filename for filename in filenames
if file.filename.startswith(filename.split('.')[0])
and file.filename.endswith(filename.split('.')[1])]
if candidate_filenames:
filename = candidate_filenames[0]
print(f'\t\tSaving {file} as {filename}.')
file.download(f'{directory}/Group{student_group.get_name().split()[-1]}/{filename}')
else:
print(f'\t\tExtra file: {file}, not saved.')
else:
print(f'Group "{student_group}" has a submission without files.')
else:
print(f'Group "{student_group}" has no submissions.')
else:
print(f'Group "{student_group}" has no students.')
if __name__ == '__main__':
canvas_course_id: int = int(input('What is the Canvas course ID? '))
path: str = input('Where should the files be saved? ')
course: CanvasCourse = CanvasCourse(canvas_course_id)
# print('Retrieving users...')
# students: List[CanvasUser] = course.get_students()
print('Retrieving groups...')
group_set = [group_set for group_set in course.get_user_groupsets() if group_set.get_name() == 'Group Project'][0]
groups = group_set.get_groups()
print('Retrieving assignments...')
assignments: List[CanvasAssignment] = course.get_assignments()
print('Retrieving submissions...')
group_lab: Iterable[Submission] = \
([assignment for assignment in assignments if assignment.get_name() == 'GroupLab'][0]
.canvas_assignment.get_submissions())
save_files(group_lab, groups, path, ['alarm.c', 'sensor.c', 'user_controls.c', 'shared_variables.h'])
{
"canvas course id": 167285,
"end of grading periods": [
"2024-02-23",
"2024-04-05",
"2024-05-10"
],
"first semester": {
"teams": [
"UNL BSE Hebets",
"UNMC Nebraska Medicine"
],
"submission assignments": [
"Lab Report 1",
"Lab Report 2",
"Lab Report 3"
],
"floating assignments": [
"Team Performance (Grading Period 1)",
"Team Performance (Grading Period 2)",
"Team Performance (Grading Period 3)",
"Behaviors (Grading Period 1)",
"Behaviors (Grading Period 2)",
"Behaviors (Grading Period 3)",
"Written Communication (Grading Period 1)",
"Written Communication (Grading Period 2)",
"Written Communication (Grading Period 3)"
]
},
"second semester": {
"teams": [
"Aulick Industries",
"CLAAS Omaha",
"Compass North",
"Crete Carrier",
"DMSi",
"Emerson",
"Farm Credit Services of America",
"Fast Forward",
"Firework Media Studio Xrenegades",
"Henderson State Bank",
"Mutual of Omaha",
"National Indemnity",
"Nebraska Water Center",
"Nelnet",
"Streck",
"UNL BSE Brown-Brandl",
"UNL SoC Falkinburg-Daniel",
"UNL SoC Liu",
"UNL SoC Ramamurthy",
"UNMC College of Nursing",
"USDA-NRCS",
"Werner Enterprises"
],
"submission assignments": [
"Lab Report 4",
"Lab Report 5",
"Lab Report 6"
],
"floating assignments": [
"Team Performance (Grading Period 4)",
"Team Performance (Grading Period 5)",
"Team Performance (Grading Period 6)",
"Behaviors (Grading Period 4)",
"Behaviors (Grading Period 5)",
"Behaviors (Grading Period 6)",
"Written Communication (Grading Period 4)",
"Written Communication (Grading Period 5)",
"Written Communication (Grading Period 6)"
]
},
"other": [
{
"teams": "Entrepreneur",
"submission assignments": [],
"floating assignments": [
"Entrepreneur Behaviors (Grading Period 4)",
"Entrepreneur Behaviors (Grading Period 5)",
"Entrepreneur Behaviors (Grading Period 6)"
]
},
{
"teams": "PM Protege",
"submission assignments": [],
"floating assignments": []
},
{
"teams": "Research",
"submission assignments": [],
"floating assignments": [
"Researcher Behaviors (first grading period of the semester)",
"Researcher Behaviors (second grading period of the semester)",
"Researcher Behaviors (third grading period of the semester)"
]
},
{
"teams": "Tech Reviewer",
"submission assignments": [],
"floating assignments": [
"Tech Reviewer Behaviors (first grading period of the semester)",
"Tech Reviewer Behaviors (second grading period of the semester)",
"Tech Reviewer Behaviors (third grading period of the semester)"
]
}
],
"individual assignments": [
{
"description": "Individual Performance",
"select students": {
"students": [
"Matthew Lobmeyer"
],
"assignments": [
"PM Protege Performance (Grading Period 1)",
"PM Protege Performance (Grading Period 2)",
"PM Protege Performance (Grading Period 3)",
"PM Protege Behaviors (Grading Period 1)",
"PM Protege Behaviors (Grading Period 2)",
"PM Protege Behaviors (Grading Period 3)"
]
},
"everybody else": [
"Individual Performance (first grading period of the semester)",
"Individual Performance (second grading period of the semester)",
"Individual Performance (third grading period of the semester)"
]
},
{
"description": "PM Protege Behaviors",
"select students": {
"students": [
"Sarah Oran",
"Han Tran"
],
"assignments": [
"PM Protege Behaviors (Grading Period 4)",
"PM Protege Behaviors (Grading Period 5)",
"PM Protege Behaviors (Grading Period 6)"
]
},
"everybody else": []
},
{
"description": "Verbal Communication",
"select students": [],
"everybody else": [
"Verbal Communication (first grading period of the semester)",
"Verbal Communication (second grading period of the semester)",
"Verbal Communication (third grading period of the semester)",
"Verbal Communication (across semester)"
]
},
{
"description": "Professionalism",
"select students": [],
"everybody else": [
"Professionalism and Respect (first grading period of the semester)",
"Professionalism and Respect (second grading period of the semester)",
"Professionalism and Respect (third grading period of the semester)",
"Engagement and Participation (across semester)"
]
}
]
}
\ No newline at end of file
import json
from datetime import date
from typing import Set, Tuple
from api.canvas_classes import *
def validate_teams(json_data, canvas_teams: List[CanvasUserGroup]) -> Tuple[Set[str], Set[str]]:
json_team_names = (json_data['first semester']['teams'] +
json_data['second semester']['teams'] +
[element['teams'] for element in json_data['other']])
canvas_team_names = [team.get_name() for team in canvas_teams]
not_in_canvas = set(json_team_names) - set(canvas_team_names)
not_in_json = set(canvas_team_names) - set(json_team_names)
number_of_duplicates = len(json_team_names) - len(canvas_team_names)
if len(not_in_json) == 0 and len(not_in_canvas) == 0 and number_of_duplicates != 0:
print(f'Notice: there appear to be {number_of_duplicates} teams repeated in the json data (continuing...)')
return not_in_canvas, not_in_json
def assign_team_assignments(teams: List[CanvasUserGroup],
assignments: List[CanvasAssignment],
due_dates: List[date] | None = None) -> None:
for index, assignment in enumerate(assignments):
due_date = due_dates[index] if due_dates is not None else None
print(f'{assignment} ({due_date})' if due_dates is not None else f'{assignment}')
# make sure nothing wierd happens when all overrides are gone
assignment.canvas_assignment.edit(assignment={'only_visible_to_overrides': False})
# remove whatever was there before (almost certainly simpler than editing existing overrides)
for override in assignment.canvas_assignment.get_overrides():
override.delete()
# assign specific teams to the assignment
for team in teams:
print(f'\t{team}')
if due_dates is not None:
assignment.canvas_assignment.create_override(assignment_override={
'group_id': team.canvas_user_group.id,
'due_at': due_date
})
else:
assignment.canvas_assignment.create_override(
assignment_override={'group_id': team.canvas_user_group.id})
# eliminate the "everyone else" due date
assignment.canvas_assignment.edit(assignment={'only_visible_to_overrides': True})
def assign_individual_assignments(assignment_data,
students: List[CanvasUser],
teams: List[CanvasUserGroup],
assignments: List[CanvasAssignment]) -> None:
select_students = assignment_data['select students']
if select_students:
# assign select students' assignments to select students
# assign "everybody else" to all the other teams and to all the select students' teammates
special_students = [student for student in students if student.get_name() in select_students['students']]
special_assignments = [assignment for assignment in assignments
if assignment.get_name() in select_students['assignments']]
for assignment in special_assignments:
print(assignment)
# make sure nothing wierd happens when all overrides are gone
assignment.canvas_assignment.edit(assignment={'only_visible_to_overrides': False})
# remove whatever was there before (almost certainly simpler than editing existing overrides)
for override in assignment.canvas_assignment.get_overrides():
override.delete()
# assign specific students to the assignment
print(f'\t{special_students}')
assignment.canvas_assignment.create_override(
assignment_override={'student_ids': [student.get_canvas_id() for student in special_students]})
# eliminate the "everyone else" due date
assignment.canvas_assignment.edit(assignment={'only_visible_to_overrides': True})
standard_assignments = [assignment for assignment in assignments
if assignment.get_name() in assignment_data['everybody else']]
for assignment in standard_assignments:
print(assignment)
for student in special_students:
print(f'\tyou need to manually excuse {student}')
# assignment.canvas_assignment.get_submission(student.canvas_user).edit(submission = {'excuse': True})
# only works if the course is published
pass
pass
pass
else:
# do nothing -- by default the assignment is assigned to "everybody"
pass
def assign_assignments(every_assignment: List[CanvasAssignment],
every_team: List[CanvasUserGroup],
every_student: List[CanvasUser],
json_data) -> None:
ends_of_grading_periods = [date.fromisoformat(iso_date) for iso_date in json_data['end of grading periods']]
team_assignment_data = ([json_data['first semester'], json_data['second semester']] +
[other for other in json_data['other']])
for team_assignments in team_assignment_data:
teams = [team for team in every_team if team.get_name() in team_assignments['teams']]
assign_team_assignments(teams,
[assignment for assignment in every_assignment
if assignment.get_name() in team_assignments['submission assignments']],
ends_of_grading_periods)
assign_team_assignments(teams,
[assignment for assignment in every_assignment
if assignment.get_name() in team_assignments['floating assignments']])
for individual_assignments in json_data['individual assignments']:
assign_individual_assignments(individual_assignments, every_student, every_team, every_assignment)
if __name__ == '__main__':
print('Connecting...')
with open('SeniorDesignAssignments.json', 'r') as json_file:
data = json.load(json_file)
course: CanvasCourse = CanvasCourse(int(data['canvas course id']))
all_assignments: List[CanvasAssignment] = course.get_assignments()
all_teams: List[CanvasUserGroup] = course.get_all_user_groups()
all_students: List[CanvasUser] = course.get_students()
extra_teams, missing_teams = validate_teams(data, all_teams)
if missing_teams:
print(f'These teams are found in Canvas but not in the JSON file: {missing_teams} (continuing...)')
if extra_teams:
print(f'These teams are found in the JSON file but not in Canvas: {extra_teams} (terminating!)')
else:
assign_assignments(all_assignments, all_teams, all_students, data)
import csv
import functools
from datetime import date, timedelta
from typing import Set, Tuple
from api.canvas_classes import *
canvas_course_id: int = 167285
ethics_start_date: date = date(2024, 2, 26)
ethics_end_date: date = date(2024, 4, 5)
end_of_grading_periods = [date(2024, 2, 23), date(2024, 4, 5), date(2024, 5, 10)]
one_day: timedelta = timedelta(days=1)
one_week: timedelta = timedelta(days=7)
status_report_exclusions: Set[str] = {'Entrepreneur', 'PM Protege', 'Tech Reviewer', 'Research'}
ethics_filename = 'ethics_quizzes.csv'
grading_filename = 'grading_status.csv'
def grade_ethics_modules(all_assignments: List[CanvasAssignment]):
quizzes: List[CanvasAssignment] = [assignment for assignment in all_assignments
if 'Quiz' in assignment.get_name()]
# initialize the data structure
quiz_grades: Dict[date, Dict[str, List[int]]] = {}
keys: List[str] = ['Quiz 1', 'Quiz 2', 'Quiz 3', 'Quiz 4']
day: date = ethics_start_date
while day <= ethics_end_date:
quiz_grades[day] = {key: [0 for _ in range(0, 17)] for key in keys}
day = day + one_day
# retrieve the grades
for quiz in quizzes:
quiz_name: str = ''
if '1' in quiz.get_name():
quiz_name = 'Quiz 1'
elif '2' in quiz.get_name():
quiz_name = 'Quiz 2'
elif '3' in quiz.get_name():
quiz_name = 'Quiz 3'
elif '4' in quiz.get_name():
quiz_name = 'Quiz 4'
print(quiz_name)
submissions: Iterable[Submission] = quiz.canvas_assignment.get_submissions(include=['submission_history'])
for submission in submissions:
submission_timestamp: Optional[str] = submission.submission_history[0]['submitted_at']
if submission_timestamp is not None:
completion: date = date.fromisoformat(
submission_timestamp[:10]) # will be off by a few hours; close enough
if ethics_start_date <= completion <= ethics_end_date:
score: int = int(submission.submission_history[0]['score'])
quiz_grades[completion][quiz_name][score] += 1
# print it
day: date = ethics_start_date
while day <= ethics_end_date:
for i in range(0, 17):
if quiz_grades[day][quiz_name][i] > 0:
print(f'{day},{i},{quiz_grades[day][quiz_name][i]}')
day = day + one_day
print()
def process_ethics_modules(all_assignments: List[CanvasAssignment]):
quizzes: List[CanvasAssignment] = [assignment for assignment in all_assignments
if 'Participation Survey' in assignment.get_name()
or 'Quiz' in assignment.get_name()]
# initialize the data structure
quiz_completions: Dict[date, Dict[str, int]] = {}
keys: List[str] = ['Participation Survey', 'Quiz 1', 'Quiz 2', 'Quiz 3', 'Quiz 4']
day: date = ethics_start_date
while day <= ethics_end_date:
quiz_completions[day] = {key: 0 for key in keys}
day = day + one_day
# count the completions
for quiz in quizzes:
quiz_name: str = 'Participation Survey'
if '1' in quiz.get_name():
quiz_name = 'Quiz 1'
elif '2' in quiz.get_name():
quiz_name = 'Quiz 2'
elif '3' in quiz.get_name():
quiz_name = 'Quiz 3'
elif '4' in quiz.get_name():
quiz_name = 'Quiz 4'
print(quiz_name)
submissions: Iterable[Submission] = quiz.canvas_assignment.get_submissions(include=['submission_history'])
for submission in submissions:
submission_timestamp: Optional[str] = submission.submission_history[0]['submitted_at']
if submission_timestamp is not None:
# completion = datetime.fromisoformat(submission_timestamp)
# completion = completion.date()
completion = date.fromisoformat(submission_timestamp[:10]) # will be off by a few hours; close enough
if ethics_start_date <= completion <= ethics_end_date:
quiz_completions[completion][quiz_name] += 1
# convert it into a running total
running_total: Dict[str, int] = {key: 0 for key in keys}
day = ethics_start_date
while day <= ethics_end_date:
for key in keys:
running_total[key] += quiz_completions[day][key]
quiz_completions[day][key] = running_total[key]
day = day + one_day
# save it
with open(ethics_filename, mode='w') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=['Date'] + keys)
writer.writeheader()
for completion_day in quiz_completions.keys():
row: Dict[str, Union[int, str]] = quiz_completions[completion_day]
row['Date'] = str(completion_day)
writer.writerow(row)
print(f'Saved completion of ethics modules data to {ethics_filename}.')
def get_submissions(all_assignments: List[CanvasAssignment], students: List[CanvasUser], assignment_name: str) \
-> Tuple[CanvasAssignment, List[Submission]]:
assignment: CanvasAssignment = [assignment for assignment in all_assignments
if assignment.get_name() == assignment_name][0]
submissions: List[Submission] = [submission for submission in
assignment.canvas_assignment.get_submissions(include=['submission_history'])
if submission.user_id in [user.get_canvas_id() for user in students]]
return assignment, submissions
def set_grading_status(grading_status, all_assignments, students, team_name, amalgam_assignment,
first_possible_assignment, second_possible_assignment=None):
if team_name not in grading_status:
print(f'No such team: {team_name} ({amalgam_assignment}')
elif amalgam_assignment not in grading_status[team_name]:
print(f'No such assignment for {team_name}: {amalgam_assignment}')
elif grading_status[team_name][amalgam_assignment] == 100:
print(f'{team_name} - {amalgam_assignment} -- fully scored')
else:
print(f'{team_name} - {amalgam_assignment} -- retrieving grading status')
assignment, submissions = get_submissions(all_assignments, students, first_possible_assignment)
if len(submissions) == 0 and second_possible_assignment is not None:
assignment, submissions = get_submissions(all_assignments, students, second_possible_assignment)
if len(submissions) > 0:
graded_submission_count: int = functools.reduce(lambda x, y: x + y,
[0 if grade is None else 1 for grade in
[submission.grade for submission in submissions]])
grading_status[team_name][amalgam_assignment] = int(100 * graded_submission_count / len(submissions))
def process_grading(all_assignments: List[CanvasAssignment], teams: List[CanvasUserGroup]):
# initialize the data structure
keys: List[str] = ['Team Performance 1/4', 'Individual Performance 1/4', 'Behaviors 1/4',
'Written Communication 1/4', 'Verbal Communication 1/4', 'Professionalism & Respect 1/4',
'Team Performance 2/5', 'Individual Performance 2/5', 'Behaviors 2/5',
'Written Communication 2/5', 'Verbal Communication 2/5', 'Professionalism & Respect 2/5',
'Team Performance 3/6', 'Individual Performance 3/6', 'Behaviors 3/6',
'Written Communication 3/6', 'Verbal Communication 3/6', 'Professionalism & Respect 3/6',
'Verbal Communication (across semester)', 'Engagement & Participation (across semester)']
grading_status: Dict[str, Dict[str, int]] = {}
try:
with open(grading_filename) as csv_file:
reader = csv.DictReader(csv_file)
for row in reader:
team_name = row['Team']
grading_status[team_name] = {key: int(row[key]) for key in keys}
except FileNotFoundError:
for team in teams:
team_name = str(team)
grading_status[team_name] = {key: -1 for key in keys}
for team in teams:
team_name = str(team)
students: List[CanvasUser] = team.get_students()
# team performance
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Team Performance 1/4',
'Team Performance (Grading Period 1)', 'Team Performance (Grading Period 4)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Team Performance 2/5',
'Team Performance (Grading Period 2)', 'Team Performance (Grading Period 5)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Team Performance 3/6',
'Team Performance (Grading Period 3)', 'Team Performance (Grading Period 6)')
# individual performance
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Individual Performance 1/4',
'Individual Performance (first grading period of the semester)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Individual Performance 2/5',
'Individual Performance (second grading period of the semester)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Individual Performance 3/6',
'Individual Performance (third grading period of the semester)')
# team behaviors
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Behaviors 1/4',
'Behaviors (Grading Period 1)', 'Behaviors (Grading Period 4)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Behaviors 2/5',
'Behaviors (Grading Period 2)', 'Behaviors (Grading Period 5)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Behaviors 3/6',
'Behaviors (Grading Period 3)', 'Behaviors (Grading Period 6)')
# team written communication
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Written Communication 1/4',
'Written Communication (Grading Period 1)', 'Written Communication (Grading Period 4)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Written Communication 2/5',
'Written Communication (Grading Period 2)', 'Written Communication (Grading Period 5)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Written Communication 3/6',
'Written Communication (Grading Period 3)', 'Written Communication (Grading Period 6)')
# individual verbal communication
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Verbal Communication 1/4',
'Verbal Communication (first grading period of the semester)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Verbal Communication 2/5',
'Verbal Communication (second grading period of the semester)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Verbal Communication 3/6',
'Verbal Communication (third grading period of the semester)')
# individual professionalism
if date.today() >= end_of_grading_periods[0]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Professionalism & Respect 1/4',
'Professionalism and Respect (first grading period of the semester)')
if date.today() >= end_of_grading_periods[1]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Professionalism & Respect 2/5',
'Professionalism and Respect (second grading period of the semester)')
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name, 'Professionalism & Respect 3/6',
'Professionalism and Respect (third grading period of the semester)')
# individual one-time grades
if date.today() >= end_of_grading_periods[2]:
set_grading_status(grading_status, all_assignments, students, team_name,
'Verbal Communication (across semester)',
'Verbal Communication (across semester)')
set_grading_status(grading_status, all_assignments, students, team_name,
'Engagement & Participation (across semester)',
'Engagement and Participation (across semester)')
# save it
with open(grading_filename, mode='w') as csv_file:
writer = csv.DictWriter(csv_file, fieldnames=['Team'] + keys)
writer.writeheader()
for team in teams:
team_name = str(team)
if team_name not in grading_status:
print(f'No row written for {team_name}')
else:
row: Dict[str, Union[int, str]] = grading_status[team_name]
row['Team'] = team_name
writer.writerow(row)
print(f'Saved grading status data to {grading_filename}.')
def process_team_status_reports(all_assignments: List[CanvasAssignment], teams: List[CanvasUserGroup]):
status_report_date: date = date.today()
status_report: Optional[CanvasAssignment] = None
while status_report is None:
status_report_name: str = f'Weekly Status Report - {status_report_date.month}/{status_report_date.day}'
candidate_assignments: List[CanvasAssignment] = [assignment for assignment in all_assignments
if assignment.get_name() == status_report_name]
if len(candidate_assignments) == 0:
print(f'There is no assignment named "{status_report_name}".')
status_report_date -= one_day
else:
status_report = candidate_assignments[0]
print(status_report)
for team in teams:
students: List[CanvasUser] = team.get_students()
submissions: List[Submission] = [submission for submission in
status_report.canvas_assignment.get_submissions(include=['submission_history'])
if submission.user_id == students[0].get_canvas_id()]
if len(submissions) == 0:
print(f'{team} is not assigned {status_report}.')
elif len(submissions[0].attachments) == 0:
print(f'{team} has NOT submitted {status_report}!')
else:
print(f'{team} - ok')
if __name__ == '__main__':
print('Connecting...')
course: CanvasCourse = CanvasCourse(canvas_course_id)
assignments: List[CanvasAssignment] = course.get_assignments()
student_groups: List[CanvasUserGroup] = course.get_all_user_groups()
if end_of_grading_periods[0] < date.today() < end_of_grading_periods[1] + one_week:
process_ethics_modules(assignments)
# grade_ethics_modules(assignments)
print()
if date.today() > end_of_grading_periods[0]:
process_grading(assignments,
[team for team in student_groups if team.get_name() not in status_report_exclusions])
print()
# process_team_status_reports(assignments,
# [team for team in student_groups if team.get_name() not in status_report_exclusions])
from api.canvas_classes import *
if __name__ == '__main__':
canvas_course_id: int = int(input('What is the Canvas course ID? '))
course: CanvasCourse = CanvasCourse(canvas_course_id)
print('Retrieving sections...')
sections = course.get_sections()
print('Retrieving students...')
students: Iterable[CanvasUser] = sections[0].get_students()
print('Retrieving enrollments...')
enrollments = sections[0].canvas_section.get_enrollments()
sorted_enrollments = sorted(enrollments, key=lambda x: x.created_at_date)
print('\n\n Enrollment Date \tStudent')
print(' =============== \t=======')
for enrollment in sorted_enrollments:
student = [student for student in students if student.get_canvas_id() == enrollment.user_id]
print(f'{enrollment.created_at_date}\t{student[0].get_name() if student else "[]"}')
......@@ -53,7 +53,10 @@ class CanvasUser:
return self.canvas_user.sortable_name
def get_username(self) -> str:
try:
return self.canvas_user.login_id
except AttributeError:
return 'Unknown User'
def get_canvas_id(self) -> int:
return self.canvas_user.id
......@@ -323,6 +326,9 @@ class CanvasAssignment:
return None
def get_quiz_response(self, canvas_user: CanvasUser) -> List[Dict[str, str]]:
# TODO: fix for multiple questions each with multiple answers and test to see if it broke single questions
# need to sort get_submission_questions() by q.position
# ['submission_data'] already appears to be sorted by position, but need to look for `answer_for_ans0` and `answer_for_ans1`
questions_and_answers: List[Dict[str, str]] = []
questions: List[str] = []
answers: List[str] = []
......
import sys
from datetime import datetime, date
from deprecated import deprecated
from functools import reduce
......@@ -262,7 +263,7 @@ class GitlabCommit:
# noinspection PyShadowingNames
def get_diffs(self) -> List[Dict[str, Union[str, int]]]:
diffs: List[Dict[str, Union[str, int]]] = []
gitlab_diffs: List[Dict[str, str]] = self.git_commit.diff()
gitlab_diffs: List[Dict[str, str]] = self.git_commit.diff(get_all=True)
for diff in gitlab_diffs:
diffs.append({'file': diff['new_path'], 'text': diff['diff'],
'+': diff['diff'].count('\n+'), '-': diff['diff'].count('\n-')})
......
......@@ -6,4 +6,5 @@ class Course:
# Canvas course information
# canvas_course_id = '73696' # Software Engineering Sandbox
# canvas_course_id = '83539' # CSCE 361-1205
canvas_course_id = '141437' # SOFT 160-1228
\ No newline at end of file
# canvas_course_id = '141437' # SOFT 160-1228
canvas_course_id = '162910' # SOFT 160-1238
\ No newline at end of file
......@@ -145,7 +145,7 @@ def display_git_contributions(project: GitlabProject, all_branches: bool = False
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 the master branch:')
print(f'Contributions by each partner to {project} on the default branch:')
for contribution in contributions:
contributor = list(filter(lambda c: c[1] == contribution, contributors))[0]
email = contributor[1]
......
import statistics
from api.canvas_classes import *
canvas_course_id: int = 162910
if __name__ == '__main__':
print('Connecting...')
course: CanvasCourse = CanvasCourse(canvas_course_id)
students: Iterable[CanvasUser] = course.get_students()
assignments: List[CanvasAssignment] = course.get_assignments()
assignment: CanvasAssignment = \
[assignment for assignment in assignments if 'Parts 1 & 2' in assignment.get_name()][0]
rubric = assignment.canvas_assignment.rubric
error_detection: str = [item['id'] for item in rubric if item['description'] == 'Error Detection'][0]
no_false_detections: str = [item['id'] for item in rubric if item['description'] == 'No False Detections'][0]
# submissions: Iterable[Submission] = assignment.canvas_assignment.get_submissions(include='rubric_assessment')
# ugh, this will probably be slow
print('Retrieving submissions...')
submissions: Iterable[Submission] = [assignment.canvas_assignment.get_submission(student.get_canvas_id(),
include='rubric_assessment')
for student in sorted(students, key=CanvasUser.get_sortable_name)]
grades = []
error_detections = []
false_detections = []
print('STUDENT\t\t\t\tGRADE\tERROR DETECTION\t\tNO FALSE DETECTIONS')
for submission in submissions:
candidates = [student for student in students if student.get_canvas_id() == submission.user_id]
if len(candidates) == 0:
print(f'No student with Canvas ID {submission.user_id}')
else:
student = candidates[0].get_username()
if len(student) < 8:
student = student + ' '
if len(student) < 12:
student = student + ' '
if len(student) < 16:
student = student + ' '
grade = submission.grade
if grade == 0.0 or grade == 0 or grade == '0' or grade == '0.0':
print(f'{student}\t{grade}')
else:
grades.append(float(grade))
try:
assessment = submission.rubric_assessment
if 'points' in assessment[error_detection]:
error_detection_points = assessment[error_detection]['points']
error_detections.append(float(error_detection_points))
else:
error_detection_points = 'not scored'
if 'points' in assessment[no_false_detections]:
false_detection_points = assessment[no_false_detections]['points']
false_detections.append(float(false_detection_points))
else:
false_detection_points = 'not scored'
print(
f'{student}\t{grade} \t\t{error_detection_points}\t\t{false_detection_points}')
except AttributeError:
print(f'{student}\tnot assessed')
print()
print('Grade statistics:')
print(f'\tMaximum: {max(grades)}')
print(f'\tMedian: {statistics.median(grades)}')
print(f'\tMean: {statistics.mean(grades)}')
print(f'\tMinimum: {min(grades)}')
print('Error Detection statistics:')
print(f'\tMaximum: {max(error_detections)}')
print(f'\tMedian: {statistics.median(error_detections)}')
print(f'\tMean: {statistics.mean(error_detections)}')
print(f'\tMinimum: {min(error_detections)}')
print('False Detection statistics:')
print(f'\tMaximum: {max(false_detections)}')
print(f'\tMedian: {statistics.median(false_detections)}')
print(f'\tMean: {statistics.mean(false_detections)}')
print(f'\tMinimum: {min(false_detections)}')
......@@ -158,6 +158,7 @@ def create_cloning_script(user_repos: Dict[CompositeUser, Optional[GitlabProject
if __name__ == '__main__':
course = CanvasCourse(Course.canvas_course_id)
print(f'Using course: {course}')
print('First, select the "setup" assignment to extract student information from.\n')
assignment_groups = course.get_assignment_groups()
assignment_group = select_from_list(assignment_groups, 'assignment group')
......
import pyperclip
def convert_to_tag_chars(input_string):
return ''.join(chr(0xE0000 + ord(ch)) for ch in input_string)
if __name__ == '__main__':
user_input = input('Enter a string to convert to characters: ')
tagged_output = convert_to_tag_chars(user_input)
print("tagged output:", tagged_output)
pyperclip.copy(tagged_output)
......@@ -4,25 +4,18 @@ from api.gitlab_classes import GitlabProject, GitlabIssue
# noinspection SpellCheckingInspection
project_paths: Dict[str, str] = {
'A1': 'csalem3/prescribed-burn-plan-evaluator',
'A2': 'mthomsen7/prescribed-burn-plan-evaluator',
'A3': 'rpesek4/prescribed-burn-plan-evaluator',
'A4': 'rkrishna2/prescribed-burn-plan-evaluator',
'A5': 'mgarcia42/prescribed-burn-plan-evaluator',
'A6': 'mdejournett2/prescribed-burn-plan-evaluator',
'A7': 'jvaccaro7/prescribed-burn-plan-evaluator',
'A8': 'bgroenjes3/prescribed-burn-plan-evaluator',
'A9': 'mnolda2/prescribed-burn-plan-evaluator',
'B1': 'tscott19/prescribed-burn-plan-evaluator',
'B2': 'shoover5/prescribed-burn-plan-evaluator',
'B3': 'wglover2/prescribed-burn-plan-evaluator',
'B4': 'zalexander2/prescribed-burn-plan-evaluator',
'B5': 'fle2/prescribed-burn-plan-evaluator',
'B6': 'erohrs2/prescribed-burn-plan-evaluator',
'B7': 'ksierra2/prescribed-burn-plan-evaluator',
'B8': 'wadair2/prescribed-burn-plan-evaluator',
'B9': 'bdick-burkey2/prescribed-burn-plan-evaluator',
'B10': 'dhermanson3/prescribed-burn-plan-evaluator'
'A1': 'zkerchal2/prescribed-burn-plan-evaluator',
'A2': 'tnguyen197/prescribed-burn-plan-evaluator',
'A3': 'zcloutier2/prescribed-burn-plan-evaluator',
'A4': 'alahm2/prescribed-burn-plan-evaluator',
'A5': 'ppeterson85/prescribed-burn-plan-evaluator',
'A6': 'troelfs2/Capstone',
'B1': 'anguyen104/prescribed-burn-plan-evaluator',
'B2': 'bjohnson261/prescribe-burn-plan-evaluation',
'B3': 'lloseke2/prescribed-burn-plan-evaluator',
'B4': 'llinquet2/prescribed-burn-plan-evaluator',
'B5': 'blarson31/prescribed-burn-plan-evaluator',
'B6': 'nmcvay2/prescribed-burn-plan-evaluator'
}
if __name__ == '__main__':
......@@ -30,9 +23,18 @@ if __name__ == '__main__':
for team_name in project_paths.keys()}
for team_name in projects:
print(f'==== {team_name} ====')
print(f'URL: {projects[team_name].get_cloning_url()}')
issues: List[GitlabIssue] = sorted(projects[team_name].get_issues(),
key=lambda i: i.get_project_issue_id())
for issue in issues:
print(f'\tIssue {issue.get_project_issue_id()} created on {issue.get_created_at()} '
f'was assigned to {issue.get_assignee()} and closed at {issue.get_closed_at()}')
print(f'\tIssue {issue.get_project_issue_id()} "{issue.get_title()}"')
if issue.is_closed():
print(f'\t\tCreated on {issue.get_created_at()}, assigned to {issue.get_assignee()},'
f' and closed at {issue.get_closed_at()}.')
elif issue.get_created_at() != issue.get_updated_at():
print(f'\t\tCreated on {issue.get_created_at()}, assigned to {issue.get_assignee()},'
f' and last updated at {issue.get_updated_at()}')
else:
print(f'\t\tCreated on {issue.get_created_at()}, assigned to {issue.get_assignee()},'
f' and never updated')
print()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment