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