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
2d6f8a3f
Commit
2d6f8a3f
authored
Jul 14, 2015
by
Tim Krones
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement client-server communication for pagination.
parent
cd69698b
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
92 additions
and
24 deletions
+92
-24
problem_builder/instructor_tool.py
+26
-1
problem_builder/public/js/instructor_tool.js
+63
-20
problem_builder/tests/integration/test_instructor_tool.py
+3
-3
No files found.
problem_builder/instructor_tool.py
View file @
2d6f8a3f
...
...
@@ -23,14 +23,16 @@ Instructor Tool: An XBlock for instructors to export student answers from a cour
All processing is done offline.
"""
import
json
from
django.core.paginator
import
Paginator
from
xblock.core
import
XBlock
from
xblock.exceptions
import
JsonHandlerError
from
xblock.fields
import
Scope
,
String
,
Dict
from
xblock.fields
import
Scope
,
String
,
Dict
,
List
from
xblock.fragment
import
Fragment
from
xblockutils.resources
import
ResourceLoader
loader
=
ResourceLoader
(
__name__
)
PAGE_SIZE
=
15
# Make '_' a no-op so we can scrape strings
def
_
(
text
):
...
...
@@ -63,6 +65,12 @@ class InstructorToolBlock(XBlock):
default
=
None
,
scope
=
Scope
.
user_state
,
)
display_data
=
List
(
# The list of results associated with the most recent successful export.
# Stored separately to avoid the overhead of sending it to the client.
default
=
None
,
scope
=
Scope
.
user_state
,
)
has_author_view
=
True
@property
...
...
@@ -90,11 +98,26 @@ class InstructorToolBlock(XBlock):
self
.
active_export_task_id
=
''
if
task_result
.
successful
():
if
isinstance
(
task_result
.
result
,
dict
)
and
not
task_result
.
result
.
get
(
'error'
):
self
.
display_data
=
task_result
.
result
[
'display_data'
]
del
task_result
.
result
[
'display_data'
]
self
.
last_export_result
=
task_result
.
result
else
:
self
.
last_export_result
=
{
'error'
:
u'Unexpected result: {}'
.
format
(
repr
(
task_result
.
result
))}
self
.
display_data
=
None
else
:
self
.
last_export_result
=
{
'error'
:
unicode
(
task_result
.
result
)}
self
.
display_data
=
None
@XBlock.json_handler
def
get_result_page
(
self
,
data
,
suffix
=
''
):
""" Return requested page of `last_export_result`. """
paginator
=
Paginator
(
self
.
display_data
,
PAGE_SIZE
)
page
=
data
.
get
(
'page'
,
None
)
return
{
'display_data'
:
paginator
.
page
(
page
)
.
object_list
,
'num_results'
:
len
(
self
.
display_data
),
'page_size'
:
PAGE_SIZE
}
def
student_view
(
self
,
context
=
None
):
""" Normal View """
...
...
@@ -144,6 +167,7 @@ class InstructorToolBlock(XBlock):
self
.
last_export_result
=
{
'error'
:
message
,
}
self
.
display_data
=
None
raise
JsonHandlerError
(
code
,
message
)
@XBlock.json_handler
...
...
@@ -157,6 +181,7 @@ class InstructorToolBlock(XBlock):
def
_delete_export
(
self
):
self
.
last_export_result
=
None
self
.
display_data
=
None
self
.
active_export_task_id
=
''
@XBlock.json_handler
...
...
problem_builder/public/js/instructor_tool.js
View file @
2d6f8a3f
...
...
@@ -18,12 +18,57 @@ function InstructorToolBlock(runtime, element) {
model
:
Result
,
getCurrentPage
:
function
(
returnObject
)
{
var
currentPage
=
this
.
state
.
currentPage
;
if
(
returnObject
)
{
return
this
.
getPage
(
currentPage
);
state
:
{
order
:
0
},
url
:
runtime
.
handlerUrl
(
element
,
'get_result_page'
),
parseState
:
function
(
response
)
{
return
{
totalRecords
:
response
.
num_results
,
pageSize
:
response
.
page_size
};
},
parseRecords
:
function
(
response
)
{
return
_
.
map
(
response
.
display_data
,
function
(
row
)
{
return
new
Result
(
null
,
{
values
:
row
});
});
},
fetchOptions
:
{
reset
:
true
,
type
:
'POST'
,
contentType
:
'application/json'
,
processData
:
false
,
beforeSend
:
function
(
jqXHR
,
options
)
{
options
.
data
=
JSON
.
stringify
(
options
.
data
);
}
return
currentPage
;
},
getFirstPage
:
function
()
{
Backbone
.
PageableCollection
.
prototype
.
getFirstPage
.
call
(
this
,
this
.
fetchOptions
);
},
getPreviousPage
:
function
()
{
Backbone
.
PageableCollection
.
prototype
.
getPreviousPage
.
call
(
this
,
this
.
fetchOptions
);
},
getNextPage
:
function
()
{
Backbone
.
PageableCollection
.
prototype
.
getNextPage
.
call
(
this
,
this
.
fetchOptions
);
},
getLastPage
:
function
()
{
Backbone
.
PageableCollection
.
prototype
.
getLastPage
.
call
(
this
,
this
.
fetchOptions
);
},
getCurrentPage
:
function
()
{
return
this
.
state
.
currentPage
;
},
getTotalPages
:
function
()
{
...
...
@@ -34,17 +79,21 @@ function InstructorToolBlock(runtime, element) {
var
ResultsView
=
Backbone
.
View
.
extend
({
initialize
:
function
()
{
this
.
listenTo
(
this
.
collection
,
'reset'
,
this
.
render
);
},
render
:
function
()
{
this
.
_insertRecords
(
this
.
collection
.
getCurrentPage
(
true
)
);
this
.
_insertRecords
();
this
.
_updateControls
();
this
.
$
(
'#total-pages'
).
text
(
this
.
collection
.
getTotalPages
()
||
0
);
return
this
;
},
_insertRecords
:
function
(
records
)
{
_insertRecords
:
function
()
{
var
tbody
=
this
.
$
(
'tbody'
);
tbody
.
empty
();
records
.
each
(
function
(
result
,
index
)
{
this
.
collection
.
each
(
function
(
result
,
index
)
{
var
row
=
$
(
'<tr>'
);
_
.
each
(
Result
.
properties
,
function
(
name
)
{
row
.
append
(
$
(
'<td>'
).
text
(
result
.
get
(
name
)));
...
...
@@ -66,26 +115,26 @@ function InstructorToolBlock(runtime, element) {
},
_firstPage
:
function
()
{
this
.
_insertRecords
(
this
.
collection
.
getFirstPage
()
);
this
.
collection
.
getFirstPage
(
);
this
.
_updateControls
();
},
_prevPage
:
function
()
{
if
(
this
.
collection
.
hasPreviousPage
())
{
this
.
_insertRecords
(
this
.
collection
.
getPreviousPage
()
);
this
.
collection
.
getPreviousPage
(
);
}
this
.
_updateControls
();
},
_nextPage
:
function
()
{
if
(
this
.
collection
.
hasNextPage
())
{
this
.
_insertRecords
(
this
.
collection
.
getNextPage
()
);
this
.
collection
.
getNextPage
(
);
}
this
.
_updateControls
();
},
_lastPage
:
function
()
{
this
.
_insertRecords
(
this
.
collection
.
getLastPage
()
);
this
.
collection
.
getLastPage
(
);
this
.
_updateControls
();
},
...
...
@@ -107,7 +156,7 @@ function InstructorToolBlock(runtime, element) {
});
var
resultsView
=
new
ResultsView
({
collection
:
new
Results
([]
,
{
mode
:
"client"
,
state
:
{
pageSize
:
15
}
}
),
collection
:
new
Results
([]),
el
:
$element
.
find
(
'#results'
)
});
...
...
@@ -207,13 +256,7 @@ function InstructorToolBlock(runtime, element) {
)
));
// Display results
var
results
=
_
.
map
(
status
.
last_export_result
.
display_data
,
function
(
row
)
{
return
new
Result
(
null
,
{
values
:
row
});
});
resultsView
.
collection
.
fullCollection
.
reset
(
results
);
resultsView
.
render
();
resultsView
.
collection
.
getFirstPage
();
showResults
();
}
...
...
problem_builder/tests/integration/test_instructor_tool.py
View file @
2d6f8a3f
...
...
@@ -7,7 +7,7 @@ from mock import patch, Mock
from
selenium.common.exceptions
import
NoSuchElementException
from
xblockutils.base_test
import
SeleniumXBlockTest
from
problem_builder.instructor_tool
import
InstructorToolBlock
from
problem_builder.instructor_tool
import
PAGE_SIZE
,
InstructorToolBlock
class
MockTasksModule
(
object
):
...
...
@@ -199,7 +199,7 @@ class InstructorToolTest(SeleniumXBlockTest):
successful
=
True
,
display_data
=
[[
'Test section'
,
'Test subsection'
,
'Test unit'
,
'Test type'
,
'Test question'
,
'Test answer'
,
'Test username'
]
for
_
in
range
(
45
)]),
]
for
_
in
range
(
PAGE_SIZE
*
3
)]),
'instructor_task'
:
True
,
'instructor_task.models'
:
MockInstructorTaskModelsModule
(),
})
...
...
@@ -224,7 +224,7 @@ class InstructorToolTest(SeleniumXBlockTest):
'Test type'
,
'Test question'
,
'Test answer'
,
'Test username'
]:
occurrences
=
re
.
findall
(
contents
,
result_block
.
text
)
self
.
assertEqual
(
len
(
occurrences
),
15
)
self
.
assertEqual
(
len
(
occurrences
),
PAGE_SIZE
)
self
.
assertFalse
(
first_page_button
.
is_enabled
())
self
.
assertFalse
(
prev_page_button
.
is_enabled
())
...
...
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