Commit e093431d by Christina Roberts

Merge pull request #1581 from MITx/feature/vik/advanced-studio

Feature/vik/advanced studio
parents d7484c5e 102c594e
...@@ -68,6 +68,10 @@ log = logging.getLogger(__name__) ...@@ -68,6 +68,10 @@ log = logging.getLogger(__name__)
COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video'] COMPONENT_TYPES = ['customtag', 'discussion', 'html', 'problem', 'video']
ADVANCED_COMPONENT_TYPES = ['annotatable','combinedopenended', 'peergrading']
ADVANCED_COMPONENT_CATEGORY = 'advanced'
ADVANCED_COMPONENT_POLICY_KEY = 'advanced_modules'
# cdodge: these are categories which should not be parented, they are detached from the hierarchy # cdodge: these are categories which should not be parented, they are detached from the hierarchy
DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info'] DETACHED_CATEGORIES = ['about', 'static_tab', 'course_info']
...@@ -281,10 +285,31 @@ def edit_unit(request, location): ...@@ -281,10 +285,31 @@ def edit_unit(request, location):
component_templates = defaultdict(list) component_templates = defaultdict(list)
# Check if there are any advanced modules specified in the course policy. These modules
# should be specified as a list of strings, where the strings are the names of the modules
# in ADVANCED_COMPONENT_TYPES that should be enabled for the course.
course_metadata = CourseMetadata.fetch(course.location)
course_advanced_keys = course_metadata.get(ADVANCED_COMPONENT_POLICY_KEY, [])
# Set component types according to course policy file
component_types = list(COMPONENT_TYPES)
if isinstance(course_advanced_keys, list):
course_advanced_keys = [c for c in course_advanced_keys if c in ADVANCED_COMPONENT_TYPES]
if len(course_advanced_keys) > 0:
component_types.append(ADVANCED_COMPONENT_CATEGORY)
else:
log.error("Improper format for course advanced keys! {0}".format(course_advanced_keys))
templates = modulestore().get_items(Location('i4x', 'edx', 'templates')) templates = modulestore().get_items(Location('i4x', 'edx', 'templates'))
for template in templates: for template in templates:
if template.location.category in COMPONENT_TYPES: category = template.location.category
component_templates[template.location.category].append((
if category in course_advanced_keys:
category = ADVANCED_COMPONENT_CATEGORY
if category in component_types:
#This is a hack to create categories for different xmodules
component_templates[category].append((
template.display_name, template.display_name,
template.location.url(), template.location.url(),
'markdown' in template.metadata, 'markdown' in template.metadata,
......
...@@ -254,6 +254,30 @@ ...@@ -254,6 +254,30 @@
background: url(../img/html-icon.png) center no-repeat; background: url(../img/html-icon.png) center no-repeat;
} }
.large-openended-icon {
display: inline-block;
width: 100px;
height: 60px;
margin-right: 5px;
background: url(../img/large-openended-icon.png) center no-repeat;
}
.large-annotations-icon {
display: inline-block;
width: 100px;
height: 60px;
margin-right: 5px;
background: url(../img/large-annotations-icon.png) center no-repeat;
}
.large-advanced-icon {
display: inline-block;
width: 100px;
height: 60px;
margin-right: 5px;
background: url(../img/large-advanced-icon.png) center no-repeat;
}
.large-textbook-icon { .large-textbook-icon {
display: inline-block; display: inline-block;
width: 100px; width: 100px;
......
...@@ -79,6 +79,9 @@ class CombinedOpenEndedV1Module(): ...@@ -79,6 +79,9 @@ class CombinedOpenEndedV1Module():
INTERMEDIATE_DONE = 'intermediate_done' INTERMEDIATE_DONE = 'intermediate_done'
DONE = 'done' DONE = 'done'
#Where the templates live for this problem
TEMPLATE_DIR = "combinedopenended"
def __init__(self, system, location, definition, descriptor, def __init__(self, system, location, definition, descriptor,
instance_state=None, shared_state=None, metadata = None, static_data = None, **kwargs): instance_state=None, shared_state=None, metadata = None, static_data = None, **kwargs):
...@@ -343,7 +346,7 @@ class CombinedOpenEndedV1Module(): ...@@ -343,7 +346,7 @@ class CombinedOpenEndedV1Module():
Output: rendered html Output: rendered html
""" """
context = self.get_context() context = self.get_context()
html = self.system.render_template('combined_open_ended.html', context) html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
return html return html
def get_html_nonsystem(self): def get_html_nonsystem(self):
...@@ -354,7 +357,7 @@ class CombinedOpenEndedV1Module(): ...@@ -354,7 +357,7 @@ class CombinedOpenEndedV1Module():
Output: HTML rendered directly via Mako Output: HTML rendered directly via Mako
""" """
context = self.get_context() context = self.get_context()
html = self.system.render_template('combined_open_ended.html', context) html = self.system.render_template('{0}/combined_open_ended.html'.format(self.TEMPLATE_DIR), context)
return html return html
def get_html_base(self): def get_html_base(self):
...@@ -531,7 +534,7 @@ class CombinedOpenEndedV1Module(): ...@@ -531,7 +534,7 @@ class CombinedOpenEndedV1Module():
'task_name' : 'Scored Rubric', 'task_name' : 'Scored Rubric',
'class_name' : 'combined-rubric-container' 'class_name' : 'combined-rubric-container'
} }
html = self.system.render_template('combined_open_ended_results.html', context) html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_legend(self, get): def get_legend(self, get):
...@@ -543,7 +546,7 @@ class CombinedOpenEndedV1Module(): ...@@ -543,7 +546,7 @@ class CombinedOpenEndedV1Module():
context = { context = {
'legend_list' : LEGEND_LIST, 'legend_list' : LEGEND_LIST,
} }
html = self.system.render_template('combined_open_ended_legend.html', context) html = self.system.render_template('{0}/combined_open_ended_legend.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_results(self, get): def get_results(self, get):
...@@ -574,7 +577,7 @@ class CombinedOpenEndedV1Module(): ...@@ -574,7 +577,7 @@ class CombinedOpenEndedV1Module():
'submission_id' : ri['submission_ids'][i], 'submission_id' : ri['submission_ids'][i],
} }
context_list.append(context) context_list.append(context)
feedback_table = self.system.render_template('open_ended_result_table.html', { feedback_table = self.system.render_template('{0}/open_ended_result_table.html'.format(self.TEMPLATE_DIR), {
'context_list' : context_list, 'context_list' : context_list,
'grader_type_image_dict' : GRADER_TYPE_IMAGE_DICT, 'grader_type_image_dict' : GRADER_TYPE_IMAGE_DICT,
'human_grader_types' : HUMAN_GRADER_TYPE, 'human_grader_types' : HUMAN_GRADER_TYPE,
...@@ -586,7 +589,7 @@ class CombinedOpenEndedV1Module(): ...@@ -586,7 +589,7 @@ class CombinedOpenEndedV1Module():
'task_name' : "Feedback", 'task_name' : "Feedback",
'class_name' : "result-container", 'class_name' : "result-container",
} }
html = self.system.render_template('combined_open_ended_results.html', context) html = self.system.render_template('{0}/combined_open_ended_results.html'.format(self.TEMPLATE_DIR), context)
return {'html': html, 'success': True} return {'html': html, 'success': True}
def get_status_ajax(self, get): def get_status_ajax(self, get):
...@@ -700,7 +703,7 @@ class CombinedOpenEndedV1Module(): ...@@ -700,7 +703,7 @@ class CombinedOpenEndedV1Module():
'legend_list' : LEGEND_LIST, 'legend_list' : LEGEND_LIST,
'render_via_ajax' : render_via_ajax, 'render_via_ajax' : render_via_ajax,
} }
status_html = self.system.render_template("combined_open_ended_status.html", context) status_html = self.system.render_template("{0}/combined_open_ended_status.html".format(self.TEMPLATE_DIR), context)
return status_html return status_html
......
...@@ -30,6 +30,8 @@ class RubricParsingError(Exception): ...@@ -30,6 +30,8 @@ class RubricParsingError(Exception):
class CombinedOpenEndedRubric(object): class CombinedOpenEndedRubric(object):
TEMPLATE_DIR = "combinedopenended/openended"
def __init__ (self, system, view_only = False): def __init__ (self, system, view_only = False):
self.has_score = False self.has_score = False
self.view_only = view_only self.view_only = view_only
...@@ -57,9 +59,9 @@ class CombinedOpenEndedRubric(object): ...@@ -57,9 +59,9 @@ class CombinedOpenEndedRubric(object):
rubric_scores = [cat['score'] for cat in rubric_categories] rubric_scores = [cat['score'] for cat in rubric_categories]
max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories) max_scores = map((lambda cat: cat['options'][-1]['points']), rubric_categories)
max_score = max(max_scores) max_score = max(max_scores)
rubric_template = 'open_ended_rubric.html' rubric_template = '{0}/open_ended_rubric.html'.format(self.TEMPLATE_DIR)
if self.view_only: if self.view_only:
rubric_template = 'open_ended_view_only_rubric.html' rubric_template = '{0}/open_ended_view_only_rubric.html'.format(self.TEMPLATE_DIR)
html = self.system.render_template(rubric_template, html = self.system.render_template(rubric_template,
{'categories': rubric_categories, {'categories': rubric_categories,
'has_score': self.has_score, 'has_score': self.has_score,
...@@ -207,7 +209,7 @@ class CombinedOpenEndedRubric(object): ...@@ -207,7 +209,7 @@ class CombinedOpenEndedRubric(object):
for grader_type in tuple[3]: for grader_type in tuple[3]:
rubric_categories[i]['options'][j]['grader_types'].append(grader_type) rubric_categories[i]['options'][j]['grader_types'].append(grader_type)
html = self.system.render_template('open_ended_combined_rubric.html', html = self.system.render_template('{0}/open_ended_combined_rubric.html'.format(self.TEMPLATE_DIR),
{'categories': rubric_categories, {'categories': rubric_categories,
'has_score': True, 'has_score': True,
'view_only': True, 'view_only': True,
......
...@@ -40,6 +40,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -40,6 +40,8 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
</openended> </openended>
""" """
TEMPLATE_DIR = "combinedopenended/openended"
def setup_response(self, system, location, definition, descriptor): def setup_response(self, system, location, definition, descriptor):
""" """
Sets up the response type. Sets up the response type.
...@@ -397,10 +399,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -397,10 +399,10 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
rubric_scores = rubric_dict['rubric_scores'] rubric_scores = rubric_dict['rubric_scores']
if not response_items['success']: if not response_items['success']:
return system.render_template("open_ended_error.html", return system.render_template("{0}/open_ended_error.html".format(self.TEMPLATE_DIR),
{'errors': feedback}) {'errors': feedback})
feedback_template = system.render_template("open_ended_feedback.html", { feedback_template = system.render_template("{0}/open_ended_feedback.html".format(self.TEMPLATE_DIR), {
'grader_type': response_items['grader_type'], 'grader_type': response_items['grader_type'],
'score': "{0} / {1}".format(response_items['score'], self.max_score()), 'score': "{0} / {1}".format(response_items['score'], self.max_score()),
'feedback': feedback, 'feedback': feedback,
...@@ -558,7 +560,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -558,7 +560,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
@return: Rendered html @return: Rendered html
""" """
context = {'msg': feedback, 'id': "1", 'rows': 50, 'cols': 50} context = {'msg': feedback, 'id': "1", 'rows': 50, 'cols': 50}
html = system.render_template('open_ended_evaluation.html', context) html = system.render_template('{0}/open_ended_evaluation.html'.format(self.TEMPLATE_DIR), context)
return html return html
def handle_ajax(self, dispatch, get, system): def handle_ajax(self, dispatch, get, system):
...@@ -692,7 +694,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild): ...@@ -692,7 +694,7 @@ class OpenEndedModule(openendedchild.OpenEndedChild):
'accept_file_upload': self.accept_file_upload, 'accept_file_upload': self.accept_file_upload,
'eta_message' : eta_string, 'eta_message' : eta_string,
} }
html = system.render_template('open_ended.html', context) html = system.render_template('{0}/open_ended.html'.format(self.TEMPLATE_DIR), context)
return html return html
......
...@@ -32,6 +32,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -32,6 +32,8 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
</selfassessment> </selfassessment>
""" """
TEMPLATE_DIR = "combinedopenended/selfassessment"
def setup_response(self, system, location, definition, descriptor): def setup_response(self, system, location, definition, descriptor):
""" """
Sets up the module Sets up the module
...@@ -68,7 +70,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -68,7 +70,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
'accept_file_upload': self.accept_file_upload, 'accept_file_upload': self.accept_file_upload,
} }
html = system.render_template('self_assessment_prompt.html', context) html = system.render_template('{0}/self_assessment_prompt.html'.format(self.TEMPLATE_DIR), context)
return html return html
...@@ -129,7 +131,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -129,7 +131,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
#This is a dev_facing_error #This is a dev_facing_error
raise ValueError("Self assessment module is in an illegal state '{0}'".format(self.state)) raise ValueError("Self assessment module is in an illegal state '{0}'".format(self.state))
return system.render_template('self_assessment_rubric.html', context) return system.render_template('{0}/self_assessment_rubric.html'.format(self.TEMPLATE_DIR), context)
def get_hint_html(self, system): def get_hint_html(self, system):
""" """
...@@ -155,7 +157,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild): ...@@ -155,7 +157,7 @@ class SelfAssessmentModule(openendedchild.OpenEndedChild):
#This is a dev_facing_error #This is a dev_facing_error
raise ValueError("Self Assessment module is in an illegal state '{0}'".format(self.state)) raise ValueError("Self Assessment module is in an illegal state '{0}'".format(self.state))
return system.render_template('self_assessment_hint.html', context) return system.render_template('{0}/self_assessment_hint.html'.format(self.TEMPLATE_DIR), context)
def save_answer(self, get, system): def save_answer(self, get, system):
......
...@@ -471,6 +471,9 @@ class PeerGradingModule(XModule): ...@@ -471,6 +471,9 @@ class PeerGradingModule(XModule):
#This is a student_facing_error #This is a student_facing_error
error_text = "Could not get list of problems to peer grade. Please notify course staff." error_text = "Could not get list of problems to peer grade. Please notify course staff."
success = False success = False
except:
log.exception("Could not contact peer grading service.")
success = False
def _find_corresponding_module_for_location(location): def _find_corresponding_module_for_location(location):
...@@ -589,7 +592,6 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor): ...@@ -589,7 +592,6 @@ class PeerGradingDescriptor(XmlDescriptor, EditingDescriptor):
'task_xml': dictionary of xml strings, 'task_xml': dictionary of xml strings,
} }
""" """
log.debug("In definition")
expected_children = [] expected_children = []
for child in expected_children: for child in expected_children:
if len(xml_object.xpath(child)) == 0: if len(xml_object.xpath(child)) == 0:
......
---
metadata:
display_name: Open Ended Response
max_attempts: 1
max_score: 1
is_graded: False
version: 1
display_name: Open Ended Response
skip_spelling_checks: False
accept_file_upload: False
data: |
<combinedopenended>
<rubric>
<rubric>
<category>
<description>Category 1</description>
<option>
The response does not incorporate what is needed for a one response.
</option>
<option>
The response is correct for category 1.
</option>
</category>
</rubric>
</rubric>
<prompt>
<p>Why is the sky blue?</p>
</prompt>
<task>
<selfassessment/>
</task>
<task>
<openended min_score_to_attempt="1" max_score_to_attempt="2">
<openendedparam>
<initial_display>Enter essay here.</initial_display>
<answer_display>This is the answer.</answer_display>
<grader_payload>{"grader_settings" : "peer_grading.conf", "problem_id" : "700x/Demo"}</grader_payload>
</openendedparam>
</openended>
</task>
</combinedopenended>
children: []
---
metadata:
display_name: Peer Grading Interface
attempts: 1
use_for_single_location: False
link_to_location: None
is_graded: False
max_grade: 1
data: |
<peergrading>
</peergrading>
children: []
...@@ -89,14 +89,24 @@ def peer_grading(request, course_id): ...@@ -89,14 +89,24 @@ def peer_grading(request, course_id):
Show a peer grading interface Show a peer grading interface
''' '''
#Get the current course
course = get_course_with_access(request.user, course_id, 'load') course = get_course_with_access(request.user, course_id, 'load')
course_id_parts = course.id.split("/") course_id_parts = course.id.split("/")
course_id_norun = "/".join(course_id_parts[0:2]) false_dict = [False,"False", "false", "FALSE"]
pg_location = "i4x://" + course_id_norun + "/peergrading/init"
#Reverse the base course url
base_course_url = reverse('courses') base_course_url = reverse('courses')
try: try:
problem_url_parts = search.path_to_location(modulestore(), course.id, pg_location) #TODO: This will not work with multiple runs of a course. Make it work. The last key in the Location passed
#to get_items is called revision. Is this the same as run?
#Get the peer grading modules currently in the course
items = modulestore().get_items(['i4x', None, course_id_parts[1], 'peergrading', None])
#See if any of the modules are centralized modules (ie display info from multiple problems)
items = [i for i in items if i.metadata.get("use_for_single_location", True) in false_dict]
#Get the first one
item_location = items[0].location
#Generate a url for the first module and redirect the user to it
problem_url_parts = search.path_to_location(modulestore(), course.id, item_location)
problem_url = generate_problem_url(problem_url_parts, base_course_url) problem_url = generate_problem_url(problem_url_parts, base_course_url)
return HttpResponseRedirect(problem_url) return HttpResponseRedirect(problem_url)
......
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