Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
P
problem-builder
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
OpenEdx
problem-builder
Commits
0931ca2c
Commit
0931ca2c
authored
Nov 07, 2017
by
Uman Shahzad
Committed by
Braden MacDonald
Nov 10, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Fix Swipe component to match desired specs and API behavior
parent
1dc9578a
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
68 additions
and
131 deletions
+68
-131
doc/Native APIs.md
+15
-26
problem_builder/public/css/problem-builder.css
+0
-4
problem_builder/swipe.py
+52
-67
problem_builder/templates/html/swipeblock.html
+0
-33
setup.py
+1
-1
No files found.
doc/Native APIs.md
View file @
0931ca2c
...
...
@@ -371,40 +371,29 @@ Swipeable Binary Response (`pb-swipe`)
-
`block_id`
: (string) The XBlock's usage ID
-
`display_name`
: (string) The XBlock's display name
-
`type`
: (string): The XBlock's identifier, "pb-swipe"
-
`question`
: (string) The question contents
-
`message`
: (string) Feedback provided when submitting
-
`img_url`
: (string) URL to an associated image
-
`weight`
: (float) Overall value of the question
-
`choices`
: (array) A list of objects providing info about available
choices. See below for more info.
-
`tips`
: (array) A list of objects providing info about tips defined for the
problem. See below for more info.
#### `tips`
Each entry in the
`tips`
array contains these values:
-
`content`
: (string) The text content of the tip.
-
`for_choices`
: (array) A list of string values corresponding to choices to
which this tip applies to.
#### `choices`
Each item in the
`choices`
array contains these fields:
-
`value`
: (string) The value of the choice.
-
`content`
: (string) The description of the choice
-
`text`
: (string) The text to display on the card
-
`img_url`
: (string) URL of the image to display as the background of the card
-
`correct`
: (boolean) Whether the student's swipe is correct or not
### `student_view_user_state`
-
`student_choice`
: (
string
) The value of the last submitted choice.
-
`student_choice`
: (
boolean
) The value of the last submitted choice.
### POST Submit Data
When submitting the problem the data should be a single object containing the
`"value"`
property which has the value of the selected choice.
Example:
`{"value": "blue"}`
`"value"`
property which has a boolean value indicating which swipe the student took:
-
Right -> True
-
Left -> False
Example:
`{"value": true}`
if the student made a
*rightwards*
swipe.
The returned result will contain the following:
-
`submission`
: (boolean) The value just POST'd.
-
`status`
: (string)
`"correct"`
if the swipe is correct,
`"incorrect"`
otherwise.
-
`score`
: (integer) 1 if the swipe is correct, otherwise 0.
Multiple Response Question (`pb-mrq`)
-------------------------------------
...
...
problem_builder/public/css/problem-builder.css
View file @
0931ca2c
...
...
@@ -260,7 +260,3 @@
.mentoring
.copyright
a
{
color
:
#69C0E8
;
}
.swipe-img
{
max-width
:
100%
;
}
problem_builder/swipe.py
View file @
0931ca2c
...
...
@@ -19,18 +19,20 @@
#
# Imports ###########################################################
import
logging
from
xblock.fields
import
Scope
,
String
from
xblock.validation
import
ValidationMessage
from
xblock.core
import
XBlock
from
xblock.fields
import
Boolean
,
Scope
,
String
from
xblock.fragment
import
Fragment
from
xblockutils.resources
import
ResourceLoader
from
xblockutils.studio_editable
import
StudioEditableXBlockMixin
,
XBlockWithPreviewMixin
from
.mixins
import
StudentViewUserStateMixin
from
.questionnaire
import
QuestionnaireAbstractBlock
from
.sub_api
import
SubmittingXBlockMixin
from
.mixins
import
QuestionMixin
,
StudentViewUserStateMixin
# Globals ###########################################################
log
=
logging
.
getLogger
(
__name__
)
loader
=
ResourceLoader
(
__name__
)
...
...
@@ -41,53 +43,51 @@ def _(text):
# Classes ###########################################################
class
SwipeBlock
(
SubmittingXBlockMixin
,
StudentViewUserStateMixin
,
QuestionnaireAbstractBlock
):
@XBlock.needs
(
"i18n"
)
class
SwipeBlock
(
QuestionMixin
,
StudioEditableXBlockMixin
,
StudentViewUserStateMixin
,
XBlockWithPreviewMixin
,
XBlock
,
):
"""
An XBlock used to ask binary-choice questions with a swiping interface
"""
CATEGORY
=
'pb-swipe'
STUDIO_LABEL
=
_
(
u"Swipeable Binary Choice Question"
)
USER_STATE_FIELDS
=
[
'num_attempts'
,
'student_choice'
]
message
=
String
(
display_name
=
_
(
"Message"
),
help
=
_
(
"General feedback provided when submitting. "
"(This is not shown if there is a more specific feedback tip for the choice selected by the learner.)"
),
scope
=
Scope
.
content
,
default
=
""
)
USER_STATE_FIELDS
=
[
'student_choice'
]
student_choice
=
String
(
# {Last input submitted by the student
text
=
String
(
display_name
=
_
(
"Text"
),
help
=
_
(
"Text to display on this card. The student must determine if this statement is true or false."
),
scope
=
Scope
.
content
,
default
=
""
,
scope
=
Scope
.
user_stat
e
,
multiline_editor
=
Tru
e
,
)
correct_choice
=
String
(
display_name
=
_
(
"Correct Choice"
),
help
=
_
(
"Specify the value that students may select for this question to be considered correct."
),
scope
=
Scope
.
content
,
values_provider
=
QuestionnaireAbstractBlock
.
choice_values_provider
,
)
img_url
=
String
(
display_name
=
_
(
"Image"
),
help
=
_
(
"Specify the URL of an image associated with this question."
),
help
=
_
(
"Specify the URL of an image associated with this question."
),
scope
=
Scope
.
content
,
default
=
""
)
editable_fields
=
QuestionnaireAbstractBlock
.
editable_fields
+
(
'message'
,
'correct_choice'
,
'img_url'
,)
correct
=
Boolean
(
display_name
=
_
(
"Correct Choice"
),
help
=
_
(
"Specifies whether the card is correct."
),
scope
=
Scope
.
content
,
)
student_choice
=
Boolean
(
scope
=
Scope
.
user_state
,
help
=
_
(
"Last input submitted by the student."
)
)
editable_fields
=
(
'display_name'
,
'text'
,
'img_url'
,
'correct'
)
def
calculate_results
(
self
,
submission
):
correct
=
s
elf
.
correct_choice
==
submission
correct
=
s
ubmission
==
self
.
correct
return
{
'submission'
:
submission
,
'message'
:
self
.
message_formatted
,
'status'
:
'correct'
if
correct
else
'incorrect'
,
'weight'
:
self
.
weight
,
'score'
:
1
if
correct
else
0
,
}
...
...
@@ -99,33 +99,12 @@ class SwipeBlock(SubmittingXBlockMixin, StudentViewUserStateMixin, Questionnaire
def
submit
(
self
,
submission
):
log
.
debug
(
u'Received Swipe submission: "
%
s"'
,
submission
)
result
=
self
.
calculate_results
(
submission
[
'value'
]
)
# We expect to receive a boolean indicating the swipe the student made (left -> false, right -> true
)
self
.
student_choice
=
submission
[
'value'
]
result
=
self
.
calculate_results
(
self
.
student_choice
)
log
.
debug
(
u'Swipe submission result:
%
s'
,
result
)
return
result
def
validate_field_data
(
self
,
validation
,
data
):
"""
Validate this block's field data.
"""
super
(
SwipeBlock
,
self
)
.
validate_field_data
(
validation
,
data
)
def
add_error
(
msg
):
validation
.
add
(
ValidationMessage
(
ValidationMessage
.
ERROR
,
msg
))
if
len
(
self
.
all_choice_values
)
==
0
:
# Let's not set an error until at least one choice is added
return
if
len
(
self
.
all_choice_values
)
!=
2
:
add_error
(
self
.
_
(
u"You must have exactly two choices."
)
)
if
not
data
.
correct_choice
:
add_error
(
self
.
_
(
u"You must indicate the correct answer, or the student will always get this question wrong."
)
)
def
student_view_data
(
self
,
context
=
None
):
"""
Returns a JSON representation of the student_view of this XBlock,
...
...
@@ -136,15 +115,9 @@ class SwipeBlock(SubmittingXBlockMixin, StudentViewUserStateMixin, Questionnaire
'block_id'
:
unicode
(
self
.
scope_ids
.
usage_id
),
'display_name'
:
self
.
display_name_with_default
,
'type'
:
self
.
CATEGORY
,
'question'
:
self
.
question
,
'message'
:
self
.
message
,
'text'
:
self
.
text
,
'img_url'
:
self
.
expand_static_url
(
self
.
img_url
),
'choices'
:
[
{
'value'
:
choice
[
'value'
],
'content'
:
choice
[
'display_name'
]}
for
choice
in
self
.
human_readable_choices
],
'weight'
:
self
.
weight
,
'tips'
:
[
tip
.
student_view_data
()
for
tip
in
self
.
get_tips
()],
'correct'
:
self
.
correct
,
}
def
expand_static_url
(
self
,
url
):
...
...
@@ -166,6 +139,18 @@ class SwipeBlock(SubmittingXBlockMixin, StudentViewUserStateMixin, Questionnaire
pass
return
url
@property
def
expanded_img_url
(
self
):
return
self
.
expand_static_url
(
self
.
img_url
)
def
mentoring_view
(
self
,
context
=
None
):
""" Render the swipe image, text & whether it's correct within a mentoring block question. """
return
Fragment
(
(
u'<img src="{img_url}" style="max-width: 100
%
;" />'
u'<p class="swipe-text">"{text}"</p>'
)
.
format
(
img_url
=
self
.
expand_static_url
(
self
.
img_url
),
text
=
self
.
text
,
)
)
def
student_view
(
self
,
context
=
None
):
""" Normal view of this XBlock, identical to mentoring_view """
return
self
.
mentoring_view
(
context
)
problem_builder/templates/html/swipeblock.html
deleted
100644 → 0
View file @
1dc9578a
{% load i18n %}
{% if not hide_header %}
<h4
class=
"question-title"
id=
"heading_{{ self.html_id }}"
>
{{ self.display_name_with_default }}
</h4>
{% endif %}
{% if self.img_url.strip %}
<img
class=
"swipe-img"
src=
"{{ self.expanded_img_url }}"
alt=
""
/>
{% endif %}
<fieldset
class=
"choices questionnaire"
id=
"{{ self.html_id }}"
>
<legend
class=
"question field-group-hd"
>
{{ self.question|safe }}
</legend>
<div
class=
"choices-list"
>
{% for choice in custom_choices %}
<div
class=
"choice"
aria-live=
"polite"
aria-atomic=
"true"
>
<label
class=
"choice-label"
aria-describedby=
"feedback_{{ self.html_id }} choice_tips_{{ self.html_id }}-{{ forloop.counter }}"
>
<span
class=
"choice-result fa icon-2x"
aria-label=
""
data-label_correct=
"{% trans "
Correct
"
%}"
data-label_incorrect=
"{% trans "
Incorrect
"
%}"
></span>
<span
class=
"choice-selector"
>
<input
type=
"radio"
name=
"{{ self.name }}"
value=
"{{ choice.value }}"
{%
if
self
.
student_choice =
=
choice
.
value
and
not
hide_prev_answer
%}
checked
{%
endif
%}
/>
</span>
<span
class=
"choice-label-text"
>
{{ choice.content|safe }}
</span>
</label>
<div
class=
"choice-tips-container"
>
<div
class=
"choice-tips"
id=
"choice_tips_{{ self.html_id }}-{{ forloop.counter }}"
></div>
</div>
</div>
{% endfor %}
<div
class=
"feedback"
id=
"feedback_{{ self.html_id }}"
></div>
</div>
</fieldset>
setup.py
View file @
0931ca2c
...
...
@@ -72,7 +72,7 @@ BLOCKS = [
setup
(
name
=
'xblock-problem-builder'
,
version
=
'2.7.
9
'
,
version
=
'2.7.
10
'
,
description
=
'XBlock - Problem Builder'
,
packages
=
find_packages
(),
install_requires
=
[
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment