def submission_path_and_context(self): """ Determine the template path and context to use when rendering the response (submission) step. Returns: tuple of `(path, context)`, where `path` (str) is the path to the template, and `context` (dict) is the template context. """ workflow = self.get_workflow_info() problem_closed, reason, start_date, due_date = self.is_closed( 'submission') user_preferences = get_user_preferences( self.runtime.service(self, 'user')) path = 'openassessmentblock/response/oa_response.html' context = { 'user_timezone': user_preferences['user_timezone'], 'user_language': user_preferences['user_language'], "xblock_id": self.get_xblock_id(), "text_response": self.text_response, "file_upload_response": self.file_upload_response, } # Due dates can default to the distant future, in which case # there's effectively no due date. # If we don't add the date to the context, the template won't display it. if due_date < DISTANT_FUTURE: context["submission_due"] = due_date context['file_upload_type'] = self.file_upload_type context['allow_latex'] = self.allow_latex file_urls = None if self.file_upload_type: try: saved_files_descriptions = json.loads( self.saved_files_descriptions) except ValueError: saved_files_descriptions = [] file_urls = [] for i in range(self.MAX_FILES_COUNT): file_url = self._get_download_url(i) file_description = '' if file_url: try: file_description = saved_files_descriptions[i] except IndexError: pass file_urls.append((file_url, file_description)) else: break context['file_urls'] = file_urls if self.file_upload_type == 'custom': context['white_listed_file_types'] = self.white_listed_file_types if not workflow and problem_closed: if reason == 'due': path = 'openassessmentblock/response/oa_response_closed.html' elif reason == 'start': context['submission_start'] = start_date path = 'openassessmentblock/response/oa_response_unavailable.html' elif not workflow: # For backwards compatibility. Initially, problems had only one prompt # and a string answer. We convert it to the appropriate dict. try: json.loads(self.saved_response) saved_response = { 'answer': json.loads(self.saved_response), } except ValueError: saved_response = { 'answer': { 'text': self.saved_response, }, } context['saved_response'] = create_submission_dict( saved_response, self.prompts) context['save_status'] = self.save_status submit_enabled = True if self.text_response == 'required' and not self.saved_response: submit_enabled = False if self.file_upload_response == 'required' and not file_urls: submit_enabled = False if self.text_response == 'optional' and self.file_upload_response == 'optional' \ and not self.saved_response and not file_urls: submit_enabled = False context['submit_enabled'] = submit_enabled path = "openassessmentblock/response/oa_response.html" elif workflow["status"] == "cancelled": context[ "workflow_cancellation"] = self.get_workflow_cancellation_info( self.submission_uuid) context["student_submission"] = self.get_user_submission( workflow["submission_uuid"]) path = 'openassessmentblock/response/oa_response_cancelled.html' elif workflow["status"] == "done": student_submission = self.get_user_submission( workflow["submission_uuid"]) context["student_submission"] = create_submission_dict( student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_graded.html' else: student_submission = self.get_user_submission( workflow["submission_uuid"]) peer_in_workflow = "peer" in workflow["status_details"] self_in_workflow = "self" in workflow["status_details"] context["peer_incomplete"] = peer_in_workflow and not workflow[ "status_details"]["peer"]["complete"] context["self_incomplete"] = self_in_workflow and not workflow[ "status_details"]["self"]["complete"] context["student_submission"] = create_submission_dict( student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_submitted.html' return path, context
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Import is placed here to avoid model import at project startup. from openassessment.assessment.api import peer as peer_api from openassessment.assessment.api import self as self_api from openassessment.assessment.api import staff as staff_api from submissions import api as sub_api # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] staff_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: peer_api.get_score(submission_uuid, self.workflow_requirements()["peer"]) feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(peer_assessment) for peer_assessment in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) raw_staff_assessment = staff_api.get_latest_staff_assessment(submission_uuid) if raw_staff_assessment: staff_assessment = self._assessment_grade_context(raw_staff_assessment) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'has_submitted_feedback': has_submitted_feedback, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'grade_details': self.grade_details( submission_uuid, peer_assessments=peer_assessments, self_assessment=self_assessment, staff_assessment=staff_assessment, ), 'file_upload_type': self.file_upload_type, 'allow_latex': self.allow_latex, 'file_urls': self.get_download_urls_from_submission(student_submission), 'xblock_id': self.get_xblock_id() } return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(asmnt) for asmnt in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) if "example-based-assessment" in assessment_steps: example_based_assessment = self._assessment_grade_context( ai_api.get_latest_assessment(submission_uuid) ) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'example_based_assessment': example_based_assessment, 'rubric_criteria': self._rubric_criteria_grade_context(peer_assessments, self_assessment), 'has_submitted_feedback': has_submitted_feedback, 'allow_file_upload': self.allow_file_upload, 'allow_latex': self.allow_latex, 'file_url': self.get_download_url_from_submission(student_submission) } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) median_scores = None if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores(submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria(submission_uuid) elif "example-based-assessment" in assessment_steps: median_scores = ai_api.get_assessment_scores_by_criteria(submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: # Although we prevent course authors from modifying criteria post-release, # it's still possible for assessments created by course staff to # have criteria that differ from the current problem definition. # It's also possible to circumvent the post-release restriction # if course authors directly import a course into Studio. # If this happens, we simply leave the score blank so that the grade # section can render without error. criterion["median_score"] = median_scores.get(criterion["name"], '') criterion["total_value"] = max_scores.get(criterion["name"], '') return ('openassessmentblock/grade/oa_grade_complete.html', context)
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] staff_assessment = None example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: peer_api.get_score(submission_uuid, self.workflow_requirements()["peer"]) feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(peer_assessment) for peer_assessment in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) if "example-based-assessment" in assessment_steps: example_based_assessment = self._assessment_grade_context( ai_api.get_latest_assessment(submission_uuid) ) raw_staff_assessment = staff_api.get_latest_staff_assessment(submission_uuid) if raw_staff_assessment: staff_assessment = self._assessment_grade_context(raw_staff_assessment) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'has_submitted_feedback': has_submitted_feedback, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'grade_details': self.grade_details( submission_uuid, peer_assessments=peer_assessments, self_assessment=self_assessment, example_based_assessment=example_based_assessment, staff_assessment=staff_assessment, ), 'file_upload_type': self.file_upload_type, 'allow_latex': self.allow_latex, 'file_url': self.get_download_url_from_submission(student_submission) } return ('openassessmentblock/grade/oa_grade_complete.html', context)
def submission_path_and_context(self): """ Determine the template path and context to use when rendering the response (submission) step. Returns: tuple of `(path, context)`, where `path` (str) is the path to the template, and `context` (dict) is the template context. """ workflow = self.get_workflow_info() problem_closed, reason, start_date, due_date = self.is_closed("submission") path = "openassessmentblock/response/oa_response.html" context = {} # Due dates can default to the distant future, in which case # there's effectively no due date. # If we don't add the date to the context, the template won't display it. if due_date < DISTANT_FUTURE: context["submission_due"] = due_date context["file_upload_type"] = self.file_upload_type context["allow_latex"] = self.allow_latex context["has_peer"] = "peer-assessment" in self.assessment_steps context["has_self"] = "self-assessment" in self.assessment_steps if self.file_upload_type: context["file_url"] = self._get_download_url() if self.file_upload_type == "custom": context["white_listed_file_types"] = self.white_listed_file_types if not workflow and problem_closed: if reason == "due": path = "openassessmentblock/response/oa_response_closed.html" elif reason == "start": context["submission_start"] = start_date path = "openassessmentblock/response/oa_response_unavailable.html" elif not workflow: # For backwards compatibility. Initially, problems had only one prompt # and a string answer. We convert it to the appropriate dict. try: json.loads(self.saved_response) saved_response = {"answer": json.loads(self.saved_response)} except ValueError: saved_response = {"answer": {"text": self.saved_response}} context["saved_response"] = create_submission_dict(saved_response, self.prompts) context["save_status"] = self.save_status context["submit_enabled"] = self.saved_response != "" path = "openassessmentblock/response/oa_response.html" elif workflow["status"] == "cancelled": workflow_cancellation = workflow_api.get_assessment_workflow_cancellation(self.submission_uuid) if workflow_cancellation: workflow_cancellation["cancelled_by"] = self.get_username(workflow_cancellation["cancelled_by_id"]) context["workflow_cancellation"] = workflow_cancellation context["student_submission"] = self.get_user_submission(workflow["submission_uuid"]) path = "openassessmentblock/response/oa_response_cancelled.html" elif workflow["status"] == "done": student_submission = self.get_user_submission(workflow["submission_uuid"]) context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = "openassessmentblock/response/oa_response_graded.html" else: student_submission = self.get_user_submission(workflow["submission_uuid"]) context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = "openassessmentblock/response/oa_response_submitted.html" return path, context
def submission_path_and_context(self): """ Determine the template path and context to use when rendering the response (submission) step. Returns: tuple of `(path, context)`, where `path` (str) is the path to the template, and `context` (dict) is the template context. """ workflow = self.get_workflow_info() problem_closed, reason, start_date, due_date = self.is_closed('submission') path = 'openassessmentblock/response/oa_response.html' context = {} # Due dates can default to the distant future, in which case # there's effectively no due date. # If we don't add the date to the context, the template won't display it. if due_date < DISTANT_FUTURE: context["submission_due"] = due_date context['file_upload_type'] = self.file_upload_type context['allow_latex'] = self.allow_latex if self.file_upload_type: context['file_url'] = self._get_download_url() if self.file_upload_type == 'custom': context['white_listed_file_types'] = self.white_listed_file_types if not workflow and problem_closed: if reason == 'due': path = 'openassessmentblock/response/oa_response_closed.html' elif reason == 'start': context['submission_start'] = start_date path = 'openassessmentblock/response/oa_response_unavailable.html' elif not workflow: # For backwards compatibility. Initially, problems had only one prompt # and a string answer. We convert it to the appropriate dict. try: json.loads(self.saved_response) saved_response = { 'answer': json.loads(self.saved_response), } except ValueError: saved_response = { 'answer': { 'text': self.saved_response, }, } context['saved_response'] = create_submission_dict(saved_response, self.prompts) context['save_status'] = self.save_status context['submit_enabled'] = self.saved_response != '' path = "openassessmentblock/response/oa_response.html" elif workflow["status"] == "cancelled": context["workflow_cancellation"] = self.get_workflow_cancellation_info(self.submission_uuid) context["student_submission"] = self.get_user_submission( workflow["submission_uuid"] ) path = 'openassessmentblock/response/oa_response_cancelled.html' elif workflow["status"] == "done": student_submission = self.get_user_submission( workflow["submission_uuid"] ) context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_graded.html' else: student_submission = self.get_user_submission( workflow["submission_uuid"] ) peer_in_workflow = "peer" in workflow["status_details"] self_in_workflow = "self" in workflow["status_details"] context["peer_incomplete"] = peer_in_workflow and not workflow["status_details"]["peer"]["complete"] context["self_incomplete"] = self_in_workflow and not workflow["status_details"]["self"]["complete"] context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_submitted.html' return path, context
def submission_path_and_context(self): """ Determine the template path and context to use when rendering the response (submission) step. Returns: tuple of `(path, context)`, where `path` (str) is the path to the template, and `context` (dict) is the template context. """ workflow = self.get_workflow_info() problem_closed, reason, start_date, due_date = self.is_closed('submission') user_preferences = get_user_preferences(self.runtime.service(self, 'user')) path = 'openassessmentblock/response/oa_response.html' context = { 'user_timezone': user_preferences['user_timezone'], 'user_language': user_preferences['user_language'], "xblock_id": self.get_xblock_id(), "text_response": self.text_response, "file_upload_response": self.file_upload_response, "prompts_type": self.prompts_type, } # Due dates can default to the distant future, in which case # there's effectively no due date. # If we don't add the date to the context, the template won't display it. if due_date < DISTANT_FUTURE: context["submission_due"] = due_date context['file_upload_type'] = self.file_upload_type context['allow_latex'] = self.allow_latex file_urls = None if self.file_upload_type: try: saved_files_descriptions = json.loads(self.saved_files_descriptions) except ValueError: saved_files_descriptions = [] file_urls = [] for i in range(self.MAX_FILES_COUNT): file_url = self._get_download_url(i) file_description = '' if file_url: try: file_description = saved_files_descriptions[i] except IndexError: pass file_urls.append((file_url, file_description)) else: break context['file_urls'] = file_urls if self.file_upload_type == 'custom': context['white_listed_file_types'] = self.white_listed_file_types if not workflow and problem_closed: if reason == 'due': path = 'openassessmentblock/response/oa_response_closed.html' elif reason == 'start': context['submission_start'] = start_date path = 'openassessmentblock/response/oa_response_unavailable.html' elif not workflow: # For backwards compatibility. Initially, problems had only one prompt # and a string answer. We convert it to the appropriate dict. try: json.loads(self.saved_response) saved_response = { 'answer': json.loads(self.saved_response), } except ValueError: saved_response = { 'answer': { 'text': self.saved_response, }, } context['saved_response'] = create_submission_dict(saved_response, self.prompts) context['save_status'] = self.save_status submit_enabled = True if self.text_response == 'required' and not self.saved_response: submit_enabled = False if self.file_upload_response == 'required' and not file_urls: submit_enabled = False if self.text_response == 'optional' and self.file_upload_response == 'optional' \ and not self.saved_response and not file_urls: submit_enabled = False context['submit_enabled'] = submit_enabled path = "openassessmentblock/response/oa_response.html" elif workflow["status"] == "cancelled": context["workflow_cancellation"] = self.get_workflow_cancellation_info(self.submission_uuid) context["student_submission"] = self.get_user_submission( workflow["submission_uuid"] ) path = 'openassessmentblock/response/oa_response_cancelled.html' elif workflow["status"] == "done": student_submission = self.get_user_submission( workflow["submission_uuid"] ) context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_graded.html' else: student_submission = self.get_user_submission( workflow["submission_uuid"] ) peer_in_workflow = "peer" in workflow["status_details"] self_in_workflow = "self" in workflow["status_details"] context["peer_incomplete"] = peer_in_workflow and not workflow["status_details"]["peer"]["complete"] context["self_incomplete"] = self_in_workflow and not workflow["status_details"]["self"]["complete"] context["student_submission"] = create_submission_dict(student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_submitted.html' return path, context
def submission_path_and_context(self): """ Determine the template path and context to use when rendering the response (submission) step. Returns: tuple of `(path, context)`, where `path` (str) is the path to the template, and `context` (dict) is the template context. """ workflow = self.get_workflow_info() problem_closed, reason, start_date, due_date = self.is_closed( 'submission') path = 'openassessmentblock/response/oa_response.html' context = {} # Due dates can default to the distant future, in which case # there's effectively no due date. # If we don't add the date to the context, the template won't display it. if due_date < DISTANT_FUTURE: context["submission_due"] = due_date context['file_upload_type'] = self.file_upload_type context['allow_latex'] = self.allow_latex context['has_peer'] = 'peer-assessment' in self.assessment_steps context['has_self'] = 'self-assessment' in self.assessment_steps if self.file_upload_type: context['file_url'] = self._get_download_url() if self.file_upload_type == 'custom': context['white_listed_file_types'] = self.white_listed_file_types if not workflow and problem_closed: if reason == 'due': path = 'openassessmentblock/response/oa_response_closed.html' elif reason == 'start': context['submission_start'] = start_date path = 'openassessmentblock/response/oa_response_unavailable.html' elif not workflow: # For backwards compatibility. Initially, problems had only one prompt # and a string answer. We convert it to the appropriate dict. try: json.loads(self.saved_response) saved_response = { 'answer': json.loads(self.saved_response), } except ValueError: saved_response = { 'answer': { 'text': self.saved_response, }, } context['saved_response'] = create_submission_dict( saved_response, self.prompts) context['save_status'] = self.save_status context['submit_enabled'] = self.saved_response != '' path = "openassessmentblock/response/oa_response.html" elif workflow["status"] == "cancelled": workflow_cancellation = workflow_api.get_assessment_workflow_cancellation( self.submission_uuid) if workflow_cancellation: workflow_cancellation['cancelled_by'] = self.get_username( workflow_cancellation['cancelled_by_id']) context['workflow_cancellation'] = workflow_cancellation context["student_submission"] = self.get_user_submission( workflow["submission_uuid"]) path = 'openassessmentblock/response/oa_response_cancelled.html' elif workflow["status"] == "done": student_submission = self.get_user_submission( workflow["submission_uuid"]) context["student_submission"] = create_submission_dict( student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_graded.html' else: student_submission = self.get_user_submission( workflow["submission_uuid"]) context["student_submission"] = create_submission_dict( student_submission, self.prompts) path = 'openassessmentblock/response/oa_response_submitted.html' return path, context
def render_grade_complete(self, workflow): """ Render the grade complete state. Args: workflow (dict): The serialized Workflow model. Returns: tuple of context (dict), template_path (string) """ # Peer specific stuff... assessment_steps = self.assessment_steps submission_uuid = workflow['submission_uuid'] example_based_assessment = None self_assessment = None feedback = None peer_assessments = [] has_submitted_feedback = False if "peer-assessment" in assessment_steps: feedback = peer_api.get_assessment_feedback(submission_uuid) peer_assessments = [ self._assessment_grade_context(asmnt) for asmnt in peer_api.get_assessments(submission_uuid) ] has_submitted_feedback = feedback is not None if "self-assessment" in assessment_steps: self_assessment = self._assessment_grade_context( self_api.get_assessment(submission_uuid) ) if "example-based-assessment" in assessment_steps: example_based_assessment = self._assessment_grade_context( ai_api.get_latest_assessment(submission_uuid) ) feedback_text = feedback.get('feedback', '') if feedback else '' student_submission = sub_api.get_submission(submission_uuid) # We retrieve the score from the workflow, which in turn retrieves # the score for our current submission UUID. # We look up the score by submission UUID instead of student item # to ensure that the score always matches the rubric. # It's possible for the score to be `None` even if the workflow status is "done" # when all the criteria in the rubric are feedback-only (no options). score = workflow['score'] context = { 'score': score, 'feedback_text': feedback_text, 'student_submission': create_submission_dict(student_submission, self.prompts), 'peer_assessments': peer_assessments, 'self_assessment': self_assessment, 'example_based_assessment': example_based_assessment, 'rubric_criteria': self._rubric_criteria_grade_context(peer_assessments, self_assessment), 'has_submitted_feedback': has_submitted_feedback, 'file_upload_type': self.file_upload_type, 'allow_latex': self.allow_latex, 'file_url': self.get_download_url_from_submission(student_submission) } # Update the scores we will display to the user # Note that we are updating a *copy* of the rubric criteria stored in # the XBlock field max_scores = peer_api.get_rubric_max_scores(submission_uuid) median_scores = None if "peer-assessment" in assessment_steps: median_scores = peer_api.get_assessment_median_scores(submission_uuid) elif "self-assessment" in assessment_steps: median_scores = self_api.get_assessment_scores_by_criteria(submission_uuid) elif "example-based-assessment" in assessment_steps: median_scores = ai_api.get_assessment_scores_by_criteria(submission_uuid) if median_scores is not None and max_scores is not None: for criterion in context["rubric_criteria"]: # Although we prevent course authors from modifying criteria post-release, # it's still possible for assessments created by course staff to # have criteria that differ from the current problem definition. # It's also possible to circumvent the post-release restriction # if course authors directly import a course into Studio. # If this happens, we simply leave the score blank so that the grade # section can render without error. criterion["median_score"] = median_scores.get(criterion["name"], '') criterion["total_value"] = max_scores.get(criterion["name"], '') return ('openassessmentblock/grade/oa_grade_complete.html', context)