Commit 757b05ec by Stephen Sanchez Committed by Brian Talbot

Adding all templates and rendering for peer and self assessment.

parent b2df1f79
......@@ -196,7 +196,7 @@ def _score_if_finished(student_item,
return
finished_evaluating = has_finished_required_evaluating(
student_item.student_id,
StudentItemSerializer(student_item).data,
required_assessments_for_student
)
assessments = Assessment.objects.filter(submission=submission)
......@@ -255,23 +255,26 @@ def get_assessment_median_scores(submission_id, must_be_graded_by):
raise PeerAssessmentInternalError(error_message)
def has_finished_required_evaluating(student_id, required_assessments):
def has_finished_required_evaluating(student_item_dict, required_assessments):
"""Check if a student still needs to evaluate more submissions
Per the contract of the peer assessment workflow, a student must evaluate a
number of peers before receiving feedback on their submission.
Args:
student_id (str): The student in the peer grading workflow to check for
peer workflow criteria. This argument is required.
student_item (dict): The student id is required to determine if the
student has completed enough assessments, relative to the item id
and course id available in the student item. This argument is
required.
required_assessments (int): The number of assessments a student has to
submit before receiving the feedback on their submission. This is a
required argument.
Returns:
bool: True if the student has evaluated enough peer submissions to move
tuple: True if the student has evaluated enough peer submissions to move
through the peer assessment workflow. False if the student needs to
evaluate more peer submissions.
evaluate more peer submissions. The second value is the count of
assessments completed.
Raises:
PeerAssessmentRequestError: Raised when the student_id is invalid, or
......@@ -280,16 +283,31 @@ def has_finished_required_evaluating(student_id, required_assessments):
while evaluating this workflow rule.
Examples:
>>> has_finished_required_evaluating("Tim", 3)
True
>>> student_item_dict = dict(
>>> item_id="item_1",
>>> course_id="course_1",
>>> item_type="type_one",
>>> student_id="Bob",
>>> )
>>> has_finished_required_evaluating(student_item_dict, 3)
True, 3
"""
if required_assessments < 0:
raise PeerAssessmentRequestError(
"Required Assessment count must be a positive integer.")
return Assessment.objects.filter(
scorer_id=student_id
).count() >= required_assessments
student_items = StudentItem.objects.filter(
item_id=student_item_dict["item_id"],
course_id=student_item_dict["course_id"]
)
submissions = Submission.objects.filter(
student_item__in=student_items
)
count = Assessment.objects.filter(
submission__in=submissions,
scorer_id=student_item_dict["student_id"]
).count()
return count >= required_assessments, count
def get_assessments(submission_id):
......
......@@ -166,27 +166,27 @@ class TestApi(TestCase):
scores = sub_api.get_score(STUDENT_ITEM)
self.assertFalse(scores)
self.assertFalse(peer_api.has_finished_required_evaluating("Tim", REQUIRED_GRADED))
self.assertEquals((False, 0), peer_api.has_finished_required_evaluating(STUDENT_ITEM, REQUIRED_GRADED))
peer_api.create_assessment(
bob["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT, RUBRIC_DICT
)
peer_api.create_assessment(
sally["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT, RUBRIC_DICT
)
self.assertFalse(peer_api.has_finished_required_evaluating("Tim", REQUIRED_GRADED))
self.assertEquals((False, 2), peer_api.has_finished_required_evaluating(STUDENT_ITEM, REQUIRED_GRADED))
peer_api.create_assessment(
jim["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT, RUBRIC_DICT
)
self.assertFalse(peer_api.has_finished_required_evaluating("Tim", REQUIRED_GRADED))
self.assertEquals((False, 3), peer_api.has_finished_required_evaluating(STUDENT_ITEM, REQUIRED_GRADED))
peer_api.create_assessment(
buffy["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT, RUBRIC_DICT
)
self.assertFalse(peer_api.has_finished_required_evaluating("Tim", REQUIRED_GRADED))
self.assertEquals((False, 4), peer_api.has_finished_required_evaluating(STUDENT_ITEM, REQUIRED_GRADED))
peer_api.create_assessment(
xander["uuid"], "Tim", REQUIRED_GRADED, REQUIRED_GRADED_BY, ASSESSMENT_DICT, RUBRIC_DICT
)
self.assertTrue(peer_api.has_finished_required_evaluating("Tim", REQUIRED_GRADED))
self.assertEquals((True, 5), peer_api.has_finished_required_evaluating(STUDENT_ITEM, REQUIRED_GRADED))
# Tim should not have a score, because his submission does not have
# enough assessments.
......@@ -212,7 +212,7 @@ class TestApi(TestCase):
@raises(peer_api.PeerAssessmentRequestError)
def test_bad_configuration(self):
peer_api.has_finished_required_evaluating("Tim", -1)
peer_api.has_finished_required_evaluating(STUDENT_ITEM, -1)
def test_get_submission_to_evaluate(self):
self._create_student_and_submission("Tim", "Tim's answer", MONDAY)
......
......@@ -11,19 +11,17 @@
<!-- CASE: default/not started -->
{% block list_item %}
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment ui-toggle-visibility is--expanded">
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment ui-toggle-visibility">
{% endblock %}
<span class="system__element" id="peer_submission_uuid">
{{ peer_submission.uuid }}
</span>
{% block header %}
<header class="step__header ui-toggle-visibility__control">
<h2 class="step__title">
<span class="step__counter"></span>
<span class="wrapper--copy">
<span class="step__label">Evaluate Peers' Responses</span>
<span class="step__deadline">due <span class="date">January 30, 2014</span> at <span class="time">15:00 UTC</span></span>
<span class="step__deadline">due <span class="date">{{ formatted_due_datetime }}</span></span>
</span>
</header>
......@@ -66,7 +64,7 @@
<ul class="list list--actions">
<li class="list--actions__item">
<button type="submit" id="peer-assessment--001__assessment__submit" class="action action--submit">Submit your assessment &amp; move to response #2</button>
<button type="submit" id="peer-assessment--001__assessment__submit" class="action action--submit">{{ submit_button_text }}</button>
</li>
</ul>
</form>
......
{% extends "openassessmentblock/oa_peer_assessment.html" %}
{% extends "openassessmentblock/peer/oa_peer_assessment.html" %}
{% block list_item %}
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment is--collapsed ui-toggle-visibility">
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment is--collapsed ui-toggle-visibility">
{% endblock %}
{% block body %}
{% endblock %}
{% extends "openassessmentblock/peer/oa_peer_assessment.html" %}
<!-- CASE: started, but incomplete and problem closed -->
{% block list_item %}
<li id="openassessment__peer-assessment"class="openassessment__steps__step step--peer-assessment is--unavailable ui-toggle-visibility is--collapsed">
{% endblock %}
{% block title %}
<span class="step__status">
<span class="ico"><i class="fa fa-info-circle"></i></span>
<span class="step__status__label">This step's status:</span>
<span class="step__status__value">
<span class="step__status__value--completed">{{ graded }}</span> of
<span class="step__status__value--required">{{ must_grade }}</span> completed
</span>
</span>
{% endblock %}
{% block body %}{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/peer/oa_peer_assessment.html" %}
<!-- CASE: completed -->
{% block list_item %}
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment ui-toggle-visibility is--collapsed">
{% endblock %}
{% block title %}
<span class="step__status">
<span class="ico"><i class="fa fa-check"></i></span>
<span class="step__status__label">This step's status:</span>
<span class="step__status__value">
<span class="step__status__value--completed">{{ graded }}</span> of
<span class="step__status__value--required">{{ must_grade }}</span> completed
</span>
</span>
{% endblock %}
{% block body %}{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/peer/oa_peer_assessment.html" %}
<!-- CASE: no peer responses to assess -->
{% block list_item %}
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment is--unavailable ui-toggle-visibility is--collapsed">
{% endblock %}
{% block title %}
<span class="step__status">
<span class="ico"><i class="fa fa-info-circle"></i></span>
<span class="step__status__label">This step's status:</span>
<span class="step__status__value">
Awaiting Peer Responses
</span>
</span>
{% endblock %}
{% block body %}{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/oa_response.html" %}
{% extends "openassessmentblock/response/oa_response.html" %}
{% block list_item %}
<li id="openassessment__response" class="openassessment__steps__step step--response is--unavailable ui-toggle-visibility">
{% endblock %}
{% block body %}
<div class="step__instruction">
<p>You did not complete this portion of the problem before its due date.</p>
</div>
{% endblock %}
{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/oa_response.html" %}
{% extends "openassessmentblock/response/oa_response.html" %}
{% block list_item %}
<li id="openassessment__response" class="openassessment__steps__step step--response is--collapsed ui-toggle-visibility">
<li id="openassessment__response" class="openassessment__steps__step step--response is--collapsed ui-toggle-visibility">
{% endblock %}
{% block body %}
{% endblock %}
{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/oa_response.html" %}
{% extends "openassessmentblock/response/oa_response.html" %}
{% block list_item %}
<li id="openassessment__response" class="openassessment__steps__step step--response is--graded is--collapsed ui-toggle-visibility">
{% endblock %}
{% block body %}
<div class="step__content">
<!-- user's response -->
......@@ -84,4 +82,4 @@
</ul>
</form>
</div>
{% endblock %}
{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/oa_response.html" %}
{% extends "openassessmentblock/response/oa_response.html" %}
{% block list_item %}
<li id="openassessment__response" class="openassessment__steps__step step--response is--submitted is--unavailable ui-toggle-visibility is--expanded">
<li id="openassessment__response" class="openassessment__steps__step step--response is--submitted is--unavailable ui-toggle-visibility">
{% endblock %}
{% block body %}
<div class="step__content">
<!-- user's response -->
......@@ -15,4 +13,4 @@
</div>
</article>
</div>
{% endblock %}
{% endblock %}
\ No newline at end of file
......@@ -12,29 +12,23 @@
<!-- CASE: default/not started -->
{% block list_item %}
<li id="openassessment__self-assessment" class="openassessment__steps__step step--self-assessment ui-toggle-visibility is--expanded">
<li id="openassessment__self-assessment" class="openassessment__steps__step step--self-assessment">
{% endblock %}
{% block header %}
<header class="step__header ui-toggle-visibility__control">
<header class="step__header">
<h2 class="step__title">
<span class="step__counter"></span>
<span class="wrapper--copy">
<span class="step__label">Evaluate Your Response</span>
<span class="step__deadline">due <span class="date">January 31, 2014</span> at <span class="time">15:00 UTC</span></span>
<span class="step__title__label">Evaluate Your Response</span>
<span class="step__deadline">due <span class="date">{{ formatted_due_datetime }}</span></span>
</span>
</h2>
{% block title %}
<span class="step__status">
<span class="step__status__label">This step's status:</span>
<span class="step__status__value">
<i class="ico fa fa-check"></i>
<span class="copy">{{ step_status }}</span>
</span>
<span class="step__status__label">This step's status:</span>
<span class="step_status_value">{{ step_status }}</span>
</span>
</header>
{% endblock %}
</header>
{% block body %}
<div class="step--content">
<article class="self-assessment" id="self-assessment">
......@@ -56,18 +50,23 @@
<!-- individual rubric question (radio-based choice) -->
<li class="field field--radio is--required assessment__rubric__question" id="assessment__rubric__question--{{ criterion.name }}">
<h4 class="question__title">
{{ criterion.instructions }}
{{ criterion.prompt }}
<span class="label--required">* <span class="sr">(Required)</span></span>
</h4>
<ol class="question__answers">
{% for value, text in criterion.options %}
{% for option in criterion.options %}
<li class="answer">
<div class="wrapper--input">
<input type="radio" name="assessment__rubric__question--{{ criterion.name }}" id="assessment__rubric__question--{{ criterion.name }}--01" class="answer__value" value="answer--001__option--01 - Very Well" />
<label for="assessment__rubric__question--001__option--01" class="answer__label">({{ value }}) {{ text }}</label>
<input type="radio"
name="{{ criterion.name }}"
id="assessment__rubric__question--{{ criterion.name }}"
class="answer__value"
value="{{ option.name }}" />
<label for="assessment__rubric__question--001__option--01"
class="answer__label">{{ option.name }}</label>
</div>
<span class="answer__tip">TODO: Criterion Instructions</span>
<span class="answer__tip">{{ option.explanation }}</span>
</li>
{% endfor %}
</ol>
......
{% extends "openassessmentblock/oa_self_assessment.html" %}
{% extends "openassessmentblock/self/oa_self_assessment.html" %}
{% block list_item %}
<li id="openassessment__self-assessment" class="openassessment__steps__step is--collapsed step--self-assessment">
<li id="openassessment__self-assessment" class="openassessment__steps__step is--collapsed step--self-assessment">
{% endblock %}
{% block body %}
{% endblock %}
{% extends "openassessmentblock/self/oa_self_assessment.html" %}
<!-- CASE: not started and problem closed -->
{% block list_item %}
<li id="openassessment__self-assessment" class="openassessment__steps__step step--self-assessment is--unavailable ui-toggle-visibility is--collapsed">
{% endblock %}
{% block title %}
<span class="step__status">
<span class="ico"><i class="fa fa-info-circle"></i></span>
<span class="step__status__label">This step's status:</span>
<span class="step_status_value">{{ step_status }}</span>
</span>
{% endblock %}
{% block body %}{% endblock %}
\ No newline at end of file
{% extends "openassessmentblock/self/oa_self_assessment.html" %}
<!-- CASE: complete -->
{% block list_item %}
<li id="openassessment__self-assessment" class="openassessment__steps__step step--self-assessment ui-toggle-visibility is--collapsed">
{% endblock %}
{% block title %}
<span class="step__status">
<span class="ico"><i class="fa fa-check"></i></span>
<span class="step__status__label">This step's status:</span>
<span class="step_status_value">{{ step_status }}</span>
</span>
{% endblock %}
{% block body %}{% endblock %}
\ No newline at end of file
......@@ -67,14 +67,45 @@ class PeerAssessmentMixin(object):
more information on rendering XBlock sections.
"""
context_dict = {
"rubric_instructions": self.rubric_instructions,
"rubric_criteria": self.rubric_criteria,
"estimated_time": "20 minutes" # TODO: Need to configure this.
}
path = 'openassessmentblock/peer/oa_peer_waiting.html'
assessment = self.get_assessment_module('peer-assessment')
if assessment:
peer_sub = self.get_peer_submission(self.get_student_item_dict(), assessment)
context_dict = {
"peer_submission": peer_sub,
"rubric_criteria": self.rubric_criteria
}
return self.render_assessment('openassessmentblock/oa_peer_assessment.html', context_dict)
context_dict["must_grade"] = assessment["must_grade"]
student_item = self.get_student_item_dict()
finished, count = peer_api.has_finished_required_evaluating(
student_item,
assessment["must_grade"]
)
context_dict["graded"] = count
if finished:
path = "openassessmentblock/peer/oa_peer_complete.html"
else:
peer_sub = self.get_peer_submission(student_item, assessment)
if peer_sub:
path = 'openassessmentblock/peer/oa_peer_assessment.html'
context_dict["peer_submission"] = peer_sub
if assessment["must_grade"] - count == 1:
context_dict["submit_button_text"] = "Submit your assessment & move onto next step."
else:
context_dict["submit_button_text"] = "Submit your assessment & move to response #{}".format(count + 1)
problem_open, date = self.is_open()
if not problem_open and date == "due" and not finished:
path = 'openassessmentblock/peer/oa_peer_closed.html'
return self.render_assessment(path, context_dict)
def get_peer_submission(self, student_item_dict, assessment):
peer_submission = False
......@@ -102,4 +133,4 @@ class PeerAssessmentMixin(object):
@XBlock.handler
def render_peer_assessment_collapsed(self, data, suffix=''):
return self.render_assessment("openassessmentblock/oa_peer_assessment_collapsed.html")
return self.render_assessment("openassessmentblock/peer/oa_peer_assessment_collapsed.html")
......@@ -16,9 +16,9 @@ class SelfAssessmentMixin(object):
@XBlock.handler
def render_self_assessment(self, data, suffix=''):
return self.render_assessment('openassessmentblock/oa_self_assessment.html')
return self.render_assessment('openassessmentblock/self/oa_self_assessment.html')
@XBlock.handler
def render_self_assessment_collapsed(self, data, suffix=''):
return self.render_assessment("openassessmentblock/oa_self_assessment_collapsed.html")
return self.render_assessment("openassessmentblock/self/oa_self_assessment_collapsed.html")
......@@ -53,7 +53,7 @@ function OpenAssessmentBlock(runtime, element) {
criteriaChoices[selector[i].name] = selector[i].value
}
return {
"submission_uuid":$("div#peer_submission_uuid")[0].innerText,
"submission_uuid":$("span#peer_submission_uuid")[0].innerText,
"points_earned":values,
"options_selected":criteriaChoices
};
......
import datetime
from xblock.core import XBlock
from submissions import api
from openassessment.peer import api as peer_api
......@@ -148,7 +147,7 @@ class SubmissionMixin(object):
"step_status": step_status,
}
path = "openassessmentblock/oa_response.html"
path = "openassessmentblock/response/oa_response.html"
if student_score:
assessments = peer_api.get_assessments(student_submission["uuid"])
median_scores = peer_api.get_assessment_median_scores(
......@@ -160,14 +159,14 @@ class SubmissionMixin(object):
for criterion in context["rubric_criteria"]:
criterion["median_score"] = median_scores[criterion["name"]]
path = 'openassessmentblock/oa_response_graded.html'
path = 'openassessmentblock/response/oa_response_graded.html'
elif student_submission:
path = 'openassessmentblock/oa_response_submitted.html'
path = 'openassessmentblock/response/oa_response_submitted.html'
elif not problem_open and date == "due" and not student_submission:
path = 'openassessmentblock/oa_response_closed.html'
path = 'openassessmentblock/response/oa_response_closed.html'
return self.render_assessment(path, context_dict=context)
@XBlock.handler
def render_submission_collapsed(self, data, suffix=''):
return self.render_assessment("openassessmentblock/oa_response_collapsed.html")
return self.render_assessment("openassessmentblock/response/oa_response_collapsed.html")
......@@ -141,10 +141,21 @@ class TestOpenAssessment(TestCase):
xblock_fragment = self.runtime.render(self.assessment, "student_view")
self.assertTrue(xblock_fragment.body_html().find("Openassessmentblock"))
# Validate Submission Rendering.
submission_response = self.assessment.render_submission({})
self.assertIsNotNone(submission_response)
self.assertTrue(submission_response.body.find("openassessment__response"))
# Validate Peer Rendering.
peer_response = self.assessment.render_peer_assessment({})
self.assertIsNotNone(peer_response)
self.assertTrue(peer_response.body.find("openassessment__peer-assessment"))
# Validate Self Rendering.
self_response = self.assessment.render_self_assessment({})
self.assertIsNotNone(self_response)
self.assertTrue(self_response.body.find("openassessment__peer-assessment"))
def test_start_end_date_checks(self):
"""
Check if the start and end date checks work appropriately.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment