def grade_page_visits(fctx, flow_session, answer_visits, force_regrade=False): for i in range(len(answer_visits)): answer_visit = answer_visits[i] if answer_visit is not None: answer_visit.is_graded_answer = True answer_visit.save() else: page_data = flow_session.page_data.get(ordinal=i) page = instantiate_flow_page_with_ctx(fctx, page_data) if not page.expects_answer(): continue # Create a synthetic visit to attach a grade answer_visit = FlowPageVisit() answer_visit.flow_session = flow_session answer_visit.page_data = page_data answer_visit.is_synthetic = True answer_visit.answer = None answer_visit.is_graded_answer = True answer_visit.save() answer_visits[i] = answer_visit if (answer_visit is not None and (not answer_visit.grades.count() or force_regrade)): grade_page_visit(answer_visit, graded_at_git_commit_sha=fctx.flow_commit_sha)
def grade_page_visits(fctx, flow_session, answer_visits): for i in range(len(answer_visits)): answer_visit = answer_visits[i] if answer_visit is not None: answer_visit.is_graded_answer = True answer_visit.save() else: page_data = flow_session.page_data.get(ordinal=i) page = instantiate_flow_page_with_ctx(fctx, page_data) if not page.expects_answer(): continue # Create a synthetic visit to attach a grade answer_visit = FlowPageVisit() answer_visit.flow_session = flow_session answer_visit.page_data = page_data answer_visit.is_synthetic = True answer_visit.answer = None answer_visit.is_graded_answer = True answer_visit.save() answer_visits[i] = answer_visit if answer_visit is not None: grade_page_visit(answer_visit)
def count_answered_gradable(fctx, flow_session, answer_visits): all_page_data = FlowPageData.objects.filter(flow_session=flow_session, ordinal__isnull=False).order_by("ordinal") answered_count = 0 unanswered_count = 0 for i, page_data in enumerate(all_page_data): assert i == page_data.ordinal if answer_visits[i] is not None: answer_data = answer_visits[i].answer else: answer_data = None page = instantiate_flow_page_with_ctx(fctx, page_data) if page.expects_answer() and page.is_answer_gradable(): if answer_data is None: unanswered_count += 1 else: answered_count += 1 return (answered_count, unanswered_count)
def get_flow_session_content(api_ctx, course_identifier): # type: (APIContext, Text) -> http.HttpResponse if not api_ctx.has_permission(pperm.view_gradebook): raise PermissionDenied("token role does not have required permissions") try: session_id_str = api_ctx.request.GET["flow_session_id"] except KeyError: raise APIError("must specify flow_id GET parameter") session_id = int(session_id_str) flow_session = get_object_or_404(FlowSession, id=session_id) if flow_session.course != api_ctx.course: raise PermissionDenied( "session's course does not match auth context") from course.content import get_course_repo from course.flow import adjust_flow_session_page_data, assemble_answer_visits with get_course_repo(api_ctx.course) as repo: from course.utils import FlowContext, instantiate_flow_page_with_ctx fctx = FlowContext(repo, api_ctx.course, flow_session.flow_id) adjust_flow_session_page_data(repo, flow_session, api_ctx.course.identifier, fctx.flow_desc) from course.flow import get_all_page_data all_page_data = get_all_page_data(flow_session) answer_visits = assemble_answer_visits(flow_session) pages = [] for i, page_data in enumerate(all_page_data): page = instantiate_flow_page_with_ctx(fctx, page_data) assert i == page_data.page_ordinal page_data_json = dict( ordinal=i, page_type=page_data.page_type, group_id=page_data.group_id, page_id=page_data.page_id, page_data=page_data.data, title=page_data.title, bookmarked=page_data.bookmarked, ) answer_json = None grade_json = None visit = answer_visits[i] if visit is not None: from course.page.base import PageContext pctx = PageContext(api_ctx.course, repo, fctx.course_commit_sha, flow_session) norm_bytes_answer_tup = page.normalized_bytes_answer( pctx, page_data.data, visit.answer) # norm_answer needs to be JSON-encodable norm_answer = None # type: Any if norm_bytes_answer_tup is not None: answer_file_ext, norm_bytes_answer = norm_bytes_answer_tup if answer_file_ext in [".txt", ".py"]: norm_answer = norm_bytes_answer.decode("utf-8") elif answer_file_ext == ".json": import json norm_answer = json.loads(norm_bytes_answer) else: from base64 import b64encode norm_answer = [answer_file_ext, b64encode(norm_bytes_answer).decode("utf-8")] answer_json = dict( visit_time=visit.visit_time.isoformat(), remote_address=repr(visit.remote_address), user=visit.user.username if visit.user is not None else None, impersonated_by=(visit.impersonated_by.username if visit.impersonated_by is not None else None), is_synthetic_visit=visit.is_synthetic, answer_data=visit.answer, answer=norm_answer, ) grade = visit.get_most_recent_grade() if grade is not None: grade_json = dict( grader=(grade.grader.username if grade.grader is not None else None), grade_time=grade.grade_time.isoformat(), graded_at_git_commit_sha=grade.graded_at_git_commit_sha, max_points=grade.max_points, correctness=grade.correctness, feedback=grade.feedback) pages.append({ "page": page_data_json, "answer": answer_json, "grade": grade_json, }) result = { "session": flow_session_to_json(flow_session), "pages": pages, } return http.JsonResponse(result, safe=False)
def get_flow_session_content(api_ctx, course_identifier): # type: (APIContext, Text) -> http.HttpResponse if not api_ctx.has_permission(pperm.view_gradebook): raise PermissionDenied("token role does not have required permissions") try: session_id_str = api_ctx.request.GET["flow_session_id"] except KeyError: raise APIError("must specify flow_id GET parameter") session_id = int(session_id_str) flow_session = get_object_or_404(FlowSession, id=session_id) if flow_session.course != api_ctx.course: raise PermissionDenied( "session's course does not match auth context") from course.content import get_course_repo from course.flow import adjust_flow_session_page_data, assemble_answer_visits with get_course_repo(api_ctx.course) as repo: from course.utils import FlowContext, instantiate_flow_page_with_ctx fctx = FlowContext(repo, api_ctx.course, flow_session.flow_id) adjust_flow_session_page_data(repo, flow_session, api_ctx.course.identifier, fctx.flow_desc) from course.flow import get_all_page_data all_page_data = get_all_page_data(flow_session) answer_visits = assemble_answer_visits(flow_session) pages = [] for i, page_data in enumerate(all_page_data): page = instantiate_flow_page_with_ctx(fctx, page_data) assert i == page_data.page_ordinal page_data_json = dict( ordinal=i, page_type=page_data.page_type, group_id=page_data.group_id, page_id=page_data.page_id, page_data=page_data.data, title=page_data.title, bookmarked=page_data.bookmarked, ) answer_json = None grade_json = None visit = answer_visits[i] if visit is not None: from course.page.base import PageContext pctx = PageContext(api_ctx.course, repo, fctx.course_commit_sha, flow_session) norm_bytes_answer_tup = page.normalized_bytes_answer( pctx, page_data.data, visit.answer) # norm_answer needs to be JSON-encodable norm_answer = None # type: Any if norm_bytes_answer_tup is not None: answer_file_ext, norm_bytes_answer = norm_bytes_answer_tup if answer_file_ext in [".txt", ".py"]: norm_answer = norm_bytes_answer.decode("utf-8") elif answer_file_ext == ".json": import json norm_answer = json.loads(norm_bytes_answer) else: from base64 import b64encode norm_answer = [answer_file_ext, b64encode(norm_bytes_answer)] answer_json = dict( visit_time=visit.visit_time.isoformat(), remote_address=repr(visit.remote_address), user=visit.user.username if visit.user is not None else None, impersonated_by=(visit.impersonated_by.username if visit.impersonated_by is not None else None), is_synthetic_visit=visit.is_synthetic, answer_data=visit.answer, answer=norm_answer, ) grade = visit.get_most_recent_grade() if grade is not None: grade_json = dict( grader=grade.grader, grade_time=grade.grade_time.isoformat(), graded_at_git_commit_sha=grade.graded_at_git_commit_sha, max_points=grade.max_points, correctness=grade.correctness, feedback=grade.feedback) pages.append({ "page": page_data_json, "answer": answer_json, "grade": grade_json, }) result = { "session": flow_session_to_json(flow_session), "pages": pages, } return http.JsonResponse(result, safe=False)
def gather_grade_info(fctx, flow_session, answer_visits): """ :returns: a :class:`GradeInfo` """ all_page_data = FlowPageData.objects.filter(flow_session=flow_session, ordinal__isnull=False).order_by("ordinal") points = 0 provisional_points = 0 max_points = 0 max_reachable_points = 0 fully_correct_count = 0 partially_correct_count = 0 incorrect_count = 0 unknown_count = 0 for i, page_data in enumerate(all_page_data): page = instantiate_flow_page_with_ctx(fctx, page_data) assert i == page_data.ordinal if answer_visits[i] is None: # This is true in principle, but early code to deal with survey questions # didn't generate synthetic answer visits for survey questions, so this # can't actually be enforced. # assert not page.expects_answer() continue if not page.is_answer_gradable(): continue grade = answer_visits[i].get_most_recent_grade() assert grade is not None feedback = get_feedback_for_grade(grade) max_points += grade.max_points if feedback is None or feedback.correctness is None: unknown_count += 1 points = None continue max_reachable_points += grade.max_points page_points = grade.max_points * feedback.correctness if points is not None: points += page_points provisional_points += page_points if grade.max_points > 0: if feedback.correctness == 1: fully_correct_count += 1 elif feedback.correctness == 0: incorrect_count += 1 else: partially_correct_count += 1 return GradeInfo( points=points, provisional_points=provisional_points, max_points=max_points, max_reachable_points=max_reachable_points, fully_correct_count=fully_correct_count, partially_correct_count=partially_correct_count, incorrect_count=incorrect_count, unknown_count=unknown_count, )