Commit 120aa3cd by Diana Huang

Merge pull request #1421 from MITx/feature/vik/peer-grading-xmodule

Feature/vik/peer grading xmodule
parents 74c112e5 ce87e6f2
......@@ -64,7 +64,6 @@ def replace_static_urls(get_html, data_dir, course_namespace=None):
return static_replace.replace_static_urls(get_html(), data_dir, course_namespace)
return _get_html
def grade_histogram(module_id):
''' Print out a histogram of grades on a given problem.
Part of staff member debug info.
......
// Generated by CoffeeScript 1.3.3
// Generated by CoffeeScript 1.4.0
(function() {
var MinimaxProblemDisplay, root,
__hasProp = {}.hasOwnProperty,
......
// Generated by CoffeeScript 1.3.3
// Generated by CoffeeScript 1.4.0
(function() {
var TestProblemGenerator, root,
__hasProp = {}.hasOwnProperty,
......
// Generated by CoffeeScript 1.3.3
// Generated by CoffeeScript 1.4.0
(function() {
var TestProblemGrader, root,
__hasProp = {}.hasOwnProperty,
......
// Generated by CoffeeScript 1.3.3
// Generated by CoffeeScript 1.4.0
(function() {
var XProblemDisplay, XProblemGenerator, XProblemGrader, root;
......
......@@ -27,6 +27,7 @@ setup(
"html = xmodule.html_module:HtmlDescriptor",
"image = xmodule.backcompat_module:TranslateCustomTagDescriptor",
"error = xmodule.error_module:ErrorDescriptor",
"peergrading = xmodule.peer_grading_module:PeerGradingDescriptor",
"problem = xmodule.capa_module:CapaDescriptor",
"problemset = xmodule.seq_module:SequenceDescriptor",
"randomize = xmodule.randomize_module:RandomizeDescriptor",
......
......@@ -33,7 +33,9 @@ class CombinedOpenEndedRubric(object):
'view_only': self.view_only})
success = True
except:
raise RubricParsingError("[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml))
error_message = "[render_rubric] Could not parse the rubric with xml: {0}".format(rubric_xml)
log.error(error_message)
raise RubricParsingError(error_message)
return success, html
def check_if_rubric_is_parseable(self, rubric_string, location, max_score_allowed):
......
......@@ -5,16 +5,8 @@ import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from django.conf import settings
from django.http import HttpResponse, Http404
from courseware.access import has_access
from util.json_request import expect_json
from xmodule.course_module import CourseDescriptor
from xmodule.combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from lxml import etree
from mitxmako.shortcuts import render_to_string
from xmodule.x_module import ModuleSystem
log = logging.getLogger(__name__)
......@@ -31,7 +23,7 @@ class GradingService(object):
self.url = config['url']
self.login_url = self.url + '/login/'
self.session = requests.session()
self.system = ModuleSystem(None, None, None, render_to_string, None)
self.system = config['system']
def _login(self):
"""
......@@ -42,20 +34,20 @@ class GradingService(object):
Returns the decoded json dict of the response.
"""
response = self.session.post(self.login_url,
{'username': self.username,
'password': self.password,})
{'username': self.username,
'password': self.password,})
response.raise_for_status()
return response.json
def post(self, url, data, allow_redirects=False):
def post(self, url, data, allow_redirects=False):
"""
Make a post request to the grading controller
"""
try:
op = lambda: self.session.post(url, data=data,
allow_redirects=allow_redirects)
allow_redirects=allow_redirects)
r = self._try_with_login(op)
except (RequestException, ConnectionError, HTTPError) as err:
# reraise as promised GradingServiceError, but preserve stacktrace.
......@@ -69,8 +61,8 @@ class GradingService(object):
"""
log.debug(params)
op = lambda: self.session.get(url,
allow_redirects=allow_redirects,
params=params)
allow_redirects=allow_redirects,
params=params)
try:
r = self._try_with_login(op)
except (RequestException, ConnectionError, HTTPError) as err:
......@@ -78,7 +70,7 @@ class GradingService(object):
raise GradingServiceError, str(err), sys.exc_info()[2]
return r.text
def _try_with_login(self, operation):
"""
......@@ -96,8 +88,8 @@ class GradingService(object):
r = self._login()
if r and not r.get('success'):
log.warning("Couldn't log into staff_grading backend. Response: %s",
r)
# try again
r)
# try again
response = operation()
response.raise_for_status()
......@@ -113,23 +105,23 @@ class GradingService(object):
"""
try:
response_json = json.loads(response)
except:
response_json = response
try:
if 'rubric' in response_json:
rubric = response_json['rubric']
rubric_renderer = CombinedOpenEndedRubric(self.system, False)
success, rubric_html = rubric_renderer.render_rubric(rubric)
response_json['rubric'] = rubric_html
return response_json
# if we can't parse the rubric into HTML,
# if we can't parse the rubric into HTML,
except etree.XMLSyntaxError, RubricParsingError:
log.exception("Cannot parse rubric string. Raw string: {0}"
.format(rubric))
.format(rubric))
return {'success': False,
'error': 'Error displaying submission'}
'error': 'Error displaying submission'}
except ValueError:
log.exception("Error parsing response: {0}".format(response))
return {'success': False,
'error': "Error displaying submission"}
'error': "Error displaying submission"}
\ No newline at end of file
......@@ -2,17 +2,27 @@
# and message container when they are empty
# Can (and should be) expanded upon when our problem list
# becomes more sophisticated
class PeerGrading
constructor: () ->
class @PeerGrading
constructor: (element) ->
@peer_grading_container = $('.peer-grading')
@use_single_location = @peer_grading_container.data('use-single-location')
@peer_grading_outer_container = $('.peer-grading-container')
@ajax_url = @peer_grading_container.data('ajax-url')
@error_container = $('.error-container')
@error_container.toggle(not @error_container.is(':empty'))
@message_container = $('.message-container')
@message_container.toggle(not @message_container.is(':empty'))
@problem_button = $('.problem-button')
@problem_button.click @show_results
@problem_list = $('.problem-list')
@construct_progress_bar()
if @use_single_location
@activate_problem()
construct_progress_bar: () =>
problems = @problem_list.find('tr').next()
problems.each( (index, element) =>
......@@ -22,6 +32,18 @@ class PeerGrading
bar_max = parseInt(problem.data('required')) + bar_value
progress_bar.progressbar({value: bar_value, max: bar_max})
)
$(document).ready(() -> new PeerGrading())
show_results: (event) =>
location_to_fetch = $(event.target).data('location')
data = {'location' : location_to_fetch}
$.postWithPrefix "#{@ajax_url}problem", data, (response) =>
if response.success
@peer_grading_outer_container.after(response.html).remove()
backend = new PeerGradingProblemBackend(@ajax_url, false)
new PeerGradingProblem(backend)
else
@gentle_alert response.error
activate_problem: () =>
backend = new PeerGradingProblemBackend(@ajax_url, false)
new PeerGradingProblem(backend)
\ No newline at end of file
......@@ -13,6 +13,10 @@ from urlparse import urlparse
import requests
from boto.s3.connection import S3Connection
from boto.s3.key import Key
#TODO: Settings import is needed now in order to specify the URL and keys for amazon s3 (to upload images).
#Eventually, the goal is to replace the global django settings import with settings specifically
#for this module. There is no easy way to do this now, so piggybacking on the django settings
#makes sense.
from django.conf import settings
import pickle
import logging
......
import json
import logging
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
#TODO: Settings import is needed now in order to specify the URL where to find the peer grading service.
#Eventually, the goal is to replace the global django settings import with settings specifically
#for this xmodule. There is no easy way to do this now, so piggybacking on the django settings
#makes sense.
from django.conf import settings
from combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from lxml import etree
from grading_service_module import GradingService, GradingServiceError
log=logging.getLogger(__name__)
class GradingServiceError(Exception):
pass
class PeerGradingService(GradingService):
"""
Interface with the grading controller for peer grading
"""
def __init__(self, config, system):
config['system'] = system
super(PeerGradingService, self).__init__(config)
self.get_next_submission_url = self.url + '/get_next_submission/'
self.save_grade_url = self.url + '/save_grade/'
self.is_student_calibrated_url = self.url + '/is_student_calibrated/'
self.show_calibration_essay_url = self.url + '/show_calibration_essay/'
self.save_calibration_essay_url = self.url + '/save_calibration_essay/'
self.get_problem_list_url = self.url + '/get_problem_list/'
self.get_notifications_url = self.url + '/get_notifications/'
self.get_data_for_location_url = self.url + '/get_data_for_location/'
self.system = system
def get_data_for_location(self, problem_location, student_id):
response = self.get(self.get_data_for_location_url,
{'location': problem_location, 'student_id': student_id})
return self.try_to_decode(response)
def get_next_submission(self, problem_location, grader_id):
response = self.get(self.get_next_submission_url,
{'location': problem_location, 'grader_id': grader_id})
return self.try_to_decode(self._render_rubric(response))
def save_grade(self, location, grader_id, submission_id, score, feedback, submission_key, rubric_scores, submission_flagged):
data = {'grader_id' : grader_id,
'submission_id' : submission_id,
'score' : score,
'feedback' : feedback,
'submission_key': submission_key,
'location': location,
'rubric_scores': rubric_scores,
'rubric_scores_complete': True,
'submission_flagged' : submission_flagged}
return self.try_to_decode(self.post(self.save_grade_url, data))
def is_student_calibrated(self, problem_location, grader_id):
params = {'problem_id' : problem_location, 'student_id': grader_id}
return self.try_to_decode(self.get(self.is_student_calibrated_url, params))
def show_calibration_essay(self, problem_location, grader_id):
params = {'problem_id' : problem_location, 'student_id': grader_id}
response = self.get(self.show_calibration_essay_url, params)
return self.try_to_decode(self._render_rubric(response))
def save_calibration_essay(self, problem_location, grader_id, calibration_essay_id, submission_key,
score, feedback, rubric_scores):
data = {'location': problem_location,
'student_id': grader_id,
'calibration_essay_id': calibration_essay_id,
'submission_key': submission_key,
'score': score,
'feedback': feedback,
'rubric_scores[]': rubric_scores,
'rubric_scores_complete': True}
return self.try_to_decode(self.post(self.save_calibration_essay_url, data))
def get_problem_list(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id}
response = self.get(self.get_problem_list_url, params)
return self.try_to_decode(response)
def get_notifications(self, course_id, grader_id):
params = {'course_id': course_id, 'student_id': grader_id}
response = self.get(self.get_notifications_url, params)
return self.try_to_decode(response)
def try_to_decode(self, text):
try:
text = json.loads(text)
except:
pass
return text
"""
This is a mock peer grading service that can be used for unit tests
without making actual service calls to the grading controller
"""
class MockPeerGradingService(object):
def get_next_submission(self, problem_location, grader_id):
return json.dumps({'success': True,
'submission_id':1,
'submission_key': "",
'student_response': 'fake student response',
'prompt': 'fake submission prompt',
'rubric': 'fake rubric',
'max_score': 4})
def save_grade(self, location, grader_id, submission_id,
score, feedback, submission_key):
return json.dumps({'success': True})
def is_student_calibrated(self, problem_location, grader_id):
return json.dumps({'success': True, 'calibrated': True})
def show_calibration_essay(self, problem_location, grader_id):
return json.dumps({'success': True,
'submission_id':1,
'submission_key': '',
'student_response': 'fake student response',
'prompt': 'fake submission prompt',
'rubric': 'fake rubric',
'max_score': 4})
def save_calibration_essay(self, problem_location, grader_id,
calibration_essay_id, submission_key, score, feedback):
return {'success': True, 'actual_score': 2}
def get_problem_list(self, course_id, grader_id):
return json.dumps({'success': True,
'problem_list': [
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo1',
'problem_name': "Problem 1", 'num_graded': 3, 'num_pending': 5}),
json.dumps({'location': 'i4x://MITx/3.091x/problem/open_ended_demo2',
'problem_name': "Problem 2", 'num_graded': 1, 'num_pending': 5})
]})
_service = None
def peer_grading_service(system):
"""
Return a peer grading service instance--if settings.MOCK_PEER_GRADING is True,
returns a mock one, otherwise a real one.
Caches the result, so changing the setting after the first call to this
function will have no effect.
"""
global _service
if _service is not None:
return _service
if settings.MOCK_PEER_GRADING:
_service = MockPeerGradingService()
else:
_service = PeerGradingService(settings.PEER_GRADING_INTERFACE, system)
return _service
<peergrading display_name = "Peer Grading" use_for_single_location="False" is_graded="False"/>
......@@ -3,11 +3,12 @@ import logging
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from grading_service import GradingService
from grading_service import GradingServiceError
from xmodule.grading_service_module import GradingService, GradingServiceError
from django.conf import settings
from django.http import HttpResponse, Http404
from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
log = logging.getLogger(__name__)
......@@ -16,6 +17,7 @@ class ControllerQueryService(GradingService):
Interface to staff grading backend.
"""
def __init__(self, config):
config['system'] = ModuleSystem(None,None,None,render_to_string,None)
super(ControllerQueryService, self).__init__(config)
self.check_eta_url = self.url + '/get_submission_eta/'
self.is_unique_url = self.url + '/is_name_unique/'
......
from django.conf import settings
from staff_grading_service import StaffGradingService
from peer_grading_service import PeerGradingService
from open_ended_grading.controller_query_service import ControllerQueryService
import json
from student.models import unique_id_for_user
......@@ -10,6 +9,7 @@ import logging
from courseware.access import has_access
from util.cache import cache
import datetime
from xmodule import peer_grading_service
log=logging.getLogger(__name__)
......
......@@ -7,8 +7,7 @@ import logging
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from grading_service import GradingService
from grading_service import GradingServiceError
from xmodule.grading_service_module import GradingService, GradingServiceError
from django.conf import settings
from django.http import HttpResponse, Http404
......@@ -22,8 +21,6 @@ from mitxmako.shortcuts import render_to_string
log = logging.getLogger(__name__)
class MockStaffGradingService(object):
"""
A simple mockup of a staff grading service, testing.
......@@ -64,6 +61,7 @@ class StaffGradingService(GradingService):
Interface to staff grading backend.
"""
def __init__(self, config):
config['system'] = ModuleSystem(None,None,None,render_to_string,None)
super(StaffGradingService, self).__init__(config)
self.get_next_url = self.url + '/get_next_submission/'
self.save_grade_url = self.url + '/save_grade/'
......
......@@ -6,7 +6,7 @@ django-admin.py test --settings=lms.envs.test --pythonpath=. lms/djangoapps/open
from django.test import TestCase
from open_ended_grading import staff_grading_service
from open_ended_grading import peer_grading_service
from xmodule import peer_grading_service, peer_grading_module
from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group
......@@ -17,10 +17,13 @@ import xmodule.modulestore.django
from nose import SkipTest
from mock import patch, Mock
import json
from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
import logging
log = logging.getLogger(__name__)
from override_settings import override_settings
from django.http import QueryDict
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
......@@ -98,6 +101,7 @@ class TestStaffGradingService(ct.PageLoader):
'submission_id': '123',
'location': self.location,
'rubric_scores[]': ['1', '2']}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
self.assertTrue(d['success'], str(d))
......@@ -136,19 +140,21 @@ class TestPeerGradingService(ct.PageLoader):
self.course_id = "edX/toy/2012_Fall"
self.toy = modulestore().get_course(self.course_id)
location = "i4x://edX/toy/peergrading/init"
self.mock_service = peer_grading_service.peer_grading_service()
self.mock_service = peer_grading_service.MockPeerGradingService()
self.system = ModuleSystem(location, None, None, render_to_string, None)
self.descriptor = peer_grading_module.PeerGradingDescriptor(self.system)
self.peer_module = peer_grading_module.PeerGradingModule(self.system,location,"<peergrading/>",self.descriptor)
self.peer_module.peer_gs = self.mock_service
self.logout()
def test_get_next_submission_success(self):
self.login(self.student, self.password)
url = reverse('peer_grading_get_next_submission', kwargs={'course_id': self.course_id})
data = {'location': self.location}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.get_next_submission(data)
d = json.loads(r)
self.assertTrue(d['success'])
self.assertIsNotNone(d['submission_id'])
self.assertIsNotNone(d['prompt'])
......@@ -156,63 +162,48 @@ class TestPeerGradingService(ct.PageLoader):
self.assertIsNotNone(d['max_score'])
def test_get_next_submission_missing_location(self):
self.login(self.student, self.password)
url = reverse('peer_grading_get_next_submission', kwargs={'course_id': self.course_id})
data = {}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.get_next_submission(data)
d = r
self.assertFalse(d['success'])
self.assertEqual(d['error'], "Missing required keys: location")
def test_save_grade_success(self):
self.login(self.student, self.password)
url = reverse('peer_grading_save_grade', kwargs={'course_id': self.course_id})
data = {'location': self.location,
'submission_id': '1',
'submission_key': 'fake key',
'score': '2',
'feedback': 'This is feedback',
'rubric_scores[]': [1, 2],
'submission_flagged' : False}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
raise SkipTest()
data = 'rubric_scores[]=1|rubric_scores[]=2|location=' + self.location + '|submission_id=1|submission_key=fake key|score=2|feedback=feedback|submission_flagged=False'
qdict = QueryDict(data.replace("|","&"))
r = self.peer_module.save_grade(qdict)
d = r
self.assertTrue(d['success'])
def test_save_grade_missing_keys(self):
self.login(self.student, self.password)
url = reverse('peer_grading_save_grade', kwargs={'course_id': self.course_id})
data = {}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.save_grade(data)
d = r
self.assertFalse(d['success'])
self.assertTrue(d['error'].find('Missing required keys:') > -1)
def test_is_calibrated_success(self):
self.login(self.student, self.password)
url = reverse('peer_grading_is_student_calibrated', kwargs={'course_id': self.course_id})
data = {'location': self.location}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.is_student_calibrated(data)
d = json.loads(r)
self.assertTrue(d['success'])
self.assertTrue('calibrated' in d)
def test_is_calibrated_failure(self):
self.login(self.student, self.password)
url = reverse('peer_grading_is_student_calibrated', kwargs={'course_id': self.course_id})
data = {}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.is_student_calibrated(data)
d = r
self.assertFalse(d['success'])
self.assertFalse('calibrated' in d)
def test_show_calibration_essay_success(self):
self.login(self.student, self.password)
url = reverse('peer_grading_show_calibration_essay', kwargs={'course_id': self.course_id})
data = {'location': self.location}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.show_calibration_essay(data)
d = json.loads(r)
log.debug(d)
log.debug(type(d))
self.assertTrue(d['success'])
self.assertIsNotNone(d['submission_id'])
self.assertIsNotNone(d['prompt'])
......@@ -220,37 +211,27 @@ class TestPeerGradingService(ct.PageLoader):
self.assertIsNotNone(d['max_score'])
def test_show_calibration_essay_missing_key(self):
self.login(self.student, self.password)
url = reverse('peer_grading_show_calibration_essay', kwargs={'course_id': self.course_id})
data = {}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.show_calibration_essay(data)
d = r
self.assertFalse(d['success'])
self.assertEqual(d['error'], "Missing required keys: location")
def test_save_calibration_essay_success(self):
self.login(self.student, self.password)
url = reverse('peer_grading_save_calibration_essay', kwargs={'course_id': self.course_id})
data = {'location': self.location,
'submission_id': '1',
'submission_key': 'fake key',
'score': '2',
'feedback': 'This is feedback',
'rubric_scores[]': [1, 2]}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
raise SkipTest()
data = 'rubric_scores[]=1|rubric_scores[]=2|location=' + self.location + '|submission_id=1|submission_key=fake key|score=2|feedback=feedback|submission_flagged=False'
qdict = QueryDict(data.replace("|","&"))
r = self.peer_module.save_calibration_essay(qdict)
d = r
self.assertTrue(d['success'])
self.assertTrue('actual_score' in d)
def test_save_calibration_essay_missing_keys(self):
self.login(self.student, self.password)
url = reverse('peer_grading_save_calibration_essay', kwargs={'course_id': self.course_id})
data = {}
r = self.check_for_post_code(200, url, data)
d = json.loads(r.content)
r = self.peer_module.save_calibration_essay(data)
d = r
self.assertFalse(d['success'])
self.assertTrue(d['error'].find('Missing required keys:') > -1)
self.assertFalse('actual_score' in d)
......
......@@ -2,6 +2,7 @@
import logging
import urllib
import re
from django.conf import settings
from django.views.decorators.cache import cache_control
......@@ -11,10 +12,8 @@ from django.core.urlresolvers import reverse
from student.models import unique_id_for_user
from courseware.courses import get_course_with_access
from peer_grading_service import PeerGradingService
from peer_grading_service import MockPeerGradingService
from controller_query_service import ControllerQueryService
from grading_service import GradingServiceError
from xmodule.grading_service_module import GradingServiceError
import json
from .staff_grading import StaffGrading
from student.models import unique_id_for_user
......@@ -25,15 +24,11 @@ import open_ended_notifications
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import search
from django.http import HttpResponse, Http404
from django.http import HttpResponse, Http404, HttpResponseRedirect
log = logging.getLogger(__name__)
template_imports = {'urllib': urllib}
if settings.MOCK_PEER_GRADING:
peer_gs = MockPeerGradingService()
else:
peer_gs = PeerGradingService(settings.PEER_GRADING_INTERFACE)
controller_url = open_ended_util.get_controller_url()
controller_qs = ControllerQueryService(controller_url)
......@@ -81,66 +76,44 @@ def staff_grading(request, course_id):
# Checked above
'staff_access': True, })
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def peer_grading(request, course_id):
'''
Show a peer grading interface
'''
course = get_course_with_access(request.user, course_id, 'load')
course_id_parts = course.id.split("/")
course_id_norun = "/".join(course_id_parts[0:2])
pg_location = "i4x://" + course_id_norun + "/peergrading/init"
# call problem list service
success = False
error_text = ""
problem_list = []
base_course_url = reverse('courses')
try:
problem_list_json = peer_gs.get_problem_list(course_id, unique_id_for_user(request.user))
problem_list_dict = json.loads(problem_list_json)
success = problem_list_dict['success']
if 'error' in problem_list_dict:
error_text = problem_list_dict['error']
problem_list = problem_list_dict['problem_list']
except GradingServiceError:
error_text = "Error occured while contacting the grading service"
success = False
# catch error if if the json loads fails
except ValueError:
error_text = "Could not get problem list"
success = False
ajax_url = _reverse_with_slash('peer_grading', course_id)
problem_url_parts = search.path_to_location(modulestore(), course.id, pg_location)
problem_url = generate_problem_url(problem_url_parts, base_course_url)
return render_to_response('peer_grading/peer_grading.html', {
'course': course,
'course_id': course_id,
'ajax_url': ajax_url,
'success': success,
'problem_list': problem_list,
'error_text': error_text,
# Checked above
'staff_access': False, })
return HttpResponseRedirect(problem_url)
except:
error_message = "Error with initializing peer grading. Centralized module does not exist. Please contact course staff."
log.exception(error_message + "Current course is: {0}".format(course_id))
return HttpResponse(error_message)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def peer_grading_problem(request, course_id):
'''
Show individual problem interface
'''
course = get_course_with_access(request.user, course_id, 'load')
problem_location = request.GET.get("location")
ajax_url = _reverse_with_slash('peer_grading', course_id)
def generate_problem_url(problem_url_parts, base_course_url):
"""
From a list of problem url parts generated by search.path_to_location and a base course url, generates a url to a problem
@param problem_url_parts: Output of search.path_to_location
@param base_course_url: Base url of a given course
@return: A path to the problem
"""
problem_url = base_course_url + "/"
for z in xrange(0,len(problem_url_parts)):
part = problem_url_parts[z]
if part is not None:
if z==1:
problem_url += "courseware/"
problem_url += part + "/"
return problem_url
return render_to_response('peer_grading/peer_grading_problem.html', {
'view_html': '',
'course': course,
'problem_location': problem_location,
'course_id': course_id,
'ajax_url': ajax_url,
# Checked above
'staff_access': False, })
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def student_problem_list(request, course_id):
......@@ -156,28 +129,22 @@ def student_problem_list(request, course_id):
problem_list = []
base_course_url = reverse('courses')
try:
problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user))
problem_list_dict = json.loads(problem_list_json)
success = problem_list_dict['success']
if 'error' in problem_list_dict:
error_text = problem_list_dict['error']
problem_list = []
else:
problem_list = problem_list_dict['problem_list']
for i in xrange(0,len(problem_list)):
problem_url_parts = search.path_to_location(modulestore(), course.id, problem_list[i]['location'])
problem_url = base_course_url + "/"
for z in xrange(0,len(problem_url_parts)):
part = problem_url_parts[z]
if part is not None:
if z==1:
problem_url += "courseware/"
problem_url += part + "/"
#try:
problem_list_json = controller_qs.get_grading_status_list(course_id, unique_id_for_user(request.user))
problem_list_dict = json.loads(problem_list_json)
success = problem_list_dict['success']
if 'error' in problem_list_dict:
error_text = problem_list_dict['error']
problem_list = []
else:
problem_list = problem_list_dict['problem_list']
problem_list[i].update({'actual_url' : problem_url})
for i in xrange(0,len(problem_list)):
problem_url_parts = search.path_to_location(modulestore(), course.id, problem_list[i]['location'])
problem_url = generate_problem_url(problem_url_parts, base_course_url)
problem_list[i].update({'actual_url' : problem_url})
"""
except GradingServiceError:
error_text = "Error occured while contacting the grading service"
success = False
......@@ -185,6 +152,7 @@ def student_problem_list(request, course_id):
except ValueError:
error_text = "Could not get problem list"
success = False
"""
ajax_url = _reverse_with_slash('open_ended_problems', course_id)
......@@ -231,16 +199,17 @@ def flagged_problem_list(request, course_id):
success = False
ajax_url = _reverse_with_slash('open_ended_flagged_problems', course_id)
return render_to_response('open_ended_problems/open_ended_flagged_problems.html', {
'course': course,
'course_id': course_id,
'ajax_url': ajax_url,
'success': success,
'problem_list': problem_list,
'error_text': error_text,
# Checked above
'staff_access': True, })
context = {
'course': course,
'course_id': course_id,
'ajax_url': ajax_url,
'success': success,
'problem_list': problem_list,
'error_text': error_text,
# Checked above
'staff_access': True,
}
return render_to_response('open_ended_problems/open_ended_flagged_problems.html', context)
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
def combined_notifications(request, course_id):
......@@ -322,7 +291,7 @@ def take_action_on_flags(request, course_id):
response = controller_qs.take_action_on_flags(course_id, student_id, submission_id, action_type)
return HttpResponse(response, mimetype="application/json")
except GradingServiceError:
log.exception("Error saving calibration grade, location: {0}, submission_id: {1}, submission_key: {2}, grader_id: {3}".format(location, submission_id, submission_key, grader_id))
log.exception("Error saving calibration grade, submission_id: {0}, submission_key: {1}, grader_id: {2}".format(submission_id, submission_key, grader_id))
return _err_response('Could not connect to grading service')
......@@ -419,7 +419,6 @@ main_vendor_js = [
discussion_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/discussion/**/*.coffee'))
staff_grading_js = sorted(rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/staff_grading/**/*.coffee'))
peer_grading_js = sorted(rooted_glob(PROJECT_ROOT / 'static','coffee/src/peer_grading/**/*.coffee'))
open_ended_js = sorted(rooted_glob(PROJECT_ROOT / 'static','coffee/src/open_ended/**/*.coffee'))
PIPELINE_CSS = {
......@@ -451,7 +450,7 @@ PIPELINE_JS = {
'source_filenames': sorted(
set(rooted_glob(COMMON_ROOT / 'static', 'coffee/src/**/*.coffee') +
rooted_glob(PROJECT_ROOT / 'static', 'coffee/src/**/*.coffee')) -
set(courseware_js + discussion_js + staff_grading_js + peer_grading_js + open_ended_js)
set(courseware_js + discussion_js + staff_grading_js + open_ended_js)
) + [
'js/form.ext.js',
'js/my_courses_dropdown.js',
......@@ -481,10 +480,6 @@ PIPELINE_JS = {
'source_filenames': staff_grading_js,
'output_filename': 'js/staff_grading.js'
},
'peer_grading' : {
'source_filenames': peer_grading_js,
'output_filename': 'js/peer_grading.js'
},
'open_ended' : {
'source_filenames': open_ended_js,
'output_filename': 'js/open_ended.js'
......
......@@ -120,7 +120,7 @@ div.peer-grading{
margin-right:20px;
> div
{
padding: 10px;
padding: 2px;
margin: 0px;
background: #eee;
height: 10em;
......
<%inherit file="/main.html" />
<%block name="bodyclass">${course.css_class}</%block>
<%namespace name='static' file='/static_content.html'/>
<%block name="headextra">
<%static:css group='course'/>
</%block>
<%block name="title"><title>${course.number} Peer Grading</title></%block>
<%include file="/courseware/course_navigation.html" args="active_page='peer_grading'" />
<%block name="js_extra">
<%static:js group='peer_grading'/>
</%block>
<section class="container">
<div class="peer-grading" data-ajax_url="${ajax_url}">
<section class="container peer-grading-container">
<div class="peer-grading" data-ajax-url="${ajax_url}" data-use-single-location="${use_single_location}">
<div class="error-container">${error_text}</div>
<h1>Peer Grading</h1>
<h2>Instructions</h2>
......@@ -38,7 +22,7 @@
%for problem in problem_list:
<tr data-graded="${problem['num_graded']}" data-required="${problem['num_required']}">
<td class="problem-name">
<a href="${ajax_url}problem?location=${problem['location']}">${problem['problem_name']}</a>
<a href="#problem" data-location="${problem['location']}" class="problem-button">${problem['problem_name']}</a>
</td>
<td>
${problem['num_graded']}
......
<%inherit file="/main.html" />
<%block name="bodyclass">${course.css_class}</%block>
<%namespace name='static' file='/static_content.html'/>
<%block name="headextra">
<%static:css group='course'/>
</%block>
<%block name="title"><title>${course.number} Peer Grading.</title></%block>
<%include file="/courseware/course_navigation.html" args="active_page='peer_grading'" />
<%block name="js_extra">
<%static:js group='peer_grading'/>
</%block>
<section class="container">
<div class="peer-grading" data-ajax_url="${ajax_url}" data-location="${problem_location}">
<section class="container peer-grading-container">
<div class="peer-grading" data-ajax-url="${ajax_url}" data-location="${problem_location}" data-use-single-location="${use_single_location}">
<div class="error-container"></div>
<section class="content-panel">
......
......@@ -268,23 +268,6 @@ if settings.COURSEWARE_ENABLED:
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/staff_grading/get_problem_list$',
'open_ended_grading.staff_grading_service.get_problem_list', name='staff_grading_get_problem_list'),
# Peer Grading
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading$',
'open_ended_grading.views.peer_grading', name='peer_grading'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/problem$',
'open_ended_grading.views.peer_grading_problem', name='peer_grading_problem'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/get_next_submission$',
'open_ended_grading.peer_grading_service.get_next_submission', name='peer_grading_get_next_submission'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/show_calibration_essay$',
'open_ended_grading.peer_grading_service.show_calibration_essay', name='peer_grading_show_calibration_essay'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/is_student_calibrated$',
'open_ended_grading.peer_grading_service.is_student_calibrated', name='peer_grading_is_student_calibrated'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/save_grade$',
'open_ended_grading.peer_grading_service.save_grade', name='peer_grading_save_grade'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading/save_calibration_essay$',
'open_ended_grading.peer_grading_service.save_calibration_essay', name='peer_grading_save_calibration_essay'),
# Open Ended problem list
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_problems$',
'open_ended_grading.views.student_problem_list', name='open_ended_problems'),
......@@ -317,6 +300,9 @@ if settings.COURSEWARE_ENABLED:
# Open Ended Notifications
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/open_ended_notifications$',
'open_ended_grading.views.combined_notifications', name='open_ended_notifications'),
url(r'^courses/(?P<course_id>[^/]+/[^/]+/[^/]+)/peer_grading$',
'open_ended_grading.views.peer_grading', name='peer_grading'),
)
# discussion forums live within courseware, so courseware must be enabled first
......
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