Commit a789d6c3 by Diana Huang

Merge branch 'master' into diana/fix-self-assessment-test

parents 6e453c2b 789db301
import copy
from fs.errors import ResourceNotFoundError
import itertools
import json
import logging
from lxml import etree
from lxml.html import rewrite_links
from path import path
import os
import sys
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .x_module import XModule
from .xml_module import XmlDescriptor
from xmodule.modulestore import Location
from combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module, CombinedOpenEndedV1Descriptor
log = logging.getLogger("mitx.courseware")
......
import copy
from fs.errors import ResourceNotFoundError
import itertools
import json
import logging
from lxml import etree
from lxml.html import rewrite_links
from path import path
import os
import sys
import re
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .x_module import XModule
from .xml_module import XmlDescriptor
from xmodule.modulestore import Location
from xmodule.timeinfo import TimeInfo
from xmodule.capa_module import only_one, ComplexEncoder
from xmodule.editing_module import EditingDescriptor
from xmodule.html_checker import check_html
from xmodule.progress import Progress
from xmodule.stringify import stringify_children
from xmodule.x_module import XModule
from xmodule.xml_module import XmlDescriptor
import self_assessment_module
import open_ended_module
from combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError, GRADER_TYPE_IMAGE_DICT, HUMAN_GRADER_TYPE, LEGEND_LIST
from .stringify import stringify_children
import dateutil
import dateutil.parser
import datetime
from timeparse import parse_timedelta
from combined_open_ended_rubric import CombinedOpenEndedRubric, GRADER_TYPE_IMAGE_DICT, HUMAN_GRADER_TYPE, LEGEND_LIST
log = logging.getLogger("mitx.courseware")
......@@ -59,6 +44,10 @@ HUMAN_TASK_TYPE = {
'openended' : "edX Assessment",
}
#Default value that controls whether or not to skip basic spelling checks in the controller
#Metadata overrides this
SKIP_BASIC_CHECKS = False
class CombinedOpenEndedV1Module():
"""
This is a module that encapsulates all open ended grading (self assessment, peer assessment, etc).
......@@ -73,7 +62,7 @@ class CombinedOpenEndedV1Module():
'save_assessment' -- Saves the student assessment (or external grader assessment)
'save_post_assessment' -- saves a post assessment (hint, feedback on feedback, etc)
ajax actions implemented by combined open ended module are:
'reset' -- resets the whole combined open ended module and returns to the first child module
'reset' -- resets the whole combined open ended module and returns to the first child moduleresource_string
'next_problem' -- moves to the next child module
'get_results' -- gets results from a given child module
......@@ -90,14 +79,6 @@ class CombinedOpenEndedV1Module():
INTERMEDIATE_DONE = 'intermediate_done'
DONE = 'done'
js = {'coffee': [resource_string(__name__, 'js/src/combinedopenended/display.coffee'),
resource_string(__name__, 'js/src/collapsible.coffee'),
resource_string(__name__, 'js/src/javascript_loader.coffee'),
]}
js_module_name = "CombinedOpenEnded"
css = {'scss': [resource_string(__name__, 'css/combinedopenended/display.scss')]}
def __init__(self, system, location, definition, descriptor,
instance_state=None, shared_state=None, metadata = None, static_data = None, **kwargs):
......@@ -165,28 +146,16 @@ class CombinedOpenEndedV1Module():
self.max_attempts = int(self.metadata.get('attempts', MAX_ATTEMPTS))
self.is_scored = self.metadata.get('is_graded', IS_SCORED) in TRUE_DICT
self.accept_file_upload = self.metadata.get('accept_file_upload', ACCEPT_FILE_UPLOAD) in TRUE_DICT
self.skip_basic_checks = self.metadata.get('skip_spelling_checks', SKIP_BASIC_CHECKS)
display_due_date_string = self.metadata.get('due', None)
if display_due_date_string is not None:
try:
self.display_due_date = dateutil.parser.parse(display_due_date_string)
except ValueError:
log.error("Could not parse due date {0} for location {1}".format(display_due_date_string, location))
raise
else:
self.display_due_date = None
grace_period_string = self.metadata.get('graceperiod', None)
if grace_period_string is not None and self.display_due_date:
try:
self.grace_period = parse_timedelta(grace_period_string)
self.close_date = self.display_due_date + self.grace_period
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
except:
log.error("Error parsing the grace period {0} for location {1}".format(grace_period_string, location))
log.error("Error parsing due date information in location {0}".format(location))
raise
else:
self.grace_period = None
self.close_date = self.display_due_date
self.display_due_date = self.timeinfo.display_due_date
# Used for progress / grading. Currently get credit just for
# completion (doesn't matter if you self-assessed correct/incorrect).
......@@ -204,7 +173,9 @@ class CombinedOpenEndedV1Module():
'rubric': definition['rubric'],
'display_name': self.display_name,
'accept_file_upload': self.accept_file_upload,
'close_date' : self.close_date,
'close_date' : self.timeinfo.close_date,
's3_interface' : self.system.s3_interface,
'skip_basic_checks' : self.skip_basic_checks,
}
self.task_xml = definition['task_xml']
......@@ -798,9 +769,6 @@ class CombinedOpenEndedV1Descriptor(XmlDescriptor, EditingDescriptor):
has_score = True
template_dir_name = "combinedopenended"
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
js_module_name = "HTMLEditingDescriptor"
@classmethod
def definition_from_xml(cls, xml_object, system):
"""
......
......@@ -5,7 +5,7 @@ import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from xmodule.combined_open_ended_rubric import CombinedOpenEndedRubric, RubricParsingError
from combined_open_ended_rubric import CombinedOpenEndedRubric
from lxml import etree
log = logging.getLogger(__name__)
......@@ -22,8 +22,6 @@ class GradingService(object):
def __init__(self, config):
self.username = config['username']
self.password = config['password']
self.url = config['url']
self.login_url = self.url + '/login/'
self.session = requests.session()
self.system = config['system']
......
......@@ -13,11 +13,6 @@ 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 re
......@@ -221,7 +216,7 @@ def run_image_tests(image):
return success
def upload_to_s3(file_to_upload, keyname):
def upload_to_s3(file_to_upload, keyname, s3_interface):
'''
Upload file to S3 using provided keyname.
......@@ -237,8 +232,8 @@ def upload_to_s3(file_to_upload, keyname):
#im.save(out_im, 'PNG')
try:
conn = S3Connection(settings.AWS_ACCESS_KEY_ID, settings.AWS_SECRET_ACCESS_KEY)
bucketname = str(settings.AWS_STORAGE_BUCKET_NAME)
conn = S3Connection(s3_interface['access_key'], s3_interface['secret_access_key'])
bucketname = str(s3_interface['storage_bucket_name'])
bucket = conn.create_bucket(bucketname.lower())
k = Key(bucket)
......
......@@ -5,28 +5,16 @@ hints, answers, and assessment judgment (currently only correct/incorrect).
Parses xml definition file--see below for exact format.
"""
import copy
from fs.errors import ResourceNotFoundError
import itertools
import json
import logging
from lxml import etree
from lxml.html import rewrite_links
from path import path
import os
import sys
import hashlib
import capa.xqueue_interface as xqueue_interface
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .xml_module import XmlDescriptor
from xmodule.modulestore import Location
from xmodule.capa_module import ComplexEncoder
from xmodule.editing_module import EditingDescriptor
from xmodule.progress import Progress
from xmodule.stringify import stringify_children
from xmodule.xml_module import XmlDescriptor
from capa.util import *
import openendedchild
......@@ -122,7 +110,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
'rubric': rubric_string,
'initial_display': self.initial_display,
'answer': self.answer,
'problem_id': self.display_name
'problem_id': self.display_name,
'skip_basic_checks': self.skip_basic_checks,
})
updated_grader_payload = json.dumps(parsed_grader_payload)
......@@ -689,9 +678,6 @@ class OpenEndedDescriptor(XmlDescriptor, EditingDescriptor):
has_score = True
template_dir_name = "openended"
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
js_module_name = "HTMLEditingDescriptor"
@classmethod
def definition_from_xml(cls, xml_object, system):
"""
......
......@@ -13,17 +13,15 @@ import hashlib
import capa.xqueue_interface as xqueue_interface
import re
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .xml_module import XmlDescriptor
from xmodule.capa_module import only_one, ComplexEncoder
import open_ended_image_submission
from xmodule.editing_module import EditingDescriptor
from xmodule.html_checker import check_html
from xmodule.progress import Progress
from xmodule.stringify import stringify_children
from xmodule.xml_module import XmlDescriptor
from xmodule.modulestore import Location
from capa.util import *
import open_ended_image_submission
from datetime import datetime
......@@ -100,6 +98,8 @@ class OpenEndedChild(object):
self.display_name = static_data['display_name']
self.accept_file_upload = static_data['accept_file_upload']
self.close_date = static_data['close_date']
self.s3_interface = static_data['s3_interface']
self.skip_basic_checks = static_data['skip_basic_checks']
# Used for progress / grading. Currently get credit just for
# completion (doesn't matter if you self-assessed correct/incorrect).
......@@ -319,7 +319,7 @@ class OpenEndedChild(object):
try:
image_data.seek(0)
success, s3_public_url = open_ended_image_submission.upload_to_s3(image_data, image_key)
success, s3_public_url = open_ended_image_submission.upload_to_s3(image_data, image_key, self.s3_interface)
except:
log.exception("Could not upload image to S3.")
......
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
from grading_service_module import GradingService
log = logging.getLogger(__name__)
......@@ -28,6 +17,8 @@ class PeerGradingService(GradingService):
def __init__(self, config, system):
config['system'] = system
super(PeerGradingService, self).__init__(config)
self.url = config['url'] + config['peer_grading']
self.login_url = self.url + '/login/'
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/'
......@@ -143,25 +134,3 @@ class MockPeerGradingService(object):
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
import copy
from fs.errors import ResourceNotFoundError
import itertools
import json
import logging
from lxml import etree
from lxml.html import rewrite_links
from path import path
import os
import sys
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .x_module import XModule
from .xml_module import XmlDescriptor
from xmodule.modulestore import Location
from xmodule.capa_module import ComplexEncoder
from xmodule.editing_module import EditingDescriptor
from xmodule.progress import Progress
from xmodule.stringify import stringify_children
from xmodule.xml_module import XmlDescriptor
import openendedchild
from combined_open_ended_rubric import CombinedOpenEndedRubric
......@@ -285,10 +273,6 @@ class SelfAssessmentDescriptor(XmlDescriptor, EditingDescriptor):
has_score = True
template_dir_name = "selfassessment"
js = {'coffee': [resource_string(__name__, 'js/src/html/edit.coffee')]}
js_module_name = "HTMLEditingDescriptor"
css = {'scss': [resource_string(__name__, 'css/editor/edit.scss'), resource_string(__name__, 'css/html/edit.scss')]}
@classmethod
def definition_from_xml(cls, xml_object, system):
"""
......
"""
This module provides an interface on the grading-service backend
for peer grading
Use peer_grading_service() to get the version specified
in settings.PEER_GRADING_INTERFACE
"""
import json
import logging
import requests
import sys
from django.conf import settings
from combined_open_ended_rubric import CombinedOpenEndedRubric
from lxml import etree
import copy
import itertools
import json
import logging
from lxml.html import rewrite_links
import os
from datetime import datetime
from pkg_resources import resource_string
from .capa_module import only_one, ComplexEncoder
from .capa_module import ComplexEncoder
from .editing_module import EditingDescriptor
from .html_checker import check_html
from progress import Progress
from .stringify import stringify_children
from .x_module import XModule
from .xml_module import XmlDescriptor
from xmodule.modulestore import Location
from xmodule.modulestore.django import modulestore
from timeinfo import TimeInfo
from peer_grading_service import peer_grading_service, GradingServiceError
from xmodule.open_ended_grading_classes.peer_grading_service import PeerGradingService, GradingServiceError
log = logging.getLogger(__name__)
......@@ -70,7 +51,8 @@ class PeerGradingModule(XModule):
#We need to set the location here so the child modules can use it
system.set('location', location)
self.system = system
self.peer_gs = peer_grading_service(self.system)
self.peer_gs = PeerGradingService(self.system.open_ended_grading_interface, self.system)
self.use_for_single_location = self.metadata.get('use_for_single_location', USE_FOR_SINGLE_LOCATION)
if isinstance(self.use_for_single_location, basestring):
......@@ -80,10 +62,28 @@ class PeerGradingModule(XModule):
if isinstance(self.is_graded, basestring):
self.is_graded = (self.is_graded in TRUE_DICT)
display_due_date_string = self.metadata.get('due', None)
grace_period_string = self.metadata.get('graceperiod', None)
try:
self.timeinfo = TimeInfo(display_due_date_string, grace_period_string)
except:
log.error("Error parsing due date information in location {0}".format(location))
raise
self.display_due_date = self.timeinfo.display_due_date
self.link_to_location = self.metadata.get('link_to_location', USE_FOR_SINGLE_LOCATION)
if self.use_for_single_location == True:
#This will raise an exception if the location is invalid
link_to_location_object = Location(self.link_to_location)
try:
self.linked_problem = modulestore().get_instance(self.system.course_id, self.link_to_location)
except:
log.error("Linked location {0} for peer grading module {1} does not exist".format(
self.link_to_location, self.location))
raise
due_date = self.linked_problem.metadata.get('peer_grading_due', None)
if due_date:
self.metadata['due'] = due_date
self.ajax_url = self.system.ajax_url
if not self.ajax_url.endswith("/"):
......@@ -95,6 +95,15 @@ class PeerGradingModule(XModule):
#This could result in an exception, but not wrapping in a try catch block so it moves up the stack
self.max_grade = int(self.max_grade)
def closed(self):
return self._closed(self.timeinfo)
def _closed(self, timeinfo):
if timeinfo.close_date is not None and datetime.utcnow() > timeinfo.close_date:
return True
return False
def _err_response(self, msg):
"""
Return a HttpResponse with a json dump with success=False, and the given error message.
......@@ -114,6 +123,8 @@ class PeerGradingModule(XModule):
Needs to be implemented by inheritors. Renders the HTML that students see.
@return:
"""
if self.closed():
return self.peer_grading_closed()
if not self.use_for_single_location:
return self.peer_grading()
else:
......@@ -142,7 +153,7 @@ class PeerGradingModule(XModule):
def query_data_for_location(self):
student_id = self.system.anonymous_student_id
location = self.system.location
location = self.link_to_location
success = False
response = {}
......@@ -171,7 +182,7 @@ class PeerGradingModule(XModule):
success, response = self.query_data_for_location()
if not success:
log.exception("No instance data found and could not get data from controller for loc {0} student {1}".format(
self.system.location, self.system.anonymous_student_id
self.system.location.url(), self.system.anonymous_student_id
))
return None
count_graded = response['count_graded']
......@@ -400,6 +411,16 @@ class PeerGradingModule(XModule):
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))
return self._err_response('Could not connect to grading service')
def peer_grading_closed(self):
'''
Show the Peer grading closed template
'''
html = self.system.render_template('peer_grading/peer_grading_closed.html', {
'use_for_single_location': self.use_for_single_location
})
return html
def peer_grading(self, get=None):
'''
Show a peer grading interface
......@@ -426,6 +447,40 @@ class PeerGradingModule(XModule):
error_text = "Could not get problem list"
success = False
def _find_corresponding_module_for_location(location):
'''
find the peer grading module that links to the given location
'''
try:
return modulestore().get_instance(self.system.course_id, location)
except:
# the linked problem doesn't exist
log.error("Problem {0} does not exist in this course".format(location))
raise
for problem in problem_list:
problem_location = problem['location']
descriptor = _find_corresponding_module_for_location(problem_location)
if descriptor:
problem['due'] = descriptor.metadata.get('peer_grading_due', None)
grace_period_string = descriptor.metadata.get('graceperiod', None)
try:
problem_timeinfo = TimeInfo(problem['due'], grace_period_string)
except:
log.error("Malformed due date or grace period string for location {0}".format(problem_location))
raise
if self._closed(problem_timeinfo):
problem['closed'] = True
else:
problem['closed'] = False
else:
# if we can't find the due date, assume that it doesn't have one
problem['due'] = None
problem['closed'] = False
ajax_url = self.ajax_url
html = self.system.render_template('peer_grading/peer_grading.html', {
'course_id': self.system.course_id,
......
......@@ -2,9 +2,9 @@ import json
from mock import Mock, MagicMock, ANY
import unittest
from xmodule.openendedchild import OpenEndedChild
from xmodule.open_ended_module import OpenEndedModule
from xmodule.combined_open_ended_modulev1 import CombinedOpenEndedV1Module
from xmodule.open_ended_grading_classes.openendedchild import OpenEndedChild
from xmodule.open_ended_grading_classes.open_ended_module import OpenEndedModule
from xmodule.open_ended_grading_classes.combined_open_ended_modulev1 import CombinedOpenEndedV1Module
from xmodule.modulestore import Location
from lxml import etree
......@@ -12,6 +12,8 @@ import capa.xqueue_interface as xqueue_interface
from datetime import datetime
from . import test_system
import test_util_open_ended
"""
Tests for the various pieces of the CombinedOpenEndedGrading system
......@@ -43,7 +45,10 @@ class OpenEndedChildTest(unittest.TestCase):
'max_score': max_score,
'display_name': 'Name',
'accept_file_upload': False,
'close_date': None
'close_date': None,
's3_interface' : "",
'open_ended_grading_interface' : {},
'skip_basic_checks' : False,
}
definition = Mock()
descriptor = Mock()
......@@ -161,6 +166,9 @@ class OpenEndedModuleTest(unittest.TestCase):
'accept_file_upload': False,
'rewrite_content_links' : "",
'close_date': None,
's3_interface' : test_util_open_ended.S3_INTERFACE,
'open_ended_grading_interface' : test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE,
'skip_basic_checks' : False,
}
oeparam = etree.XML('''
......@@ -293,6 +301,9 @@ class CombinedOpenEndedModuleTest(unittest.TestCase):
'accept_file_upload' : False,
'rewrite_content_links' : "",
'close_date' : "",
's3_interface' : test_util_open_ended.S3_INTERFACE,
'open_ended_grading_interface' : test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE,
'skip_basic_checks' : False,
}
oeparam = etree.XML('''
......
......@@ -2,12 +2,13 @@ import json
from mock import Mock, MagicMock
import unittest
from xmodule.self_assessment_module import SelfAssessmentModule
from xmodule.open_ended_grading_classes.self_assessment_module import SelfAssessmentModule
from xmodule.modulestore import Location
from lxml import etree
from . import test_system
import test_util_open_ended
class SelfAssessmentTest(unittest.TestCase):
......@@ -46,7 +47,10 @@ class SelfAssessmentTest(unittest.TestCase):
'max_score': 1,
'display_name': "Name",
'accept_file_upload': False,
'close_date': None
'close_date': None,
's3_interface' : test_util_open_ended.S3_INTERFACE,
'open_ended_grading_interface' : test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE,
'skip_basic_checks' : False,
}
self.module = SelfAssessmentModule(test_system, self.location,
......
OPEN_ENDED_GRADING_INTERFACE = {
'url' : 'http://127.0.0.1:3033/',
'username' : 'incorrect',
'password' : 'incorrect',
'staff_grading' : 'staff_grading',
'peer_grading' : 'peer_grading',
'grading_controller' : 'grading_controller'
}
S3_INTERFACE = {
'aws_access_key' : "",
'aws_secret_key' : "",
"aws_bucket_name" : "",
}
\ No newline at end of file
import dateutil
import dateutil.parser
import datetime
from timeparse import parse_timedelta
import logging
log = logging.getLogger(__name__)
class TimeInfo(object):
"""
This is a simple object that calculates and stores datetime information for an XModule
based on the due date string and the grace period string
So far it parses out three different pieces of time information:
self.display_due_date - the 'official' due date that gets displayed to students
self.grace_period - the length of the grace period
self.close_date - the real due date
"""
def __init__(self, display_due_date_string, grace_period_string):
if display_due_date_string is not None:
try:
self.display_due_date = dateutil.parser.parse(display_due_date_string)
except ValueError:
log.error("Could not parse due date {0}".format(display_due_date_string))
raise
else:
self.display_due_date = None
if grace_period_string is not None and self.display_due_date:
try:
self.grace_period = parse_timedelta(grace_period_string)
self.close_date = self.display_due_date + self.grace_period
except:
log.error("Error parsing the grace period {0}".format(grace_period_string))
raise
else:
self.grace_period = None
self.close_date = self.display_due_date
......@@ -879,7 +879,9 @@ class ModuleSystem(object):
xqueue=None,
node_path="",
anonymous_student_id='',
course_id=None):
course_id=None,
open_ended_grading_interface=None,
s3_interface=None):
'''
Create a closure around the system environment.
......@@ -930,6 +932,8 @@ class ModuleSystem(object):
self.anonymous_student_id = anonymous_student_id
self.course_id = course_id
self.user_is_staff = user is not None and user.is_staff
self.open_ended_grading_interface = open_ended_grading_interface
self.s3_interface = s3_interface
def get(self, attr):
''' provide uniform access to attributes (like etree).'''
......
......@@ -226,6 +226,30 @@ def _get_module(user, request, descriptor, student_module_cache, course_id,
'waittime': settings.XQUEUE_WAITTIME_BETWEEN_REQUESTS
}
def get_or_default(key, default):
getattr(settings, key, default)
#This is a hacky way to pass settings to the combined open ended xmodule
#It needs an S3 interface to upload images to S3
#It needs the open ended grading interface in order to get peer grading to be done
#TODO: refactor these settings into module-specific settings when possible.
#this first checks to see if the descriptor is the correct one, and only sends settings if it is
is_descriptor_combined_open_ended = (descriptor.__class__.__name__ == 'CombinedOpenEndedDescriptor')
is_descriptor_peer_grading = (descriptor.__class__.__name__ == 'PeerGradingDescriptor')
open_ended_grading_interface = None
s3_interface = None
if is_descriptor_combined_open_ended or is_descriptor_peer_grading:
open_ended_grading_interface = settings.OPEN_ENDED_GRADING_INTERFACE
open_ended_grading_interface['mock_peer_grading'] = settings.MOCK_PEER_GRADING
open_ended_grading_interface['mock_staff_grading'] = settings.MOCK_STAFF_GRADING
if is_descriptor_combined_open_ended:
s3_interface = {
'access_key' : get_or_default('AWS_ACCESS_KEY_ID',''),
'secret_access_key' : get_or_default('AWS_SECRET_ACCESS_KEY',''),
'storage_bucket_name' : get_or_default('AWS_STORAGE_BUCKET_NAME','')
}
def inner_get_module(descriptor):
"""
Delegate to get_module. It does an access check, so may return None
......@@ -255,6 +279,8 @@ def _get_module(user, request, descriptor, student_module_cache, course_id,
node_path=settings.NODE_PATH,
anonymous_student_id=unique_id_for_user(user),
course_id=course_id,
open_ended_grading_interface=open_ended_grading_interface,
s3_interface=s3_interface,
)
# pass position specified in URL to module through ModuleSystem
system.set('position', position)
......
import json
import logging
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from xmodule.grading_service_module import GradingService, GradingServiceError
from xmodule.open_ended_grading_classes.grading_service_module import GradingService
from django.conf import settings
from django.http import HttpResponse, Http404
from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
......@@ -20,6 +14,8 @@ class ControllerQueryService(GradingService):
def __init__(self, config):
config['system'] = ModuleSystem(None, None, None, render_to_string, None)
super(ControllerQueryService, self).__init__(config)
self.url = config['url'] + config['grading_controller']
self.login_url = self.url + '/login/'
self.check_eta_url = self.url + '/get_submission_eta/'
self.is_unique_url = self.url + '/is_name_unique/'
self.combined_notifications_url = self.url + '/combined_notifications/'
......
from django.conf import settings
from xmodule.open_ended_grading_classes import peer_grading_service
from staff_grading_service import StaffGradingService
from open_ended_grading.controller_query_service import ControllerQueryService
from xmodule import peer_grading_service
import json
from student.models import unique_id_for_user
import open_ended_util
from courseware.models import StudentModule
import logging
from courseware.access import has_access
from util.cache import cache
import datetime
from xmodule import peer_grading_service
from xmodule.x_module import ModuleSystem
from mitxmako.shortcuts import render_to_string
......@@ -28,7 +26,7 @@ NOTIFICATION_TYPES = (
def staff_grading_notifications(course, user):
staff_gs = StaffGradingService(settings.STAFF_GRADING_INTERFACE)
staff_gs = StaffGradingService(settings.OPEN_ENDED_GRADING_INTERFACE)
pending_grading = False
img_path = ""
course_id = course.id
......@@ -61,7 +59,7 @@ def staff_grading_notifications(course, user):
def peer_grading_notifications(course, user):
system = ModuleSystem(None, None, None, render_to_string, None)
peer_gs = peer_grading_service.PeerGradingService(settings.PEER_GRADING_INTERFACE, system)
peer_gs = peer_grading_service.PeerGradingService(settings.OPEN_ENDED_GRADING_INTERFACE, system)
pending_grading = False
img_path = ""
course_id = course.id
......@@ -93,8 +91,7 @@ def peer_grading_notifications(course, user):
def combined_notifications(course, user):
controller_url = open_ended_util.get_controller_url()
controller_qs = ControllerQueryService(controller_url)
controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE)
student_id = unique_id_for_user(user)
user_is_staff = has_access(user, course, 'staff')
course_id = course.id
......
from django.conf import settings
import logging
log = logging.getLogger(__name__)
def get_controller_url():
peer_grading_url = settings.PEER_GRADING_INTERFACE['url']
split_url = peer_grading_url.split("/")
controller_url = "http://" + split_url[2] + "/grading_controller"
controller_settings = settings.PEER_GRADING_INTERFACE.copy()
controller_settings['url'] = controller_url
return controller_settings
......@@ -4,10 +4,7 @@ This module provides views that proxy to the staff grading backend service.
import json
import logging
import requests
from requests.exceptions import RequestException, ConnectionError, HTTPError
import sys
from xmodule.grading_service_module import GradingService, GradingServiceError
from xmodule.open_ended_grading_classes.grading_service_module import GradingService, GradingServiceError
from django.conf import settings
from django.http import HttpResponse, Http404
......@@ -64,6 +61,8 @@ class StaffGradingService(GradingService):
def __init__(self, config):
config['system'] = ModuleSystem(None, None, None, render_to_string, None)
super(StaffGradingService, self).__init__(config)
self.url = config['url'] + config['staff_grading']
self.login_url = self.url + '/login/'
self.get_next_url = self.url + '/get_next_submission/'
self.save_grade_url = self.url + '/save_grade/'
self.get_problem_list_url = self.url + '/get_problem_list/'
......@@ -164,7 +163,7 @@ def staff_grading_service():
if settings.MOCK_STAFF_GRADING:
_service = MockStaffGradingService()
else:
_service = StaffGradingService(settings.STAFF_GRADING_INTERFACE)
_service = StaffGradingService(settings.OPEN_ENDED_GRADING_INTERFACE)
return _service
......
......@@ -6,7 +6,8 @@ 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 xmodule import peer_grading_service, peer_grading_module
from xmodule.open_ended_grading_classes import peer_grading_service
from xmodule import peer_grading_module
from django.core.urlresolvers import reverse
from django.contrib.auth.models import Group
......@@ -25,6 +26,8 @@ log = logging.getLogger(__name__)
from django.test.utils import override_settings
from django.http import QueryDict
from xmodule.tests import test_util_open_ended
@override_settings(MODULESTORE=ct.TEST_DATA_XML_MODULESTORE)
class TestStaffGradingService(ct.PageLoader):
......@@ -144,9 +147,11 @@ class TestPeerGradingService(ct.PageLoader):
location = "i4x://edX/toy/peergrading/init"
self.mock_service = peer_grading_service.MockPeerGradingService()
self.system = ModuleSystem(location, None, None, render_to_string, None)
self.system = ModuleSystem(location, None, None, render_to_string, None,
s3_interface = test_util_open_ended.S3_INTERFACE,
open_ended_grading_interface=test_util_open_ended.OPEN_ENDED_GRADING_INTERFACE
)
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()
......
......@@ -2,7 +2,6 @@
import logging
import urllib
import re
from django.conf import settings
from django.views.decorators.cache import cache_control
......@@ -13,12 +12,10 @@ from student.models import unique_id_for_user
from courseware.courses import get_course_with_access
from controller_query_service import ControllerQueryService
from xmodule.grading_service_module import GradingServiceError
from xmodule.open_ended_grading_classes.grading_service_module import GradingServiceError
import json
from .staff_grading import StaffGrading
from student.models import unique_id_for_user
import open_ended_util
import open_ended_notifications
from xmodule.modulestore.django import modulestore
......@@ -30,8 +27,7 @@ log = logging.getLogger(__name__)
template_imports = {'urllib': urllib}
controller_url = open_ended_util.get_controller_url()
controller_qs = ControllerQueryService(controller_url)
controller_qs = ControllerQueryService(settings.OPEN_ENDED_GRADING_INTERFACE)
"""
Reverses the URL from the name and the course id, and then adds a trailing slash if
......
......@@ -100,10 +100,7 @@ XQUEUE_INTERFACE = AUTH_TOKENS['XQUEUE_INTERFACE']
MODULESTORE = AUTH_TOKENS.get('MODULESTORE', MODULESTORE)
CONTENTSTORE = AUTH_TOKENS.get('CONTENTSTORE', CONTENTSTORE)
STAFF_GRADING_INTERFACE = AUTH_TOKENS.get('STAFF_GRADING_INTERFACE',
STAFF_GRADING_INTERFACE)
PEER_GRADING_INTERFACE = AUTH_TOKENS.get('PEER_GRADING_INTERFACE',
PEER_GRADING_INTERFACE)
OPEN_ENDED_GRADING_INTERFACE = AUTH_TOKENS.get('OPEN_ENDED_GRADING_INTERFACE', OPEN_ENDED_GRADING_INTERFACE)
PEARSON_TEST_USER = "pearsontest"
PEARSON_TEST_PASSWORD = AUTH_TOKENS.get("PEARSON_TEST_PASSWORD")
......
......@@ -310,37 +310,30 @@ WIKI_USE_BOOTSTRAP_SELECT_WIDGET = False
WIKI_LINK_LIVE_LOOKUPS = False
WIKI_LINK_DEFAULT_LEVEL = 2
################################# Staff grading config #####################
#By setting up the default settings with an incorrect user name and password,
# will get an error when attempting to connect
STAFF_GRADING_INTERFACE = {
'url': 'http://sandbox-grader-001.m.edx.org/staff_grading',
'username': 'incorrect_user',
'password': 'incorrect_pass',
}
# Used for testing, debugging
MOCK_STAFF_GRADING = False
################################# Pearson TestCenter config ################
PEARSONVUE_SIGNINPAGE_URL = "https://www1.pearsonvue.com/testtaker/signin/SignInPage/EDX"
# TESTCENTER_ACCOMMODATION_REQUEST_EMAIL = "exam-help@edx.org"
################################# Peer grading config #####################
################################# open ended grading config #####################
#By setting up the default settings with an incorrect user name and password,
# will get an error when attempting to connect
PEER_GRADING_INTERFACE = {
OPEN_ENDED_GRADING_INTERFACE = {
'url': 'http://sandbox-grader-001.m.edx.org/peer_grading',
'username': 'incorrect_user',
'password': 'incorrect_pass',
'staff_grading' : 'staff_grading',
'peer_grading' : 'peer_grading',
'grading_controller' : 'grading_controller'
}
# Used for testing, debugging
# Used for testing, debugging peer grading
MOCK_PEER_GRADING = False
# Used for testing, debugging staff grading
MOCK_STAFF_GRADING = False
################################# Jasmine ###################################
JASMINE_TEST_DIRECTORY = PROJECT_ROOT + '/static/coffee'
......
......@@ -131,21 +131,17 @@ if os.path.isdir(DATA_DIR):
MITX_VERSION_STRING = os.popen('cd %s; git describe' % REPO_ROOT).read().strip()
################################# Staff grading config #####################
STAFF_GRADING_INTERFACE = {
'url': 'http://127.0.0.1:3033/staff_grading',
'username': 'lms',
'password': 'abcd',
}
################################# Peer grading config #####################
################################# Open ended grading config #####################
OPEN_ENDED_GRADING_INTERFACE = {
'url' : 'http://127.0.0.1:3033/',
'username' : 'lms',
'password' : 'abcd',
'staff_grading' : 'staff_grading',
'peer_grading' : 'peer_grading',
'grading_controller' : 'grading_controller'
}
PEER_GRADING_INTERFACE = {
'url': 'http://127.0.0.1:3033/peer_grading',
'username': 'lms',
'password': 'abcd',
}
################################ LMS Migration #################################
MITX_FEATURES['ENABLE_LMS_MIGRATION'] = True
MITX_FEATURES['ACCESS_REQUIRE_STAFF_FOR_COURSE'] = False # require that user be in the staff_* group to be able to enroll
......
......@@ -14,6 +14,7 @@
<table class="problem-list">
<tr>
<th>Problem Name</th>
<th>Due date</th>
<th>Graded</th>
<th>Available</th>
<th>Required</th>
......@@ -22,7 +23,18 @@
%for problem in problem_list:
<tr data-graded="${problem['num_graded']}" data-required="${problem['num_required']}">
<td class="problem-name">
%if problem['closed']:
${problem['problem_name']}
%else:
<a href="#problem" data-location="${problem['location']}" class="problem-button">${problem['problem_name']}</a>
%endif
</td>
<td>
% if problem['due']:
${problem['due']}
% else:
No due date
% endif
</td>
<td>
${problem['num_graded']}
......
<section class="container peer-grading-container">
<h2>Peer Grading</h2>
<p>The due date has passed, and
% if use_for_single_location:
peer grading for this problem is closed at this time.
%else:
peer grading is closed at this time.
%endif
</p>
</section>
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