Commit 61ab0d3a by Stephen Sanchez

Changing to mixins

parent e39b9718
from django.template import Context
from django.template.loader import get_template
class AssessmentBlock(object):
assessment_type = None
name = ''
start_datetime = None
due_datetime = None
must_grade = 1
must_be_graded_by = 0
navigation_text = ""
path = ""
title = ""
def create_ui_model(self):
return {
"assessment_type": self.assessment_type,
"name": self.name,
"start_datetime": self.start_datetime,
"due_datetime": self.due_datetime,
"must_grade": self.must_grade,
"must_be_graded_by": self.must_be_graded_by,
"navigation_text": self.navigation_text,
"title": self.title
}
def render(self, context_dict):
template = get_template(self.path)
context = Context(context_dict)
return template.render(context)
\ No newline at end of file
from django.template import Context
from django.template.loader import get_template
from webob import Response
class AssessmentMixin(object):
def render(self, path):
"""Render an Assessment Module's HTML
Given the name of an assessment module, find it in the list of
configured modules, and ask for its rendered HTML.
"""
context_dict = {
"xblock_trace": self._get_xblock_trace(),
"rubric_instructions": self.rubric_instructions,
"rubric_criteria": self.rubric_criteria,
}
template = get_template(path)
context = Context(context_dict)
return Response(template.render(context), content_type='application/html', charset='UTF-8')
def _get_assessment_module(self, mixin_name):
"""Get a configured assessment module by name.
"""
for assessment in self.rubric_assessments:
if assessment.name == mixin_name:
return assessment
\ No newline at end of file
"""An XBlock where students can read a question and compose their response""" """An XBlock where students can read a question and compose their response"""
import datetime import datetime
import json
from django.template.context import Context from django.template.context import Context
from django.template.loader import get_template from django.template.loader import get_template
import pkg_resources import pkg_resources
from webob import Response
from xblock.core import XBlock from xblock.core import XBlock
from xblock.fields import List, Scope, String from xblock.fields import List, Scope, String
from xblock.fragment import Fragment from xblock.fragment import Fragment
from openassessment.xblock.peer_assessment_block import PeerAssessmentBlock from openassessment.xblock.peer_assessment_mixin import PeerAssessmentMixin
from openassessment.xblock.submission_block import SubmissionBlock from openassessment.xblock.self_assessment_mixin import SelfAssessmentMixin
from openassessment.xblock.submission_mixin import SubmissionMixin
from openassessment.xblock.ui_models import PeerAssessmentUIModel
from scenario_parser import ScenarioParser from scenario_parser import ScenarioParser
...@@ -112,7 +112,7 @@ DEFAULT_RUBRIC_CRITERIA = [ ...@@ -112,7 +112,7 @@ DEFAULT_RUBRIC_CRITERIA = [
} }
] ]
DEFAULT_PEER_ASSESSMENT = PeerAssessmentBlock() DEFAULT_PEER_ASSESSMENT = PeerAssessmentUIModel()
DEFAULT_PEER_ASSESSMENT.name = "peer-assessment" DEFAULT_PEER_ASSESSMENT.name = "peer-assessment"
DEFAULT_PEER_ASSESSMENT.start_datetime = datetime.datetime.now().isoformat() DEFAULT_PEER_ASSESSMENT.start_datetime = datetime.datetime.now().isoformat()
DEFAULT_PEER_ASSESSMENT.must_grade = 5 DEFAULT_PEER_ASSESSMENT.must_grade = 5
...@@ -122,12 +122,14 @@ DEFAULT_ASSESSMENT_MODULES = [ ...@@ -122,12 +122,14 @@ DEFAULT_ASSESSMENT_MODULES = [
DEFAULT_PEER_ASSESSMENT, DEFAULT_PEER_ASSESSMENT,
] ]
def load(path): def load(path):
"""Handy helper for getting resources from our kit.""" """Handy helper for getting resources from our kit."""
data = pkg_resources.resource_string(__name__, path) data = pkg_resources.resource_string(__name__, path)
return data.decode("utf8") return data.decode("utf8")
class OpenAssessmentBlock(XBlock):
class OpenAssessmentBlock(XBlock, SubmissionMixin, PeerAssessmentMixin, SelfAssessmentMixin):
"""Displays a question and gives an area where students can compose a response.""" """Displays a question and gives an area where students can compose a response."""
start_datetime = String( start_datetime = String(
...@@ -230,49 +232,6 @@ class OpenAssessmentBlock(XBlock): ...@@ -230,49 +232,6 @@ class OpenAssessmentBlock(XBlock):
frag.initialize_js('OpenAssessmentBlock') frag.initialize_js('OpenAssessmentBlock')
return frag return frag
@XBlock.handler
def render_assessment(self, data, suffix=''):
"""Render an Assessment Module's HTML
Given the name of an assessment module, find it in the list of
configured modules, and ask for its rendered HTML.
"""
body = json.loads(data.body)
context_dict = {
"xblock_trace": self._get_xblock_trace(),
"rubric_instructions": self.rubric_instructions,
"rubric_criteria": self.rubric_criteria,
}
assessment = self._get_assessment_module(body['assessment'])
if assessment:
return Response(assessment.render(context_dict), content_type='application/html', charset="UTF-8")
@XBlock.json_handler
def assess(self, data, suffix=''):
# TODO Pass name through the handler.
assessment = self._get_assessment_module('peer-assessment')
if assessment:
return assessment.assess(
self._get_student_item_dict(),
self.rubric_criteria,
data
)
def _get_assessment_module(self, name):
"""Get a configured assessment module by name.
"""
for assessment in self.rubric_assessments:
if assessment.name == name:
return assessment
@XBlock.json_handler
def submit(self, data, suffix=''):
"""
Place the submission text into Openassessment system
"""
return SubmissionBlock().submit(self._get_student_item_dict(), data)
@staticmethod @staticmethod
def workbench_scenarios(): def workbench_scenarios():
"""A canned scenario for display in the workbench.""" """A canned scenario for display in the workbench."""
...@@ -301,7 +260,6 @@ class OpenAssessmentBlock(XBlock): ...@@ -301,7 +260,6 @@ class OpenAssessmentBlock(XBlock):
sparser = ScenarioParser(block, node, unknown_handler) sparser = ScenarioParser(block, node, unknown_handler)
block = sparser.parse() block = sparser.parse()
block.rubric_assessments.insert(0, SubmissionBlock())
return block return block
def _get_grade_state(self): def _get_grade_state(self):
......
from xblock.core import XBlock
from openassessment.peer import api as peer_api from openassessment.peer import api as peer_api
from openassessment.peer.api import PeerAssessmentWorkflowError from openassessment.peer.api import PeerAssessmentWorkflowError
from openassessment.xblock.assessment_block import AssessmentBlock from openassessment.xblock.assessment_mixin import AssessmentMixin
class PeerAssessmentBlock(AssessmentBlock): class PeerAssessmentMixin(AssessmentMixin):
assessment_type = "peer-assessment" @XBlock.json_handler
path = "static/html/oa_peer_assessment.html" def assess(self, data, suffix=''):
title = "Assess Peers' Responses"
navigation_text = "Your assessment(s) of peer responses"
@classmethod
def assess(cls, student_item_dict, rubric_criteria, data):
"""Place an assessment into Openassessment system """Place an assessment into Openassessment system
""" """
with self._get_assessment_module('peer-assessment') as assessment:
assessment_dict = { assessment_dict = {
"points_earned": map(int, data["points_earned"]), "points_earned": map(int, data["points_earned"]),
"points_possible": sum(c['total_value'] for c in rubric_criteria), "points_possible": sum(c['total_value'] for c in self.rubric_criteria),
"feedback": "Not yet implemented.", "feedback": "Not yet implemented.",
} }
assessment = peer_api.create_assessment( assessment = peer_api.create_assessment(
data["submission_uuid"], data["submission_uuid"],
student_item_dict["student_id"], self._get_student_item_dict()["student_id"],
int(cls.must_grade), int(assessment.must_grade),
int(cls.must_be_graded_by), int(assessment.must_be_graded_by),
assessment_dict assessment_dict
) )
...@@ -33,17 +30,21 @@ class PeerAssessmentBlock(AssessmentBlock): ...@@ -33,17 +30,21 @@ class PeerAssessmentBlock(AssessmentBlock):
return assessment, "Success" return assessment, "Success"
def get_peer_submission(self, student_item_dict): @XBlock.handler
def render_peer_assessment(self, data, suffix=''):
return super(PeerAssessmentMixin, self).render('static/html/oa_peer_assessment.html')
def get_peer_submission(self, student_item_dict, assessment):
peer_submission = False peer_submission = False
try: try:
peer_submission = peer_api.get_submission_to_assess( peer_submission = peer_api.get_submission_to_assess(
student_item_dict, self.must_be_graded_by student_item_dict, assessment.must_be_graded_by
) )
# context_dict["peer_submission"] = peer_submission # context_dict["peer_submission"] = peer_submission
peer_submission = peer_api.get_submission_to_assess( peer_submission = peer_api.get_submission_to_assess(
student_item_dict, student_item_dict,
self.must_be_graded_by assessment.must_be_graded_by
) )
except PeerAssessmentWorkflowError: except PeerAssessmentWorkflowError:
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""XBlock scenario parsing routines""" """XBlock scenario parsing routines"""
from openassessment.xblock.peer_assessment_block import PeerAssessmentBlock from openassessment.xblock.ui_models import PeerAssessmentUIModel, SelfAssessmentUIModel, SubmissionUIModel
from openassessment.xblock.self_assessment_block import SelfAssessmentBlock
class ScenarioParser(object): class ScenarioParser(object):
...@@ -76,16 +75,16 @@ class ScenarioParser(object): ...@@ -76,16 +75,16 @@ class ScenarioParser(object):
must_grade="5" must_grade="5"
must_be_graded_by="3" /> must_be_graded_by="3" />
</peer-assessment>""" </peer-assessment>"""
assessment_list = [] assessment_list = [SubmissionUIModel()]
for asmnt in assessments: for asmnt in assessments:
assessment = None assessment = None
assessment_type = asmnt.tag assessment_type = asmnt.tag
if 'peer-assessment' == assessment_type: if 'peer-assessment' == assessment_type:
assessment = PeerAssessmentBlock() assessment = PeerAssessmentUIModel()
assessment.must_grade = int(asmnt.attrib.get('must_grade', 1)) assessment.must_grade = int(asmnt.attrib.get('must_grade', 1))
assessment.must_be_graded_by = int(asmnt.attrib.get('must_be_graded_by', 0)) assessment.must_be_graded_by = int(asmnt.attrib.get('must_be_graded_by', 0))
elif 'self-assessment' == assessment_type: elif 'self-assessment' == assessment_type:
assessment = SelfAssessmentBlock() assessment = SelfAssessmentUIModel()
if assessment: if assessment:
assessment.name = asmnt.attrib.get('name', '') assessment.name = asmnt.attrib.get('name', '')
......
from openassessment.xblock.assessment_block import AssessmentBlock
class SelfAssessmentBlock(AssessmentBlock):
assessment_type = "self-assessment"
navigation_text = "Your assessment of your response"
path = "static/html/oa_self_assessment.html"
title = "Assess Your Response"
\ No newline at end of file
from xblock.core import XBlock
from openassessment.xblock.assessment_mixin import AssessmentMixin
class SelfAssessmentMixin(AssessmentMixin):
@XBlock.handler
def render_self_assessment(self, data, suffix=''):
return self.render('static/html/oa_self_assessment.html')
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
<!-- CASE: default/not started --> <!-- CASE: default/not started -->
<li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment"> <li id="openassessment__peer-assessment" class="openassessment__steps__step step--peer-assessment">
{#<header class="step__header">#} <div id="peer_submission_uuid" hidden="true">{{ peer_submission.uuid }}</div>
<h2 class="step__title"> <h2 class="step__title">
<span class="step__label">Evaluate Peers' Responses</span> <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">January 30, 2014</span> at <span class="time">15:00 UTC</span></span>
......
...@@ -2,90 +2,90 @@ ...@@ -2,90 +2,90 @@
function OpenAssessmentBlock(runtime, element) { function OpenAssessmentBlock(runtime, element) {
var handlerUrl = runtime.handlerUrl(element, 'submit'); var handlerUrl = runtime.handlerUrl(element, 'submit');
var renderUrl = runtime.handlerUrl(element, 'render_assessment'); var renderSubmissionUrl = runtime.handlerUrl(element, 'render_submission');
var renderPeerUrl = runtime.handlerUrl(element, 'render_peer_assessment');
var renderSelfUrl = runtime.handlerUrl(element, 'render_self_assessment');
/* Sample Debug Console: http://localhost:8000/submissions/Joe_Bloggs/TestCourse/u_3 */ /* Sample Debug Console: http://localhost:8000/submissions/Joe_Bloggs/TestCourse/u_3 */
/* /*
Peer Assessment Functions Submission Functions
*/ */
function prepare_assessment_post(element) { function render_submissions(data) {
var selector = $("input[type=radio]:checked", element); $('#submission', element).replaceWith(data);
var values = []; $('#step--response__submit', element).click(function(eventObject) {
for (i=0; i<selector.length; i++) {
values[i] = selector[i].value;
}
return {"submission_uuid":$("div#peer_submission_uuid")[0].innerText, "points_earned":values};
}
$('.openassessment_submit', element).click(function(eventObject) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: handlerUrl, url: handlerUrl,
/* data: JSON.stringify({"submission": $('.openassessment_submission', element).val()}), */ data: JSON.stringify({"submission": $('#submission__answer__value', element).val()}),
data: JSON.stringify(prepare_assessment_post(element)),
success: function(data) { success: function(data) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: renderUrl, url: renderPeerUrl,
data: JSON.stringify({"assessment": "peer-assessment"}),
success: function(data) { success: function(data) {
$('#peer-assessment', element).replaceWith(data); render_peer_assessment(data);
} }
}); });
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: renderUrl, url: renderSubmissionUrl,
data: JSON.stringify({"assessment": "self-assessment"}),
success: function(data) { success: function(data) {
$('#self-assessment', element).replaceWith(data); render_submissions(data);
} }
}); });
} }
}); });
}); });
}
$(function ($) {
/* Here's where you'd do things on page load. */
$.ajax({
type: "POST",
url: renderUrl,
data: JSON.stringify({"assessment": "submission"}),
success: function(data) {
$('#submission', element).replaceWith(data);
/* /*
Submission Functions Peer Assessment Functions
*/ */
$('#step--response__submit', element).click(function(eventObject) { function render_peer_assessment(data) {
$('#peer-assessment', element).replaceWith(data);
function prepare_assessment_post(element) {
var selector = $("input[type=radio]:checked", element);
var values = [];
for (var i=0; i<selector.length; i++) {
values[i] = selector[i].value;
}
return {"submission_uuid":$("div#peer_submission_uuid")[0].innerText, "points_earned":values};
}
$('#peer-assessment--001__assessment__submit', element).click(function(eventObject) {
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: handlerUrl, url: handlerUrl,
data: JSON.stringify({"submission": $('#submission__answer__value', element).val()}), /* data: JSON.stringify({"submission": $('.openassessment_submission', element).val()}), */
data: JSON.stringify(prepare_assessment_post(element)),
success: function(data) { success: function(data) {
alert("Success?")
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: renderUrl, url: renderSelfUrl,
data: JSON.stringify({"assessment": "submission"}),
success: function(data) { success: function(data) {
$('#submission', element).replaceWith(data); $('#self-assessment', element).replaceWith(data);
} }
}); });
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: renderUrl, url: renderPeerUrl,
data: JSON.stringify({"assessment": "peer-assessment"}),
success: function(data) { success: function(data) {
$('#peer-assessment', element).replaceWith(data); render_peer_assessment(data)
} }
}); });
}, }
fail: function(data) {alert("FAIL!!")}
}); });
}); });
} }
$(function ($) {
/* Here's where you'd do things on page load. */
$.ajax({
type: "POST",
url: renderSubmissionUrl,
success: function(data) {
render_submissions(data);
}
}); });
}); });
} }
......
from openassessment.xblock.assessment_block import AssessmentBlock from xblock.core import XBlock
from openassessment.xblock.assessment_mixin import AssessmentMixin
from submissions import api from submissions import api
class SubmissionBlock(AssessmentBlock): class SubmissionMixin(AssessmentMixin):
assessment_type = "submission"
name = "submission"
navigation_text = "Your response to this problem"
path = "static/html/oa_response.html"
title = "Your Response"
submit_errors = { submit_errors = {
# Reported to user sometimes, and useful in tests # Reported to user sometimes, and useful in tests
...@@ -18,10 +13,12 @@ class SubmissionBlock(AssessmentBlock): ...@@ -18,10 +13,12 @@ class SubmissionBlock(AssessmentBlock):
'EUNKNOWN': 'API returned unclassified exception', 'EUNKNOWN': 'API returned unclassified exception',
} }
def submit(self, student_item_dict, data): @XBlock.json_handler
def submit(self, data, suffix=''):
""" """
Place the submission text into Openassessment system Place the submission text into Openassessment system
""" """
student_item_dict = self._get_student_item_dict()
status = False status = False
status_text = None status_text = None
student_sub = data['submission'] student_sub = data['submission']
...@@ -40,3 +37,7 @@ class SubmissionBlock(AssessmentBlock): ...@@ -40,3 +37,7 @@ class SubmissionBlock(AssessmentBlock):
# relies on success being orthogonal to errors # relies on success being orthogonal to errors
status_text = status_text if status_text else self.submit_errors[status_tag] status_text = status_text if status_text else self.submit_errors[status_tag]
return status, status_tag, status_text return status, status_tag, status_text
@XBlock.handler
def render_submission(self, data, suffix=''):
return super(SubmissionMixin, self).render('static/html/oa_response.html')
...@@ -7,7 +7,7 @@ import webob ...@@ -7,7 +7,7 @@ import webob
from django.test import TestCase from django.test import TestCase
from mock import patch from mock import patch
from openassessment.xblock.submission_block import SubmissionBlock from openassessment.xblock.submission_mixin import SubmissionMixin
from submissions import api from submissions import api
from submissions.api import SubmissionRequestError, SubmissionInternalError from submissions.api import SubmissionRequestError, SubmissionInternalError
...@@ -98,7 +98,7 @@ class TestOpenAssessment(TestCase): ...@@ -98,7 +98,7 @@ class TestOpenAssessment(TestCase):
result = json.loads(resp.body) result = json.loads(resp.body)
self.assertFalse(result[0]) self.assertFalse(result[0])
self.assertEqual(result[1], "EUNKNOWN") self.assertEqual(result[1], "EUNKNOWN")
self.assertEqual(result[2], SubmissionBlock().submit_errors["EUNKNOWN"]) self.assertEqual(result[2], SubmissionMixin().submit_errors["EUNKNOWN"])
@patch.object(api, 'create_submission') @patch.object(api, 'create_submission')
def test_submission_API_failure(self, mock_submit): def test_submission_API_failure(self, mock_submit):
......
...@@ -76,19 +76,22 @@ class TestScenarioParser(TestCase): ...@@ -76,19 +76,22 @@ class TestScenarioParser(TestCase):
assessments_xml = etree.fromstring(assessments) assessments_xml = etree.fromstring(assessments)
parsed_list = self.test_parser.get_assessments(assessments_xml) parsed_list = self.test_parser.get_assessments(assessments_xml)
# Need to capture Submissions in Tests
self.assertEqual(parsed_list[0].assessment_type, 'submission')
# Self assessments take all the parameters, but mostly ignore them. # Self assessments take all the parameters, but mostly ignore them.
self.assertEqual(parsed_list[0].assessment_type, 'self-assessment') self.assertEqual(parsed_list[1].assessment_type, 'self-assessment')
self.assertEqual(parsed_list[0].name, '0382e03c808e4f2bb12dfdd2d45d5c4b') self.assertEqual(parsed_list[1].name, '0382e03c808e4f2bb12dfdd2d45d5c4b')
self.assertEqual(parsed_list[0].must_grade, 1) self.assertEqual(parsed_list[1].must_grade, 1)
self.assertEqual(parsed_list[0].must_be_graded_by, 0) self.assertEqual(parsed_list[1].must_be_graded_by, 0)
# Peer assessments are more interesting # Peer assessments are more interesting
self.assertEqual(parsed_list[1].assessment_type, 'peer-assessment') self.assertEqual(parsed_list[2].assessment_type, 'peer-assessment')
self.assertEqual(parsed_list[1].name, '') self.assertEqual(parsed_list[2].name, '')
self.assertEqual(parsed_list[1].must_grade, 5) self.assertEqual(parsed_list[2].must_grade, 5)
self.assertEqual(parsed_list[1].must_be_graded_by, 3) self.assertEqual(parsed_list[2].must_be_graded_by, 3)
# We can parse arbitrary workflow descriptions as a list of assessments. # We can parse arbitrary workflow descriptions as a list of assessments.
# Whether or not the workflow system can use them is another matter # Whether or not the workflow system can use them is another matter
self.assertEqual(parsed_list[2].assessment_type, 'self-assessment') self.assertEqual(parsed_list[3].assessment_type, 'self-assessment')
class SubmissionUIModel(object):
def __init__(self):
self.assessment_type = "submission"
self.name = "submission"
self.navigation_text = "Your response to this problem"
self.title = "Your Response"
def create_ui_model(self):
return {
"assessment_type": self.assessment_type,
"name": self.name,
"navigation_text": self.navigation_text,
"title": self.title
}
class AssessmentUIModel(object):
def __init__(self):
self.assessment_type = None
self.name = ''
self.start_datetime = None
self.due_datetime = None
self.must_grade = 1
self.must_be_graded_by = 0
self.navigation_text = ""
self.title = ""
def create_ui_model(self):
return {
"assessment_type": self.assessment_type,
"name": self.name,
"start_datetime": self.start_datetime,
"due_datetime": self.due_datetime,
"must_grade": self.must_grade,
"must_be_graded_by": self.must_be_graded_by,
"navigation_text": self.navigation_text,
"title": self.title
}
class PeerAssessmentUIModel(AssessmentUIModel):
def __init__(self):
super(PeerAssessmentUIModel, self).__init__()
self.assessment_type = "peer-assessment"
self.title = "Assess Peers' Responses"
self.navigation_text = "Your assessment(s) of peer responses"
class SelfAssessmentUIModel(AssessmentUIModel):
def __init__(self):
super(SelfAssessmentUIModel, self).__init__()
self.assessment_type = "self-assessment"
self.navigation_text = "Your assessment of your response"
self.title = "Assess Your Response"
\ No newline at end of file
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