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
fbf23f9e
Commit
fbf23f9e
authored
Oct 16, 2015
by
Tim Krones
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Clean up.
parent
dd04ee8c
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
62 additions
and
83 deletions
+62
-83
problem_builder/plot.py
+35
-58
problem_builder/public/js/plot.js
+27
-25
No files found.
problem_builder/plot.py
View file @
fbf23f9e
...
@@ -21,6 +21,7 @@
...
@@ -21,6 +21,7 @@
import
json
import
json
import
logging
import
logging
from
lazy.lazy
import
lazy
from
opaque_keys.edx.keys
import
CourseKey
from
opaque_keys.edx.keys
import
CourseKey
from
xblock.core
import
XBlock
from
xblock.core
import
XBlock
...
@@ -126,7 +127,7 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -126,7 +127,7 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
help
=
_
(
help
=
_
(
'Claims and questions that should be included in the plot. '
'Claims and questions that should be included in the plot. '
'Each line defines a triple of the form "claim, q1, q2", '
'Each line defines a triple of the form "claim, q1, q2", '
'where "claim" is arbitrary text that represents a claim
(must be quoted using double-quotes)
, '
'where "claim" is arbitrary text that represents a claim, '
'and "q1" and "q2" are IDs of scale questions. '
'and "q1" and "q2" are IDs of scale questions. '
),
),
default
=
""
,
default
=
""
,
...
@@ -139,19 +140,26 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -139,19 +140,26 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
'q1_label'
,
'q2_label'
,
'q3_label'
,
'q4_label'
,
'claims'
'q1_label'
,
'q2_label'
,
'q3_label'
,
'q4_label'
,
'claims'
)
)
@lazy
def
course_id
(
self
):
return
unicode
(
getattr
(
self
.
runtime
,
'course_id'
,
'course_id'
))
@lazy
def
course_key_str
(
self
):
course_key
=
CourseKey
.
from_string
(
self
.
course_id
)
return
unicode
(
course_key
)
@property
@property
def
default_claims
(
self
):
def
default_claims
(
self
):
if
not
self
.
claims
:
return
self
.
_get_claims
(
self
.
_get_default_response
)
return
[]
course_id
=
unicode
(
getattr
(
self
.
runtime
,
'course_id'
,
'course_id'
))
@property
course_key
=
CourseKey
.
from_string
(
course_id
)
def
average_claims
(
self
):
course_key_str
=
unicode
(
course_key
)
return
self
.
_get_claims
(
self
.
_get_average_response
)
user_service
=
self
.
runtime
.
service
(
self
,
'user'
)
def
_get_claims
(
self
,
response_function
):
user
=
user_service
.
get_current_user
()
if
not
self
.
claims
:
username
=
user
.
opt_attrs
.
get
(
'edx-platform.username'
)
return
[]
anonymous_user_id
=
user_service
.
get_anonymous_user_id
(
username
,
course_id
)
mentoring_block
=
self
.
get_parent
()
.
get_parent
()
mentoring_block
=
self
.
get_parent
()
.
get_parent
()
question_ids
,
questions
=
mentoring_block
.
question_ids
,
mentoring_block
.
questions
question_ids
,
questions
=
mentoring_block
.
question_ids
,
mentoring_block
.
questions
...
@@ -161,66 +169,45 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -161,66 +169,45 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
r1
,
r2
=
None
,
None
r1
,
r2
=
None
,
None
for
question_id
,
question
in
zip
(
question_ids
,
questions
):
for
question_id
,
question
in
zip
(
question_ids
,
questions
):
if
question
.
name
==
q1
:
if
question
.
name
==
q1
:
r1
=
self
.
_default_response
(
course_key_str
,
question
,
question_id
,
anonymous_user
_id
)
r1
=
response_function
(
self
.
course_key_str
,
question
,
question
_id
)
if
question
.
name
==
q2
:
if
question
.
name
==
q2
:
r2
=
self
.
_default_response
(
course_key_str
,
question
,
question_id
,
anonymous_user
_id
)
r2
=
response_function
(
self
.
course_key_str
,
question
,
question
_id
)
if
r1
is
not
None
and
r2
is
not
None
:
if
r1
is
not
None
and
r2
is
not
None
:
break
break
claims
.
append
([
claim
,
r1
,
r2
])
claims
.
append
([
claim
,
r1
,
r2
])
return
claims
return
claims
def
_
default_response
(
self
,
course_key_str
,
question
,
question_id
,
user
_id
):
def
_
get_default_response
(
self
,
course_key_str
,
question
,
question
_id
):
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
# 1. Obtain block_type for question
# 1. Obtain ID of current user that can be used to look up submissions
user_service
=
self
.
runtime
.
service
(
self
,
'user'
)
user
=
user_service
.
get_current_user
()
username
=
user
.
opt_attrs
.
get
(
'edx-platform.username'
)
anonymous_user_id
=
user_service
.
get_anonymous_user_id
(
username
,
self
.
course_id
)
# 2. Obtain block_type for question
question_type
=
question
.
scope_ids
.
block_type
question_type
=
question
.
scope_ids
.
block_type
#
2. Obtain submissions for question using course_key_str, block_id, block_type, user_id
#
3. Obtain latest submission for question
student_dict
=
{
student_dict
=
{
'student_id'
:
user_id
,
'student_id'
:
anonymous_
user_id
,
'course_id'
:
course_key_str
,
'course_id'
:
course_key_str
,
'item_id'
:
question_id
,
'item_id'
:
question_id
,
'item_type'
:
question_type
,
'item_type'
:
question_type
,
}
}
submissions
=
sub_api
.
get_submissions
(
student_dict
,
limit
=
1
)
# Gets latest submission
submissions
=
sub_api
.
get_submissions
(
student_dict
,
limit
=
1
)
#
3
. Extract response from latest submission for question
#
4
. Extract response from latest submission for question
answer_cache
=
{}
answer_cache
=
{}
for
submission
in
submissions
:
for
submission
in
submissions
:
answer
=
_get_answer
(
question
,
submission
,
answer_cache
)
answer
=
_get_answer
(
question
,
submission
,
answer_cache
)
return
int
(
answer
)
return
int
(
answer
)
@property
def
_get_average_response
(
self
,
course_key_str
,
question
,
question_id
):
def
average_claims
(
self
):
if
not
self
.
claims
:
return
[]
course_id
=
unicode
(
getattr
(
self
.
runtime
,
'course_id'
,
'course_id'
))
course_key
=
CourseKey
.
from_string
(
course_id
)
course_key_str
=
unicode
(
course_key
)
mentoring_block
=
self
.
get_parent
()
.
get_parent
()
question_ids
,
questions
=
mentoring_block
.
question_ids
,
mentoring_block
.
questions
claims
=
[]
for
line
in
self
.
claims
.
split
(
'
\n
'
):
claim
,
q1
,
q2
=
line
.
split
(
', '
)
r1
,
r2
=
None
,
None
for
question_id
,
question
in
zip
(
question_ids
,
questions
):
if
question
.
name
==
q1
:
r1
=
self
.
_average_response
(
course_key_str
,
question
,
question_id
)
if
question
.
name
==
q2
:
r2
=
self
.
_average_response
(
course_key_str
,
question
,
question_id
)
if
r1
is
not
None
and
r2
is
not
None
:
break
claims
.
append
([
claim
,
r1
,
r2
])
return
claims
def
_average_response
(
self
,
course_key_str
,
question
,
question_id
):
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
from
.tasks
import
_get_answer
# Import here to avoid circular dependency
# 1. Obtain block_type for question
# 1. Obtain block_type for question
question_type
=
question
.
scope_ids
.
block_type
question_type
=
question
.
scope_ids
.
block_type
# 2. Obtain
submissions for question using course_key_str, block_id, block_type
# 2. Obtain
latest submissions for question
submissions
=
sub_api
.
get_all_submissions
(
course_key_str
,
question_id
,
question_type
)
# Gets latest submissions
submissions
=
sub_api
.
get_all_submissions
(
course_key_str
,
question_id
,
question_type
)
# 3. Extract responses from submissions for question and sum them up
# 3. Extract responses from
latest
submissions for question and sum them up
answer_cache
=
{}
answer_cache
=
{}
response_total
=
0
response_total
=
0
num_submissions
=
0
# Can't use len(submissions) because submissions is a generator
num_submissions
=
0
# Can't use len(submissions) because submissions is a generator
...
@@ -245,16 +232,6 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
...
@@ -245,16 +232,6 @@ class PlotBlock(StudioEditableXBlockMixin, StudioContainerWithNestedXBlocksMixin
'average_claims'
:
self
.
average_claims
,
'average_claims'
:
self
.
average_claims
,
}
}
def
clean_studio_edits
(
self
,
data
):
# FIXME: Use this to clean data.claims (remove leading/trailing whitespace, etc.)
pass
def
validate_field_data
(
self
,
validation
,
data
):
# FIXME: Use this to validate data.claims:
# - Each line should be of the form "claim, q1, q2" (no quotes)
# - Entries for "claim", "q1", "q2" must point to existing blocks
pass
def
author_preview_view
(
self
,
context
):
def
author_preview_view
(
self
,
context
):
return
Fragment
(
return
Fragment
(
u"<p>{}</p>"
.
format
(
u"<p>{}</p>"
.
format
(
...
...
problem_builder/public/js/plot.js
View file @
fbf23f9e
function
PlotBlock
(
runtime
,
element
)
{
function
PlotBlock
(
runtime
,
element
)
{
// Plot
// Define margins
// Define margins
var
margins
=
{
top
:
20
,
right
:
20
,
bottom
:
20
,
left
:
20
};
var
margins
=
{
top
:
20
,
right
:
20
,
bottom
:
20
,
left
:
20
};
...
@@ -52,28 +54,31 @@ function PlotBlock(runtime, element) {
...
@@ -52,28 +54,31 @@ function PlotBlock(runtime, element) {
.
attr
(
"transform"
,
"translate("
+
plotWidth
/
2
+
", 0)"
)
.
attr
(
"transform"
,
"translate("
+
plotWidth
/
2
+
", 0)"
)
.
call
(
yAxis
);
.
call
(
yAxis
);
// Buttons
var
defaultButton
=
$
(
'.plot-default'
,
element
);
var
averageButton
=
$
(
'.plot-average'
,
element
);
var
quadrantsButton
=
$
(
'.plot-quadrants'
,
element
);
// Claims
// Claims
var
defaultClaims
=
$
(
'.plot-default'
,
element
)
.
data
(
'claims'
);
var
defaultClaims
=
defaultButton
.
data
(
'claims'
);
var
averageClaims
=
$
(
'.plot-average'
,
element
)
.
data
(
'claims'
);
var
averageClaims
=
averageButton
.
data
(
'claims'
);
// Colors
// Colors
var
defaultColor
=
$
(
'.plot-default'
,
element
)
.
data
(
'point-color'
);
var
defaultColor
=
defaultButton
.
data
(
'point-color'
);
var
averageColor
=
$
(
'.plot-average'
,
element
)
.
data
(
'point-color'
);
var
averageColor
=
averageButton
.
data
(
'point-color'
);
// Quadrant labels
// Quadrant labels
var
q1Label
=
$
(
'.plot-quadrants'
,
element
)
.
data
(
'q1-label'
);
var
q1Label
=
quadrantsButton
.
data
(
'q1-label'
);
var
q2Label
=
$
(
'.plot-quadrants'
,
element
)
.
data
(
'q2-label'
);
var
q2Label
=
quadrantsButton
.
data
(
'q2-label'
);
var
q3Label
=
$
(
'.plot-quadrants'
,
element
)
.
data
(
'q3-label'
);
var
q3Label
=
quadrantsButton
.
data
(
'q3-label'
);
var
q4Label
=
$
(
'.plot-quadrants'
,
element
)
.
data
(
'q4-label'
);
var
q4Label
=
quadrantsButton
.
data
(
'q4-label'
);
// Event handlers
// Event handlers
var
defaultButton
=
$
(
'.plot-default'
,
element
);
var
averageButton
=
$
(
'.plot-average'
,
element
);
function
toggleOverlay
(
claims
,
color
,
klass
,
refresh
)
{
function
toggleOverlay
(
claims
,
color
,
klass
,
refresh
)
{
var
selector
=
"."
+
klass
;
var
selector
=
"."
+
klass
;
var
selection
=
svgContainer
.
selectAll
(
selector
);
var
selection
=
svgContainer
.
selectAll
(
selector
);
...
@@ -122,18 +127,6 @@ function PlotBlock(runtime, element) {
...
@@ -122,18 +127,6 @@ function PlotBlock(runtime, element) {
}
}
}
}
defaultButton
.
on
(
'click'
,
function
(
event
,
refresh
)
{
toggleOverlay
(
defaultClaims
,
defaultColor
,
'claim-default'
,
refresh
);
toggleBorderColor
(
this
,
defaultColor
,
refresh
);
});
averageButton
.
on
(
'click'
,
function
(
event
)
{
toggleOverlay
(
averageClaims
,
averageColor
,
'claim-average'
);
toggleBorderColor
(
this
,
averageColor
);
});
var
quadrantsButton
=
$
(
'.plot-quadrants'
,
element
);
function
toggleQuadrantLabels
()
{
function
toggleQuadrantLabels
()
{
var
selection
=
svgContainer
.
selectAll
(
".quadrant-label"
);
var
selection
=
svgContainer
.
selectAll
(
".quadrant-label"
);
var
quadrantLabelsOn
=
quadrantsButton
.
val
()
===
'On'
;
var
quadrantLabelsOn
=
quadrantsButton
.
val
()
===
'On'
;
...
@@ -170,15 +163,24 @@ function PlotBlock(runtime, element) {
...
@@ -170,15 +163,24 @@ function PlotBlock(runtime, element) {
}
}
}
}
defaultButton
.
on
(
'click'
,
function
(
event
,
refresh
)
{
toggleOverlay
(
defaultClaims
,
defaultColor
,
'claim-default'
,
refresh
);
toggleBorderColor
(
this
,
defaultColor
,
refresh
);
});
averageButton
.
on
(
'click'
,
function
(
event
)
{
toggleOverlay
(
averageClaims
,
averageColor
,
'claim-average'
);
toggleBorderColor
(
this
,
averageColor
);
});
quadrantsButton
.
on
(
'click'
,
function
()
{
quadrantsButton
.
on
(
'click'
,
function
()
{
toggleQuadrantLabels
();
toggleQuadrantLabels
();
});
});
// Quadrant labels are off initially; color of button for toggling them should reflect this
// Quadrant labels are off initially; color of button for toggling them should reflect this
quadrantsButton
.
css
(
"border-color"
,
"red"
);
quadrantsButton
.
css
(
"border-color"
,
"red"
);
//
Functions that can be called from the outside
//
API
var
dataXHR
;
var
dataXHR
;
...
...
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