Refactored the status icon and span to a unified template and updated tests
This commit is contained in:
@@ -39,25 +39,27 @@ graded status as'status'
|
||||
# makes sense, but a bunch of problems have markup that assumes block. Bigger TODO: figure out a
|
||||
# general css and layout strategy for capa, document it, then implement it.
|
||||
|
||||
import time
|
||||
import json
|
||||
import logging
|
||||
from lxml import etree
|
||||
import re
|
||||
import shlex # for splitting quoted strings
|
||||
import sys
|
||||
import pyparsing
|
||||
import html5lib
|
||||
import bleach
|
||||
|
||||
from .util import sanitize_html
|
||||
from .registry import TagRegistry
|
||||
from chem import chemcalc
|
||||
from calc.preview import latex_preview
|
||||
import xqueue_interface
|
||||
from xqueue_interface import XQUEUE_TIMEOUT
|
||||
import time
|
||||
from datetime import datetime
|
||||
|
||||
import bleach
|
||||
import html5lib
|
||||
import pyparsing
|
||||
import re
|
||||
from calc.preview import latex_preview
|
||||
from chem import chemcalc
|
||||
from lxml import etree
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
|
||||
import xqueue_interface
|
||||
from xmodule.stringify import stringify_children
|
||||
from capa.xqueue_interface import XQUEUE_TIMEOUT
|
||||
from .registry import TagRegistry
|
||||
from .util import sanitize_html
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -232,7 +234,8 @@ class InputTypeBase(object):
|
||||
|
||||
# put hint above msg if it should be displayed
|
||||
if self.hintmode == 'always':
|
||||
self.msg = self.hint + ('<br/>' if self.msg else '') + self.msg
|
||||
self.msg = HTML('{hint}<br/>{msg}' if self.msg else '{hint}').format(hint=HTML(self.hint),
|
||||
msg=HTML(self.msg))
|
||||
|
||||
self.status = state.get('status', 'unanswered')
|
||||
|
||||
@@ -322,15 +325,18 @@ class InputTypeBase(object):
|
||||
'msg': self.msg,
|
||||
'response_data': self.response_data,
|
||||
'STATIC_URL': self.capa_system.STATIC_URL,
|
||||
'describedby_html': '',
|
||||
'describedby_html': HTML(''),
|
||||
}
|
||||
|
||||
# Don't add aria-describedby attribute if there are no descriptions
|
||||
if self.response_data.get('descriptions'):
|
||||
description_ids = ' '.join(self.response_data.get('descriptions').keys())
|
||||
context.update(
|
||||
{'describedby_html': 'aria-describedby="{}"'.format(description_ids)}
|
||||
)
|
||||
# Generate the list of ids to be used with the aria-describedby field.
|
||||
# Every list should contain the status id
|
||||
status_id = 'status_' + self.input_id
|
||||
descriptions = list([status_id])
|
||||
descriptions.extend(self.response_data.get('descriptions', {}).keys())
|
||||
description_ids = ' '.join(descriptions)
|
||||
context.update(
|
||||
{'describedby_html': HTML('aria-describedby="{}"').format(description_ids)}
|
||||
)
|
||||
|
||||
context.update(
|
||||
(a, v) for (a, v) in self.loaded_attributes.iteritems() if a in self.to_render
|
||||
@@ -522,9 +528,10 @@ class ChoiceGroup(InputTypeBase):
|
||||
choices.append((choice.get("name"), stringify_children(choice)))
|
||||
else:
|
||||
if choice.tag != 'compoundhint':
|
||||
msg = u'[capa.inputtypes.extract_choices] {error_message}'.format(
|
||||
# Translators: '<choice>' and '<compoundhint>' are tag names and should not be translated.
|
||||
error_message=_('Expected a <choice> or <compoundhint> tag; got {given_tag} instead').format(
|
||||
msg = Text('[capa.inputtypes.extract_choices] {error_message}').format(
|
||||
error_message=Text(
|
||||
# Translators: '<choice>' and '<compoundhint>' are tag names and should not be translated.
|
||||
_('Expected a <choice> or <compoundhint> tag; got {given_tag} instead')).format(
|
||||
given_tag=choice.tag
|
||||
)
|
||||
)
|
||||
@@ -939,13 +946,13 @@ class MatlabInput(CodeInput):
|
||||
queue_msg = self.queue_msg
|
||||
if len(self.queue_msg) > 0: # An empty string cannot be parsed as XML but is okay to include in the template.
|
||||
try:
|
||||
etree.XML(u'<div>{0}</div>'.format(self.queue_msg))
|
||||
etree.XML(HTML(u'<div>{0}</div>').format(HTML(self.queue_msg)))
|
||||
except etree.XMLSyntaxError:
|
||||
try:
|
||||
html5lib.parseFragment(self.queue_msg, treebuilder='lxml', namespaceHTMLElements=False)[0]
|
||||
except (IndexError, ValueError):
|
||||
# If neither can parse queue_msg, it contains invalid xml.
|
||||
queue_msg = u"<span>{0}</span>".format(_("Error running code."))
|
||||
queue_msg = HTML("<span>{0}</span>").format(_("Error running code."))
|
||||
|
||||
extra_context = {
|
||||
'queue_len': str(self.queue_len),
|
||||
@@ -1797,10 +1804,10 @@ class ChoiceTextGroup(InputTypeBase):
|
||||
|
||||
for choice in element:
|
||||
if choice.tag != 'choice':
|
||||
msg = u"[capa.inputtypes.extract_choices] {0}".format(
|
||||
msg = Text("[capa.inputtypes.extract_choices] {0}").format(
|
||||
# Translators: a "tag" is an XML element, such as "<b>" in HTML
|
||||
_("Expected a {expected_tag} tag; got {given_tag} instead").format(
|
||||
expected_tag=u"<choice>",
|
||||
Text(_("Expected a {expected_tag} tag; got {given_tag} instead")).format(
|
||||
expected_tag="<choice>",
|
||||
given_tag=choice.tag,
|
||||
)
|
||||
)
|
||||
|
||||
@@ -22,12 +22,10 @@
|
||||
% for option in options:
|
||||
<li>
|
||||
% if has_options_value:
|
||||
% if all([c == 'correct' for c in option['choice'], status]):
|
||||
<span class="tag-status correct" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Correct</span></span>
|
||||
% elif all([c == 'partially-correct' for c in option['choice'], status]):
|
||||
<span class="tag-status partially-correct" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Partially Correct</span></span>
|
||||
% elif all([c == 'incorrect' for c in option['choice'], status]):
|
||||
<span class="tag-status incorrect" id="status_${id}" aria-describedby="input_${id}_comment"><span class="sr">Status: Incorrect</span></span>
|
||||
% if all([c == status.classname for c in option['choice'], status]):
|
||||
<span class="tag-status ${status.classname}" aria-describedby="input_${id}_comment">
|
||||
<%include file="status_span.html" args="status=status"/>
|
||||
</span>
|
||||
% endif
|
||||
% endif
|
||||
|
||||
@@ -53,7 +51,7 @@
|
||||
<input type="hidden" class="value" name="input_${id}" id="input_${id}" value="${value|h}" />
|
||||
% endif
|
||||
|
||||
<span class="status ${status.classname}" id="status_${id}" aria-describedby="label_${id}"><span class="sr">${status.display_name}</span></span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
<p id="answer_${id}" class="answer answer-annotation"></p>
|
||||
</div>
|
||||
|
||||
@@ -2,23 +2,21 @@
|
||||
<div id="chemicalequationinput_${id}" class="chemicalequationinput">
|
||||
<div class="script_placeholder" data-src="${previewer}"/>
|
||||
|
||||
<div class="${status.classname}" id="status_${id}">
|
||||
<div class="${status.classname}">
|
||||
|
||||
<input type="text" name="input_${id}" id="input_${id}" aria-label="${remove_markup(response_data['label'])}" aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
|
||||
% if size:
|
||||
size="${size}"
|
||||
% endif
|
||||
/>
|
||||
<input type="text" name="input_${id}" id="input_${id}" aria-label="${remove_markup(response_data['label'])}"
|
||||
aria-describedby="answer_${id}" data-input-id="${id}" value="${value|h}"
|
||||
% if size:
|
||||
size="${size}"
|
||||
% endif
|
||||
/>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<p class="status">
|
||||
${value|h}
|
||||
<span class="sr">${status.display_name}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
<div id="input_${id}_preview" class="equation"></div>
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
|
||||
</div>
|
||||
% endif
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
))
|
||||
%>
|
||||
<form class="choicegroup capa_inputtype" id="inputtype_${id}">
|
||||
<fieldset ${HTML(describedby_html)}>
|
||||
<fieldset ${describedby_html}>
|
||||
% if response_data['label']:
|
||||
<legend id="${id}-legend" class="response-fieldset-legend field-group-hd">${response_data['label']}</legend>
|
||||
% endif
|
||||
@@ -37,7 +37,7 @@
|
||||
% endif
|
||||
% endif
|
||||
class="${label_class}"
|
||||
${HTML(describedby_html)}
|
||||
${describedby_html}
|
||||
>
|
||||
<input type="${input_type}" name="input_${id}${name_array_suffix}" id="input_${id}_${choice_id}" class="field-input input-${input_type}" value="${choice_id}"
|
||||
## If the student selected this choice...
|
||||
@@ -49,8 +49,8 @@
|
||||
/> ${HTML(choice_label)}
|
||||
|
||||
% if is_radio_input(choice_id):
|
||||
% if status in ('correct', 'partially-correct', 'incorrect') and not show_correctness == 'never':
|
||||
<span class="sr status" id="${id}-${choice_id}-labeltext">${status.display_name}</span>
|
||||
% if not show_correctness == 'never' and status.classname != 'unanswered':
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
% endif
|
||||
% endif
|
||||
</label>
|
||||
@@ -59,10 +59,12 @@
|
||||
<span id="answer_${id}"></span>
|
||||
</fieldset>
|
||||
<div class="indicator-container">
|
||||
% if input_type == 'checkbox' or not value:
|
||||
<span class="status ${status.classname if show_correctness != 'never' else 'unanswered'}" id="status_${id}" aria-describedby="${id}-legend" data-tooltip="${status.display_tooltip}">
|
||||
<span class="sr">${status.display_tooltip}</span>
|
||||
</span>
|
||||
% if input_type == 'checkbox' or status.classname == 'unanswered':
|
||||
% if show_correctness != 'never':
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
% else:
|
||||
<%include file="status_span.html" args="status=status, status_id=id, hide_correctness=True"/>
|
||||
% endif
|
||||
% endif
|
||||
</div>
|
||||
% if show_correctness == "never" and (value or status not in ['unsubmitted']):
|
||||
|
||||
@@ -66,9 +66,7 @@ from openedx.core.djangolib.markup import HTML
|
||||
|
||||
<div class="indicator-container">
|
||||
% if input_type == 'checkbox' or not element_checked:
|
||||
<span class="status ${status.classname}" id="status_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
% endif
|
||||
</div>
|
||||
|
||||
|
||||
@@ -26,12 +26,9 @@ from openedx.core.djangolib.markup import HTML
|
||||
</span>
|
||||
|
||||
<div class="grader-status" tabindex="-1">
|
||||
<span id="status_${id}"
|
||||
class="${status.classname}"
|
||||
aria-describedby="input_${id}"
|
||||
>
|
||||
<span class="status sr">${status.display_name}</span>
|
||||
</span>
|
||||
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
% if status == 'queued':
|
||||
<span style="display:none;" class="xqueue" id="${id}">${queue_len}</span>
|
||||
% endif
|
||||
|
||||
@@ -16,9 +16,7 @@
|
||||
|
||||
<input type="text" name="input_${id}" aria-describedby="answer_${id}" id="input_${id}" value="${value|h}" style="display:none;"/>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
</p>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
|
||||
@@ -10,9 +10,7 @@
|
||||
<input type="hidden" name="target_shape" id="target_shape" value ="${target_shape}"></input>
|
||||
<input type="hidden" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="${value|h}"/>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
</p>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
|
||||
|
||||
@@ -17,8 +17,9 @@
|
||||
<input type="text" name="input_${id}" id="input_${id}" aria-describedby="answer_${id}" value="${value|h}"
|
||||
style="display:none;"/>
|
||||
|
||||
|
||||
<p class="status drag-and-drop--status" aria-describedby="input_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="script_placeholder" data-src="${applet_loader}"/>
|
||||
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
|
||||
<div class="${status.classname}" id="status_${id}">
|
||||
<div class="${status.classname}">
|
||||
% endif
|
||||
|
||||
<div id="genex_container"></div>
|
||||
@@ -12,7 +12,7 @@
|
||||
<input type="hidden" name="input_${id}" aria-describedby="answer_${id}" id="input_${id}" value="${value|h}"/>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div class="script_placeholder" data-src="${applet_loader}"/>
|
||||
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
|
||||
<div class="${status.classname}" id="status_${id}">
|
||||
<div class="${status.classname}">
|
||||
% endif
|
||||
|
||||
<div id="applet_${id}" class="applet" data-molfile-src="${file}" style="display:block;width:500px;height:400px">
|
||||
@@ -17,7 +17,7 @@
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
<p class="status" aria-describedby="input_${id}">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
<div class="error_message" style="padding: 5px 5px 5px 5px; background-color:#FA6666; height:60px;width:400px; display: none"></div>
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<%! from openedx.core.djangolib.markup import HTML %>
|
||||
<% doinline = 'style="display:inline-block;vertical-align:top"' if inline else "" %>
|
||||
<div id="formulaequationinput_${id}" class="inputtype formulaequationinput" ${doinline | n, decode.utf8}>
|
||||
<div class="${status.classname}" id="status_${id}">
|
||||
<div class="${status.classname}">
|
||||
% if response_data['label']:
|
||||
<label class="problem-group-label" for="input_${id}" id="label_${id}">${response_data['label']}</label>
|
||||
% endif
|
||||
@@ -11,16 +11,14 @@
|
||||
% endfor
|
||||
<input type="text" name="input_${id}" id="input_${id}"
|
||||
data-input-id="${id}" value="${value}"
|
||||
${HTML(describedby_html)}
|
||||
${describedby_html}
|
||||
% if size:
|
||||
size="${size}"
|
||||
% endif
|
||||
/>
|
||||
<span class="trailing_text">${trailing_text}</span>
|
||||
|
||||
<span class="status" id="${id}_status" aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
|
||||
<span class="sr">${status.display_tooltip}</span>
|
||||
</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
|
||||
@@ -40,11 +40,5 @@
|
||||
(new ImageInput('${id}'));
|
||||
</script>
|
||||
|
||||
<span
|
||||
class="status ${status.classname}"
|
||||
id="status_${id}"
|
||||
aria-describedby="input_${id}"
|
||||
>
|
||||
<span class="sr">${status.display_name}</span>
|
||||
</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</div>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
<%page expression_filter="h"/>
|
||||
<form class="javascriptinput capa_inputtype" id="inputtype_${id}">
|
||||
<input type="hidden" name="input_${id}" id="input_${id}" class="javascriptinput_input"/>
|
||||
<div class="javascriptinput_data" data-display_class="${display_class}"
|
||||
data-problem_state="${problem_state}" data-params="${params}"
|
||||
data-submission="${value|h}" data-evaluation="${msg|h}">
|
||||
data-submission="${value}" data-evaluation="${msg}">
|
||||
</div>
|
||||
<div class="script_placeholder" data-src="/static/js/${display_file}"></div>
|
||||
<div class="javascriptinput_container"></div>
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
<div class="script_placeholder" data-src="${jschannel_loader}"/>
|
||||
<div class="script_placeholder" data-src="${jsinput_loader}"/>
|
||||
% if status in ['unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete']:
|
||||
<div class="${status.classname}" id="status_${id}">
|
||||
<div class="${status.classname}">
|
||||
% endif
|
||||
|
||||
<iframe name="iframe_${id}"
|
||||
@@ -42,7 +42,7 @@
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
<p class="status">
|
||||
<span class="sr">${status.display_name}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</p>
|
||||
|
||||
<div class="error_message" style="padding: 5px 5px 5px 5px; background-color:#FA6666; height:60px;width:400px; display: none"></div>
|
||||
|
||||
@@ -17,12 +17,9 @@
|
||||
>${value|h}</textarea>
|
||||
|
||||
<div class="grader-status" tabindex="-1">
|
||||
<span id="status_${id}"
|
||||
class="${status.classname}"
|
||||
aria-describedby="input_${id}"
|
||||
>
|
||||
<span class="status sr">${status.display_name}</span>
|
||||
</span>
|
||||
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
% if status == 'queued':
|
||||
<span style="display:none;" class="xqueue" id="${id}">${queue_len}</span>
|
||||
% endif
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<p class="question-description" id="${description_id}">${description_text}</p>
|
||||
% endfor
|
||||
|
||||
<select name="input_${id}" id="input_${id}" ${HTML(describedby_html)}>
|
||||
<select name="input_${id}" id="input_${id}" ${describedby_html}>
|
||||
<option value="option_${id}_dummy_default">${default_option_text}</option>
|
||||
% for option_id, option_description in options:
|
||||
<option value="${option_id}"
|
||||
@@ -23,11 +23,7 @@
|
||||
</select>
|
||||
|
||||
<div class="indicator-container">
|
||||
<span class="status ${status.classname}"
|
||||
id="status_${id}"
|
||||
aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
|
||||
<span class="sr">${status.display_tooltip}</span>
|
||||
</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
</div>
|
||||
<p class="answer" id="answer_${id}"></p>
|
||||
% if msg:
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
|
||||
<span id="answer_${id}"></span>
|
||||
<div class="indicator-container">
|
||||
<span class="status ${status.classname}" id="status_${id}" aria-describedby="input_${id}"></span>
|
||||
<span class="sr">${status.display_tooltip}</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<%page expression_filter="h"/>
|
||||
<div class="solution-span">
|
||||
<span id="solution_${id}"></span>
|
||||
</div>
|
||||
|
||||
13
common/lib/capa/capa/templates/status_span.html
Normal file
13
common/lib/capa/capa/templates/status_span.html
Normal file
@@ -0,0 +1,13 @@
|
||||
<%page expression_filter="h" args="status, status_id='', hide_correctness=False"/>
|
||||
|
||||
% if status_id == '':
|
||||
<span class="status ${'' if hide_correctness == True else status.classname}"
|
||||
data-tooltip="${'' if hide_correctness == True else status.display_tooltip}">
|
||||
% else:
|
||||
<span class="status ${'' if hide_correctness == True else status.classname}" id="status_${status_id}"
|
||||
data-tooltip="${'' if hide_correctness == True else status.display_tooltip}">
|
||||
% endif
|
||||
% if hide_correctness == False:
|
||||
<span class="sr">${status.display_name}</span><span class="status-icon" aria-hidden="true"></span>
|
||||
% endif
|
||||
</span>
|
||||
@@ -9,7 +9,7 @@
|
||||
% endif
|
||||
|
||||
% if status in ('unsubmitted', 'correct', 'incorrect', 'partially-correct', 'incomplete'):
|
||||
<div class="${status.classname} ${doinline}" id="status_${id}">
|
||||
<div class="${status.classname} ${doinline}">
|
||||
% endif
|
||||
|
||||
% if hidden:
|
||||
@@ -23,7 +23,7 @@
|
||||
% for description_id, description_text in response_data['descriptions'].items():
|
||||
<p class="question-description" id="${description_id}">${description_text}</p>
|
||||
% endfor
|
||||
<input type="text" name="input_${id}" id="input_${id}" ${HTML(describedby_html)} value="${value}"
|
||||
<input type="text" name="input_${id}" id="input_${id}" ${describedby_html} value="${value}"
|
||||
% if do_math:
|
||||
class="math"
|
||||
% endif
|
||||
@@ -36,9 +36,7 @@
|
||||
/>
|
||||
<span class="trailing_text">${trailing_text}</span>
|
||||
|
||||
<span class="status" aria-describedby="label_${id}" data-tooltip="${status.display_tooltip}">
|
||||
<span class="sr">${status.display_tooltip}</span>
|
||||
</span>
|
||||
<%include file="status_span.html" args="status=status, status_id=id"/>
|
||||
|
||||
<p id="answer_${id}" class="answer"></p>
|
||||
|
||||
|
||||
@@ -22,7 +22,8 @@ def get_template(template_name):
|
||||
Return template for a capa inputtype.
|
||||
"""
|
||||
return TemplateLookup(
|
||||
directories=[path(__file__).dirname().dirname() / 'templates']
|
||||
directories=[path(__file__).dirname().dirname() / 'templates'],
|
||||
default_filters=['decode.utf8']
|
||||
).get_template(template_name)
|
||||
|
||||
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
"""
|
||||
CAPA HTML rendering tests.
|
||||
"""
|
||||
import ddt
|
||||
import unittest
|
||||
from lxml import etree
|
||||
import os
|
||||
import textwrap
|
||||
import unittest
|
||||
|
||||
import ddt
|
||||
import mock
|
||||
import os
|
||||
from capa.tests.helpers import test_capa_system, new_loncapa_problem
|
||||
from lxml import etree
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
|
||||
from .response_xml_factory import StringResponseXMLFactory, CustomResponseXMLFactory
|
||||
from capa.tests.helpers import test_capa_system, new_loncapa_problem
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
@@ -190,7 +191,7 @@ class CapaHtmlRenderTest(unittest.TestCase):
|
||||
'trailing_text': '',
|
||||
'size': None,
|
||||
'response_data': {'label': 'Test question', 'descriptions': {}},
|
||||
'describedby_html': ''
|
||||
'describedby_html': HTML('aria-describedby="status_1_2_1"')
|
||||
}
|
||||
|
||||
expected_solution_context = {'id': '1_solution_1'}
|
||||
|
||||
@@ -2,15 +2,16 @@
|
||||
Tests for the logic in input type mako templates.
|
||||
"""
|
||||
|
||||
from collections import OrderedDict
|
||||
import unittest
|
||||
import capa
|
||||
import os.path
|
||||
import json
|
||||
from lxml import etree
|
||||
from mako.template import Template as MakoTemplate
|
||||
from mako import exceptions
|
||||
import unittest
|
||||
from collections import OrderedDict
|
||||
|
||||
from capa.inputtypes import Status
|
||||
from capa.tests.helpers import capa_render_template
|
||||
from lxml import etree
|
||||
from mako import exceptions
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
|
||||
from xmodule.stringify import stringify_children
|
||||
|
||||
|
||||
@@ -23,7 +24,7 @@ class TemplateError(Exception):
|
||||
|
||||
class TemplateTestCase(unittest.TestCase):
|
||||
"""
|
||||
Utilitites for testing templates.
|
||||
Utilities for testing templates.
|
||||
"""
|
||||
|
||||
# Subclasses override this to specify the file name of the template
|
||||
@@ -46,16 +47,9 @@ class TemplateTestCase(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
"""
|
||||
Load the template under test.
|
||||
Initialize the context.
|
||||
"""
|
||||
super(TemplateTestCase, self).setUp()
|
||||
capa_path = capa.__path__[0]
|
||||
self.template_path = os.path.join(capa_path,
|
||||
'templates',
|
||||
self.TEMPLATE_NAME)
|
||||
with open(self.template_path) as f:
|
||||
self.template = MakoTemplate(f.read(), default_filters=['decode.utf8'])
|
||||
|
||||
self.context = {}
|
||||
|
||||
def render_to_xml(self, context_dict):
|
||||
@@ -66,7 +60,7 @@ class TemplateTestCase(unittest.TestCase):
|
||||
# add dummy STATIC_URL to template context
|
||||
context_dict.setdefault("STATIC_URL", "/dummy-static/")
|
||||
try:
|
||||
xml_str = self.template.render_unicode(**context_dict)
|
||||
xml_str = capa_render_template(self.TEMPLATE_NAME, context_dict)
|
||||
except:
|
||||
raise TemplateError(exceptions.text_error_template().render())
|
||||
|
||||
@@ -196,10 +190,10 @@ class TemplateTestCase(unittest.TestCase):
|
||||
# (used to by CSS to draw the green check / red x)
|
||||
self.assert_has_text(
|
||||
xml,
|
||||
"//span[@class=normalize-space('status {}')]/span[@class='sr']".format(
|
||||
"//span[@class='status {}']/span[@class='sr']".format(
|
||||
div_class if status_class else ''
|
||||
),
|
||||
self.context['status'].display_tooltip
|
||||
self.context['status'].display_name
|
||||
)
|
||||
|
||||
def assert_label(self, xpath=None, aria_label=False):
|
||||
@@ -259,7 +253,7 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
|
||||
'name_array_suffix': '1',
|
||||
'value': '3',
|
||||
'response_data': self.RESPONSE_DATA,
|
||||
'describedby_html': self.DESCRIBEDBY,
|
||||
'describedby_html': HTML(self.DESCRIBEDBY),
|
||||
}
|
||||
|
||||
def test_problem_marked_correct(self):
|
||||
@@ -290,11 +284,9 @@ class ChoiceGroupTemplateTest(TemplateTestCase):
|
||||
(not a particular option) is marked incorrect.
|
||||
"""
|
||||
conditions = [
|
||||
{'status': Status('incorrect'), 'input_type': 'radio', 'value': ''},
|
||||
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': []},
|
||||
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2']},
|
||||
{'status': Status('incorrect'), 'input_type': 'checkbox', 'value': ['2', '3']},
|
||||
{'status': Status('incomplete'), 'input_type': 'radio', 'value': ''},
|
||||
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': []},
|
||||
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2']},
|
||||
{'status': Status('incomplete'), 'input_type': 'checkbox', 'value': ['2', '3']}]
|
||||
@@ -506,7 +498,7 @@ class TextlineTemplateTest(TemplateTestCase):
|
||||
'preprocessor': None,
|
||||
'trailing_text': None,
|
||||
'response_data': self.RESPONSE_DATA,
|
||||
'describedby_html': self.DESCRIBEDBY,
|
||||
'describedby_html': HTML(self.DESCRIBEDBY),
|
||||
}
|
||||
|
||||
def test_section_class(self):
|
||||
@@ -526,7 +518,7 @@ class TextlineTemplateTest(TemplateTestCase):
|
||||
"""
|
||||
Verify status information.
|
||||
"""
|
||||
self.assert_status(status_div=True)
|
||||
self.assert_status(status_class=True)
|
||||
|
||||
def test_label(self):
|
||||
"""
|
||||
@@ -632,7 +624,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
|
||||
'reported_status': 'REPORTED_STATUS',
|
||||
'trailing_text': None,
|
||||
'response_data': self.RESPONSE_DATA,
|
||||
'describedby_html': self.DESCRIBEDBY,
|
||||
'describedby_html': HTML(self.DESCRIBEDBY),
|
||||
}
|
||||
|
||||
def test_no_size(self):
|
||||
@@ -657,7 +649,7 @@ class FormulaEquationInputTemplateTest(TemplateTestCase):
|
||||
"""
|
||||
Verify status information.
|
||||
"""
|
||||
self.assert_status(status_div=True)
|
||||
self.assert_status(status_class=True)
|
||||
|
||||
def test_label(self):
|
||||
"""
|
||||
@@ -852,7 +844,7 @@ class OptionInputTemplateTest(TemplateTestCase):
|
||||
'value': 0,
|
||||
'default_option_text': 'Select an option',
|
||||
'response_data': self.RESPONSE_DATA,
|
||||
'describedby_html': self.DESCRIBEDBY,
|
||||
'describedby_html': HTML(self.DESCRIBEDBY),
|
||||
}
|
||||
|
||||
def test_select_options(self):
|
||||
@@ -929,8 +921,8 @@ class DragAndDropTemplateTest(TemplateTestCase):
|
||||
xpath = "//div[@class='{0}']".format(expected_css_class)
|
||||
self.assert_has_xpath(xml, xpath, self.context)
|
||||
|
||||
# Expect a <p> with the status
|
||||
xpath = "//p[@class='status drag-and-drop--status']/span[@class='sr']"
|
||||
# Expect a <span> with the status
|
||||
xpath = "//span[@class='status {0}']/span[@class='sr']".format(expected_css_class)
|
||||
self.assert_has_text(xml, xpath, expected_text, exact=False)
|
||||
|
||||
def test_drag_and_drop_json_html(self):
|
||||
@@ -1206,7 +1198,7 @@ class CodeinputTemplateTest(TemplateTestCase):
|
||||
'aria_label': 'python editor',
|
||||
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
|
||||
'response_data': self.RESPONSE_DATA,
|
||||
'describedby': self.DESCRIBEDBY,
|
||||
'describedby': HTML(self.DESCRIBEDBY),
|
||||
}
|
||||
|
||||
def test_label(self):
|
||||
|
||||
@@ -16,27 +16,27 @@ TODO:
|
||||
- test funny xml chars -- should never get xml parse error if things are escaped properly.
|
||||
|
||||
"""
|
||||
from collections import OrderedDict
|
||||
import json
|
||||
from lxml import etree
|
||||
from lxml.html import fromstring
|
||||
import unittest
|
||||
import textwrap
|
||||
import unittest
|
||||
import xml.sax.saxutils as saxutils
|
||||
from collections import OrderedDict
|
||||
|
||||
from capa.tests.helpers import test_capa_system
|
||||
from capa import inputtypes
|
||||
from capa.checker import DemoSystem
|
||||
from mock import ANY, patch
|
||||
from pyparsing import ParseException
|
||||
|
||||
from capa.tests.helpers import test_capa_system
|
||||
from capa.xqueue_interface import XQUEUE_TIMEOUT
|
||||
from lxml import etree
|
||||
from lxml.html import fromstring
|
||||
from mock import ANY, patch
|
||||
from openedx.core.djangolib.markup import HTML
|
||||
from pyparsing import ParseException
|
||||
|
||||
# just a handy shortcut
|
||||
lookup_tag = inputtypes.registry.get_class_for_tag
|
||||
|
||||
|
||||
DESCRIBEDBY = 'aria-describedby="desc-1 desc-2"'
|
||||
DESCRIBEDBY = HTML('aria-describedby="status_{status_id} desc-1 desc-2"')
|
||||
DESCRIPTIONS = OrderedDict([('desc-1', 'description text 1'), ('desc-2', 'description text 2')])
|
||||
RESPONSE_DATA = {
|
||||
'label': 'question text 101',
|
||||
@@ -67,7 +67,7 @@ class OptionInputTest(unittest.TestCase):
|
||||
option_input = lookup_tag('optioninput')(test_capa_system(), element, state)
|
||||
|
||||
context = option_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'sky_input'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'value': 'Down',
|
||||
@@ -75,10 +75,10 @@ class OptionInputTest(unittest.TestCase):
|
||||
'status': inputtypes.Status('answered'),
|
||||
'msg': '',
|
||||
'inline': False,
|
||||
'id': 'sky_input',
|
||||
'id': prob_id,
|
||||
'default_option_text': 'Select an option',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id='sky_input')
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -147,7 +147,7 @@ class ChoiceGroupTest(unittest.TestCase):
|
||||
'submitted_message': 'Answer received.',
|
||||
'name_array_suffix': expected_suffix, # what is this for??
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id='sky_input')
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -189,10 +189,10 @@ class JavascriptInputTest(unittest.TestCase):
|
||||
the_input = lookup_tag('javascriptinput')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'msg': '',
|
||||
'value': '3',
|
||||
@@ -201,7 +201,7 @@ class JavascriptInputTest(unittest.TestCase):
|
||||
'display_class': display_class,
|
||||
'problem_state': problem_state,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -225,10 +225,10 @@ class TextLineTest(unittest.TestCase):
|
||||
the_input = lookup_tag('textline')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'BumbleBee',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'size': size,
|
||||
@@ -239,7 +239,7 @@ class TextLineTest(unittest.TestCase):
|
||||
'trailing_text': '',
|
||||
'preprocessor': None,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
@@ -261,10 +261,10 @@ class TextLineTest(unittest.TestCase):
|
||||
the_input = lookup_tag('textline')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'BumbleBee',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'size': size,
|
||||
@@ -278,7 +278,7 @@ class TextLineTest(unittest.TestCase):
|
||||
'script_src': script,
|
||||
},
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
@@ -309,10 +309,10 @@ class TextLineTest(unittest.TestCase):
|
||||
the_input = lookup_tag('textline')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'BumbleBee',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'size': size,
|
||||
@@ -323,7 +323,7 @@ class TextLineTest(unittest.TestCase):
|
||||
'trailing_text': expected_text,
|
||||
'preprocessor': None,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
@@ -355,10 +355,10 @@ class FileSubmissionTest(unittest.TestCase):
|
||||
the_input = input_class(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'status': inputtypes.Status('queued'),
|
||||
'msg': the_input.submitted_msg,
|
||||
'value': 'BumbleBee.py',
|
||||
@@ -366,7 +366,7 @@ class FileSubmissionTest(unittest.TestCase):
|
||||
'allowed_files': '["runme.py", "nooooo.rb", "ohai.java"]',
|
||||
'required_files': '["cookies.py"]',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -407,10 +407,10 @@ class CodeInputTest(unittest.TestCase):
|
||||
the_input = input_class(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'print "good evening"',
|
||||
'status': inputtypes.Status('queued'),
|
||||
'msg': the_input.submitted_msg,
|
||||
@@ -424,7 +424,7 @@ class CodeInputTest(unittest.TestCase):
|
||||
'aria_label': '{mode} editor'.format(mode=mode),
|
||||
'code_mirror_exit_message': 'Press ESC then TAB or click outside of the code editor to exit',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -486,7 +486,7 @@ class MatlabTest(unittest.TestCase):
|
||||
'queue_len': '3',
|
||||
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
|
||||
'response_data': {},
|
||||
'describedby_html': ''
|
||||
'describedby_html': HTML('aria-describedby="status_prob_1_2"')
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -503,10 +503,10 @@ class MatlabTest(unittest.TestCase):
|
||||
|
||||
the_input = self.input_class(test_capa_system(), elt, state)
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'print "good evening"',
|
||||
'status': inputtypes.Status('queued'),
|
||||
'msg': the_input.submitted_msg,
|
||||
@@ -521,7 +521,7 @@ class MatlabTest(unittest.TestCase):
|
||||
'queue_len': '3',
|
||||
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -535,12 +535,12 @@ class MatlabTest(unittest.TestCase):
|
||||
'response_data': RESPONSE_DATA
|
||||
}
|
||||
elt = etree.fromstring(self.xml)
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
the_input = self.input_class(test_capa_system(), elt, state)
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'print "good evening"',
|
||||
'status': inputtypes.Status(status),
|
||||
'msg': '',
|
||||
@@ -555,7 +555,7 @@ class MatlabTest(unittest.TestCase):
|
||||
'queue_len': '0',
|
||||
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -569,12 +569,12 @@ class MatlabTest(unittest.TestCase):
|
||||
'response_data': RESPONSE_DATA
|
||||
}
|
||||
elt = etree.fromstring(self.xml)
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
the_input = self.input_class(test_capa_system(), elt, state)
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'print "good evening"',
|
||||
'status': inputtypes.Status('queued'),
|
||||
'msg': the_input.submitted_msg,
|
||||
@@ -589,7 +589,7 @@ class MatlabTest(unittest.TestCase):
|
||||
'queue_len': '1',
|
||||
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -713,7 +713,8 @@ class MatlabTest(unittest.TestCase):
|
||||
\'msg\': u\'Submitted. As soon as a response is returned,
|
||||
this message will be replaced by that feedback.\',
|
||||
\'matlab_editor_js\': \'/dummy-static/js/vendor/CodeMirror/octave.js\',
|
||||
\'hidden\': \'\', \'id\': \'prob_1_2\', \'describedby_html\': \'\',
|
||||
\'hidden\': \'\', \'id\': \'prob_1_2\',
|
||||
\'describedby_html\': Markup(u\'aria-describedby="status_prob_1_2"\'),
|
||||
\'response_data\': {}}</div>
|
||||
""").replace('\n', ' ').strip()
|
||||
)
|
||||
@@ -804,9 +805,10 @@ class MatlabTest(unittest.TestCase):
|
||||
context = self.the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
self.maxDiff = None
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'print "good evening"',
|
||||
'status': inputtypes.Status('queued'),
|
||||
'msg': self.the_input.submitted_msg,
|
||||
@@ -821,7 +823,7 @@ class MatlabTest(unittest.TestCase):
|
||||
'queue_len': '3',
|
||||
'matlab_editor_js': '/dummy-static/js/vendor/CodeMirror/octave.js',
|
||||
'response_data': {},
|
||||
'describedby_html': ''
|
||||
'describedby_html': 'aria-describedby="status_{id}"'.format(id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -917,10 +919,10 @@ class SchematicTest(unittest.TestCase):
|
||||
the_input = lookup_tag('schematic')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': value,
|
||||
'status': inputtypes.Status('unsubmitted'),
|
||||
'msg': '',
|
||||
@@ -932,7 +934,7 @@ class SchematicTest(unittest.TestCase):
|
||||
'analyses': analyses,
|
||||
'submit_analyses': submit_analyses,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -965,10 +967,10 @@ class ImageInputTest(unittest.TestCase):
|
||||
the_input = lookup_tag('imageinput')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': value,
|
||||
'status': inputtypes.Status('unsubmitted'),
|
||||
'width': width,
|
||||
@@ -978,7 +980,7 @@ class ImageInputTest(unittest.TestCase):
|
||||
'gy': egy,
|
||||
'msg': '',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -1024,17 +1026,17 @@ class CrystallographyTest(unittest.TestCase):
|
||||
the_input = lookup_tag('crystallography')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': value,
|
||||
'status': inputtypes.Status('unsubmitted'),
|
||||
'msg': '',
|
||||
'width': width,
|
||||
'height': height,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -1070,10 +1072,10 @@ class VseprTest(unittest.TestCase):
|
||||
the_input = lookup_tag('vsepr_input')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': value,
|
||||
'status': inputtypes.Status('unsubmitted'),
|
||||
'msg': '',
|
||||
@@ -1082,7 +1084,7 @@ class VseprTest(unittest.TestCase):
|
||||
'molecules': molecules,
|
||||
'geometries': geometries,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -1108,17 +1110,17 @@ class ChemicalEquationTest(unittest.TestCase):
|
||||
def test_rendering(self):
|
||||
''' Verify that the render context matches the expected render context'''
|
||||
context = self.the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'H2OYeah',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'msg': '',
|
||||
'size': self.size,
|
||||
'previewer': '/dummy-static/js/capa/chemical_equation_preview.js',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
@@ -1201,10 +1203,10 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
Verify that the render context matches the expected render context
|
||||
"""
|
||||
context = self.the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'x^2+1/2',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'msg': '',
|
||||
@@ -1213,7 +1215,7 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
'inline': False,
|
||||
'trailing_text': '',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
self.assertEqual(context, expected)
|
||||
|
||||
@@ -1247,10 +1249,10 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
the_input = lookup_tag('formulaequationinput')(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': 'x^2+1/2',
|
||||
'status': inputtypes.Status('unanswered'),
|
||||
'msg': '',
|
||||
@@ -1259,7 +1261,7 @@ class FormulaEquationTest(unittest.TestCase):
|
||||
'inline': False,
|
||||
'trailing_text': expected_text,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.assertEqual(context, expected)
|
||||
@@ -1381,17 +1383,17 @@ class DragAndDropTest(unittest.TestCase):
|
||||
}
|
||||
|
||||
the_input = lookup_tag('drag_and_drop_input')(test_capa_system(), element, state)
|
||||
|
||||
prob_id = 'prob_1_2'
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'prob_1_2',
|
||||
'id': prob_id,
|
||||
'value': value,
|
||||
'status': inputtypes.Status('unsubmitted'),
|
||||
'msg': '',
|
||||
'drag_and_drop_json': json.dumps(user_input),
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
# as we are dumping 'draggables' dicts while dumping user_input, string
|
||||
@@ -1437,10 +1439,10 @@ class AnnotationInputTest(unittest.TestCase):
|
||||
the_input = lookup_tag(tag)(test_capa_system(), element, state)
|
||||
|
||||
context = the_input._get_render_context() # pylint: disable=protected-access
|
||||
|
||||
prob_id = 'annotation_input'
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'id': 'annotation_input',
|
||||
'id': prob_id,
|
||||
'status': inputtypes.Status('answered'),
|
||||
'msg': '',
|
||||
'title': 'foo',
|
||||
@@ -1460,7 +1462,7 @@ class AnnotationInputTest(unittest.TestCase):
|
||||
'debug': False,
|
||||
'return_to_annotation': True,
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
|
||||
self.maxDiff = None
|
||||
@@ -1499,9 +1501,10 @@ class TestChoiceText(unittest.TestCase):
|
||||
</{tag}>
|
||||
""".format(tag=tag, choice_tag=choice_tag)
|
||||
element = etree.fromstring(xml_str)
|
||||
prob_id = 'choicetext_input'
|
||||
state = {
|
||||
'value': '{}',
|
||||
'id': 'choicetext_input',
|
||||
'id': prob_id,
|
||||
'status': inputtypes.Status('answered'),
|
||||
'response_data': RESPONSE_DATA
|
||||
}
|
||||
@@ -1516,7 +1519,6 @@ class TestChoiceText(unittest.TestCase):
|
||||
('choiceinput_0', [first_choice_content, first_input]),
|
||||
('choiceinput_1', [second_choice_content, second_input, second_choice_text])
|
||||
]
|
||||
|
||||
expected = {
|
||||
'STATIC_URL': '/dummy-static/',
|
||||
'msg': '',
|
||||
@@ -1525,7 +1527,7 @@ class TestChoiceText(unittest.TestCase):
|
||||
'show_correctness': 'always',
|
||||
'submitted_message': 'Answer received.',
|
||||
'response_data': RESPONSE_DATA,
|
||||
'describedby_html': DESCRIBEDBY
|
||||
'describedby_html': DESCRIBEDBY.format(status_id=prob_id)
|
||||
}
|
||||
expected.update(state)
|
||||
the_input = lookup_tag(tag)(test_capa_system(), element, state)
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
// ====================
|
||||
$annotation-yellow: rgba(255,255,10,0.3);
|
||||
$color-copy-tip: rgb(100,100,100);
|
||||
$correct: $green-d2;
|
||||
$partially-correct: $green-d2;
|
||||
$incorrect: $red;
|
||||
|
||||
// FontAwesome Icon code
|
||||
// ====================
|
||||
@@ -50,11 +47,13 @@ $asterisk-icon: '\f069'; // .fa-asterisk
|
||||
// ====================
|
||||
@mixin status-icon($color: $gray, $fontAwesomeIcon: "\f00d"){
|
||||
|
||||
&:after {
|
||||
@extend %use-font-awesome;
|
||||
color: $color;
|
||||
font-size: 1.2em;
|
||||
content: $fontAwesomeIcon;
|
||||
.status-icon {
|
||||
&:after {
|
||||
@extend %use-font-awesome;
|
||||
color: $color;
|
||||
font-size: 1.2em;
|
||||
content: $fontAwesomeIcon;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -318,6 +317,12 @@ div.problem {
|
||||
@include status-icon($incorrect, $cross-icon);
|
||||
}
|
||||
|
||||
&.unsubmitted,
|
||||
&.unanswered {
|
||||
.status-icon {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -818,12 +823,14 @@ div.problem {
|
||||
}
|
||||
|
||||
.status {
|
||||
&:after {
|
||||
content: ''; // clear out correct or incorrect icon
|
||||
.status-icon {
|
||||
&:after {
|
||||
content: '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.trailing_text {
|
||||
|
||||
@@ -962,7 +962,6 @@
|
||||
$status = $('#status_' + id);
|
||||
if ($status[0]) {
|
||||
$status.removeClass().addClass('unanswered');
|
||||
$status.empty().css('display', 'inline-block');
|
||||
} else {
|
||||
$('<span>', {
|
||||
class: 'unanswered',
|
||||
@@ -979,8 +978,8 @@
|
||||
id = ($select.attr('id').match(/^input_(.*)$/))[1];
|
||||
return $select.on('change', function() {
|
||||
return $('#status_' + id).removeClass().addClass('unanswered')
|
||||
.find('span')
|
||||
.text(gettext('Status: unsubmitted'));
|
||||
.find('.sr')
|
||||
.text(gettext('unsubmitted'));
|
||||
});
|
||||
},
|
||||
textline: function(element) {
|
||||
|
||||
@@ -144,6 +144,13 @@ $success-color: rgb(0, 155, 0) !default;
|
||||
$warning-color: rgb(255, 192, 31) !default;
|
||||
$warning-color-accent: rgb(255, 252, 221) !default;
|
||||
|
||||
|
||||
// CAPA correctness color to be consistent with Alert styles above
|
||||
$correct: $success-color;
|
||||
$partially-correct: $success-color;
|
||||
$incorrect: $error-color;
|
||||
|
||||
|
||||
// BUTTONS
|
||||
|
||||
// disabled button
|
||||
|
||||
@@ -617,12 +617,12 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
|
||||
metadata={'graded': True},
|
||||
display_name='Problem Vertical'
|
||||
)
|
||||
self.define_option_problem(u'Pröblem1', parent=vertical)
|
||||
self.define_option_problem(u'Problem1', parent=vertical)
|
||||
|
||||
self.submit_student_answer(self.student_1.username, u'Pröblem1', ['Option 1'])
|
||||
self.submit_student_answer(self.student_1.username, u'Problem1', ['Option 1'])
|
||||
result = upload_problem_grade_report(None, None, self.course.id, None, 'graded')
|
||||
self.assertDictContainsSubset({'action_name': 'graded', 'attempted': 2, 'succeeded': 2, 'failed': 0}, result)
|
||||
problem_name = u'Homework 1: Problem - Pröblem1'
|
||||
problem_name = u'Homework 1: Problem - Problem1'
|
||||
header_row = self.csv_header_row + [problem_name + ' (Earned)', problem_name + ' (Possible)']
|
||||
self.verify_rows_in_csv([
|
||||
dict(zip(
|
||||
@@ -646,7 +646,7 @@ class TestProblemGradeReport(TestReportMixin, InstructorTaskModuleTestCase):
|
||||
|
||||
@patch('lms.djangoapps.instructor_task.tasks_helper._get_current_task')
|
||||
@patch('lms.djangoapps.instructor_task.tasks_helper.iterate_grades_for')
|
||||
@ddt.data(u'Cannöt grade student', '')
|
||||
@ddt.data(u'Cannot grade student', '')
|
||||
def test_grading_failure(self, error_message, mock_iterate_grades_for, _mock_current_task):
|
||||
"""
|
||||
Test that any grading errors are properly reported in the progress
|
||||
@@ -683,8 +683,8 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
|
||||
|
||||
def setUp(self):
|
||||
super(TestProblemReportSplitTestContent, self).setUp()
|
||||
self.problem_a_url = u'pröblem_a_url'
|
||||
self.problem_b_url = u'pröblem_b_url'
|
||||
self.problem_a_url = u'problem_a_url'
|
||||
self.problem_b_url = u'problem_b_url'
|
||||
self.define_option_problem(self.problem_a_url, parent=self.vertical_a)
|
||||
self.define_option_problem(self.problem_b_url, parent=self.vertical_b)
|
||||
|
||||
@@ -711,7 +711,7 @@ class TestProblemReportSplitTestContent(TestReportMixin, TestConditionalContent,
|
||||
{'action_name': 'graded', 'attempted': 2, 'succeeded': 2, 'failed': 0}, result
|
||||
)
|
||||
|
||||
problem_names = [u'Homework 1: Problem - pröblem_a_url', u'Homework 1: Problem - pröblem_b_url']
|
||||
problem_names = [u'Homework 1: Problem - problem_a_url', u'Homework 1: Problem - problem_b_url']
|
||||
header_row = [u'Student ID', u'Email', u'Username', u'Final Grade']
|
||||
for problem in problem_names:
|
||||
header_row += [problem + ' (Earned)', problem + ' (Possible)']
|
||||
@@ -817,12 +817,12 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
|
||||
display_name='Problem Vertical'
|
||||
)
|
||||
self.define_option_problem(
|
||||
u"Pröblem0",
|
||||
u"Problem0",
|
||||
parent=vertical,
|
||||
group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[0].id]}
|
||||
)
|
||||
self.define_option_problem(
|
||||
u"Pröblem1",
|
||||
u"Problem1",
|
||||
parent=vertical,
|
||||
group_access={self.course.user_partitions[0].id: [self.course.user_partitions[0].groups[1].id]}
|
||||
)
|
||||
@@ -845,20 +845,20 @@ class TestProblemReportCohortedContent(TestReportMixin, ContentGroupTestCase, In
|
||||
))
|
||||
|
||||
def test_cohort_content(self):
|
||||
self.submit_student_answer(self.alpha_user.username, u'Pröblem0', ['Option 1', 'Option 1'])
|
||||
resp = self.submit_student_answer(self.alpha_user.username, u'Pröblem1', ['Option 1', 'Option 1'])
|
||||
self.submit_student_answer(self.alpha_user.username, u'Problem0', ['Option 1', 'Option 1'])
|
||||
resp = self.submit_student_answer(self.alpha_user.username, u'Problem1', ['Option 1', 'Option 1'])
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
|
||||
resp = self.submit_student_answer(self.beta_user.username, u'Pröblem0', ['Option 1', 'Option 2'])
|
||||
resp = self.submit_student_answer(self.beta_user.username, u'Problem0', ['Option 1', 'Option 2'])
|
||||
self.assertEqual(resp.status_code, 404)
|
||||
self.submit_student_answer(self.beta_user.username, u'Pröblem1', ['Option 1', 'Option 2'])
|
||||
self.submit_student_answer(self.beta_user.username, u'Problem1', ['Option 1', 'Option 2'])
|
||||
|
||||
with patch('lms.djangoapps.instructor_task.tasks_helper._get_current_task'):
|
||||
result = upload_problem_grade_report(None, None, self.course.id, None, 'graded')
|
||||
self.assertDictContainsSubset(
|
||||
{'action_name': 'graded', 'attempted': 4, 'succeeded': 4, 'failed': 0}, result
|
||||
)
|
||||
problem_names = [u'Homework 1: Problem - Pröblem0', u'Homework 1: Problem - Pröblem1']
|
||||
problem_names = [u'Homework 1: Problem - Problem0', u'Homework 1: Problem - Problem1']
|
||||
header_row = [u'Student ID', u'Email', u'Username', u'Final Grade']
|
||||
for problem in problem_names:
|
||||
header_row += [problem + ' (Earned)', problem + ' (Possible)']
|
||||
|
||||
Reference in New Issue
Block a user