def view_grades_by_opportunity(pctx, opp_id): from course.views import get_now_or_fake_time now_datetime = get_now_or_fake_time(pctx.request) if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant]: raise PermissionDenied(_("must be instructor or TA to view grades")) opportunity = get_object_or_404(GradingOpportunity, id=int(opp_id)) if pctx.course != opportunity.course: raise SuspiciousOperation(_("opportunity from wrong course")) # {{{ batch sessions form batch_session_ops_form = None if pctx.role == participation_role.instructor and opportunity.flow_id: cursor = connection.cursor() cursor.execute("select distinct access_rules_tag from course_flowsession " "where course_id = %s and flow_id = %s " "order by access_rules_tag", (pctx.course.id, opportunity.flow_id)) session_rule_tags = [ mangle_session_access_rule_tag(row[0]) for row in cursor.fetchall()] request = pctx.request if request.method == "POST": batch_session_ops_form = ModifySessionsForm( session_rule_tags, request.POST, request.FILES) if "expire" in request.POST: op = "expire" elif "end" in request.POST: op = "end" elif "regrade" in request.POST: op = "regrade" elif "recalculate" in request.POST: op = "recalculate" else: raise SuspiciousOperation(_("invalid operation")) if batch_session_ops_form.is_valid(): rule_tag = batch_session_ops_form.cleaned_data["rule_tag"] past_due_only = batch_session_ops_form.cleaned_data["past_due_only"] if rule_tag == RULE_TAG_NONE_STRING: rule_tag = None from course.tasks import ( expire_in_progress_sessions, finish_in_progress_sessions, regrade_flow_sessions, recalculate_ended_sessions) if op == "expire": async_res = expire_in_progress_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, now_datetime, past_due_only=past_due_only) return redirect("relate-monitor_task", async_res.id) elif op == "end": async_res = finish_in_progress_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, now_datetime, past_due_only=past_due_only) return redirect("relate-monitor_task", async_res.id) elif op == "regrade": async_res = regrade_flow_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, inprog_value=False) return redirect("relate-monitor_task", async_res.id) elif op == "recalculate": async_res = recalculate_ended_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag) return redirect("relate-monitor_task", async_res.id) else: raise SuspiciousOperation("invalid operation") else: batch_session_ops_form = ModifySessionsForm(session_rule_tags) # }}} # NOTE: It's important that these queries are sorted consistently, # also consistently with the code below. participations = list(Participation.objects .filter( course=pctx.course, status=participation_status.active) .order_by("id") .select_related("user")) grade_changes = list(GradeChange.objects .filter(opportunity=opportunity) .order_by( "participation__id", "grade_time") .select_related("participation") .select_related("participation__user") .select_related("opportunity")) idx = 0 finished_sessions = 0 total_sessions = 0 grade_table = [] for participation in participations: while ( idx < len(grade_changes) and grade_changes[idx].participation.id < participation.id): idx += 1 my_grade_changes = [] while ( idx < len(grade_changes) and grade_changes[idx].participation.pk == participation.pk): my_grade_changes.append(grade_changes[idx]) idx += 1 state_machine = GradeStateMachine() state_machine.consume(my_grade_changes) if opportunity.flow_id: flow_sessions = (FlowSession.objects .filter( participation=participation, flow_id=opportunity.flow_id, ) .order_by("start_time")) for fsession in flow_sessions: total_sessions += 1 if not fsession.in_progress: finished_sessions += 1 else: flow_sessions = None grade_table.append( OpportunityGradeInfo( grade_state_machine=state_machine, flow_sessions=flow_sessions)) def grade_key(entry): (participation, grades) = entry return (participation.user.last_name.lower(), participation.user.first_name.lower()) grade_table = sorted(zip(participations, grade_table), key=grade_key) return render_course_page(pctx, "course/gradebook-by-opp.html", { "opportunity": opportunity, "participations": participations, "grade_state_change_types": grade_state_change_types, "grade_table": grade_table, "batch_session_ops_form": batch_session_ops_form, "total_sessions": total_sessions, "finished_sessions": finished_sessions, })
def view_grades_by_opportunity(pctx, opp_id): from course.views import get_now_or_fake_time now_datetime = get_now_or_fake_time(pctx.request) if pctx.role not in [ participation_role.instructor, participation_role.teaching_assistant ]: raise PermissionDenied(_("must be instructor or TA to view grades")) opportunity = get_object_or_404(GradingOpportunity, id=int(opp_id)) if pctx.course != opportunity.course: raise SuspiciousOperation(_("opportunity from wrong course")) # {{{ batch sessions form batch_session_ops_form = None if pctx.role == participation_role.instructor and opportunity.flow_id: cursor = connection.cursor() cursor.execute( "select distinct access_rules_tag from course_flowsession " "where course_id = %s and flow_id = %s " "order by access_rules_tag", (pctx.course.id, opportunity.flow_id)) session_rule_tags = [ mangle_session_access_rule_tag(row[0]) for row in cursor.fetchall() ] request = pctx.request if request.method == "POST": batch_session_ops_form = ModifySessionsForm( session_rule_tags, request.POST, request.FILES) if "expire" in request.POST: op = "expire" elif "end" in request.POST: op = "end" elif "regrade" in request.POST: op = "regrade" elif "recalculate" in request.POST: op = "recalculate" else: raise SuspiciousOperation(_("invalid operation")) if batch_session_ops_form.is_valid(): rule_tag = batch_session_ops_form.cleaned_data["rule_tag"] past_due_only = batch_session_ops_form.cleaned_data[ "past_due_only"] if rule_tag == RULE_TAG_NONE_STRING: rule_tag = None from course.tasks import (expire_in_progress_sessions, finish_in_progress_sessions, regrade_flow_sessions, recalculate_ended_sessions) if op == "expire": async_res = expire_in_progress_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, now_datetime, past_due_only=past_due_only) return redirect("relate-monitor_task", async_res.id) elif op == "end": async_res = finish_in_progress_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, now_datetime, past_due_only=past_due_only) return redirect("relate-monitor_task", async_res.id) elif op == "regrade": async_res = regrade_flow_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag, inprog_value=False) return redirect("relate-monitor_task", async_res.id) elif op == "recalculate": async_res = recalculate_ended_sessions.delay( pctx.course.id, opportunity.flow_id, rule_tag) return redirect("relate-monitor_task", async_res.id) else: raise SuspiciousOperation("invalid operation") else: batch_session_ops_form = ModifySessionsForm(session_rule_tags) # }}} # NOTE: It's important that these queries are sorted consistently, # also consistently with the code below. participations = list( Participation.objects.filter( course=pctx.course, status=participation_status.active).order_by( "id").select_related("user")) grade_changes = list( GradeChange.objects.filter(opportunity=opportunity).order_by( "participation__id", "grade_time").select_related("participation").select_related( "participation__user").select_related("opportunity")) idx = 0 finished_sessions = 0 total_sessions = 0 grade_table = [] for participation in participations: while (idx < len(grade_changes) and grade_changes[idx].participation.id < participation.id): idx += 1 my_grade_changes = [] while (idx < len(grade_changes) and grade_changes[idx].participation.pk == participation.pk): my_grade_changes.append(grade_changes[idx]) idx += 1 state_machine = GradeStateMachine() state_machine.consume(my_grade_changes) if opportunity.flow_id: flow_sessions = (FlowSession.objects.filter( participation=participation, flow_id=opportunity.flow_id, ).order_by("start_time")) for fsession in flow_sessions: total_sessions += 1 if not fsession.in_progress: finished_sessions += 1 else: flow_sessions = None grade_table.append( OpportunityGradeInfo(grade_state_machine=state_machine, flow_sessions=flow_sessions)) def grade_key(entry): (participation, grades) = entry return (participation.user.last_name.lower(), participation.user.first_name.lower()) grade_table = sorted(zip(participations, grade_table), key=grade_key) return render_course_page( pctx, "course/gradebook-by-opp.html", { "opportunity": opportunity, "participations": participations, "grade_state_change_types": grade_state_change_types, "grade_table": grade_table, "batch_session_ops_form": batch_session_ops_form, "total_sessions": total_sessions, "finished_sessions": finished_sessions, })