Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
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
edx
edx-platform
Commits
cb4025b1
Commit
cb4025b1
authored
Oct 30, 2013
by
Sarina Canelake
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1539 from edx/sarina/inst-dash-tasks
Enable Pending Tasks on beta dash // Course Info prettifying
parents
eb1b9260
b86e9129
Hide whitespace changes
Inline
Side-by-side
Showing
19 changed files
with
425 additions
and
198 deletions
+425
-198
lms/djangoapps/instructor/tests/test_api.py
+85
-21
lms/djangoapps/instructor/views/api.py
+38
-3
lms/djangoapps/instructor/views/instructor_dashboard.py
+21
-10
lms/djangoapps/instructor/views/legacy.py
+10
-8
lms/envs/dev.py
+2
-1
lms/static/coffee/src/instructor_dashboard/analytics.coffee
+4
-6
lms/static/coffee/src/instructor_dashboard/course_info.coffee
+17
-10
lms/static/coffee/src/instructor_dashboard/data_download.coffee
+15
-9
lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
+1
-1
lms/static/coffee/src/instructor_dashboard/membership.coffee
+4
-6
lms/static/coffee/src/instructor_dashboard/send_email.coffee
+14
-10
lms/static/coffee/src/instructor_dashboard/student_admin.coffee
+12
-77
lms/static/coffee/src/instructor_dashboard/util.coffee
+102
-1
lms/static/sass/course/instructor/_instructor_2.scss
+9
-2
lms/templates/instructor/instructor_dashboard_2/course_info.html
+49
-31
lms/templates/instructor/instructor_dashboard_2/data_download.html
+12
-0
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
+6
-2
lms/templates/instructor/instructor_dashboard_2/send_email.html
+12
-0
lms/templates/instructor/instructor_dashboard_2/student_admin.html
+12
-0
No files found.
lms/djangoapps/instructor/tests/test_api.py
View file @
cb4025b1
...
@@ -5,6 +5,7 @@ Unit tests for instructor.api methods.
...
@@ -5,6 +5,7 @@ Unit tests for instructor.api methods.
import
unittest
import
unittest
import
json
import
json
import
requests
import
requests
import
datetime
from
urllib
import
quote
from
urllib
import
quote
from
django.test
import
TestCase
from
django.test
import
TestCase
from
nose.tools
import
raises
from
nose.tools
import
raises
...
@@ -761,6 +762,18 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -761,6 +762,18 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
self
.
assertEqual
(
response
.
status_code
,
400
)
self
.
assertEqual
(
response
.
status_code
,
400
)
class
MockCompletionInfo
(
object
):
"""Mock for get_task_completion_info"""
times_called
=
0
def
mock_get_task_completion_info
(
self
,
*
args
):
# pylint: disable=unused-argument
"""Mock for get_task_completion_info"""
self
.
times_called
+=
1
if
self
.
times_called
%
2
==
0
:
return
True
,
'Task Completed'
return
False
,
'Task Errored In Some Way'
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
class
TestInstructorAPITaskLists
(
ModuleStoreTestCase
,
LoginEnrollmentTestCase
):
class
TestInstructorAPITaskLists
(
ModuleStoreTestCase
,
LoginEnrollmentTestCase
):
"""
"""
...
@@ -769,15 +782,46 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -769,15 +782,46 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
class
FakeTask
(
object
):
class
FakeTask
(
object
):
""" Fake task object """
""" Fake task object """
FEATURES
=
[
'task_type'
,
'task_input'
,
'task_id'
,
'requester'
,
'created'
,
'task_state'
]
FEATURES
=
[
'task_type'
,
'task_input'
,
'task_id'
,
'requester'
,
'task_state'
,
'created'
,
'status'
,
'task_message'
,
'duration_sec'
]
def
__init__
(
self
):
def
__init__
(
self
,
completion
):
for
feature
in
self
.
FEATURES
:
for
feature
in
self
.
FEATURES
:
setattr
(
self
,
feature
,
'expected'
)
setattr
(
self
,
feature
,
'expected'
)
# created needs to be a datetime
self
.
created
=
datetime
.
datetime
(
2013
,
10
,
25
,
11
,
42
,
35
)
# set 'status' and 'task_message' attrs
success
,
task_message
=
completion
()
if
success
:
self
.
status
=
"Complete"
else
:
self
.
status
=
"Incomplete"
self
.
task_message
=
task_message
# Set 'task_output' attr, which will be parsed to the 'duration_sec' attr.
self
.
task_output
=
'{"duration_ms": 1035000}'
self
.
duration_sec
=
1035000
/
1000.0
def
make_invalid_output
(
self
):
"""Munge task_output to be invalid json"""
self
.
task_output
=
'HI MY NAME IS INVALID JSON'
# This should be given the value of 'unknown' if the task output
# can't be properly parsed
self
.
duration_sec
=
'unknown'
def
to_dict
(
self
):
def
to_dict
(
self
):
""" Convert fake task to dictionary representation. """
""" Convert fake task to dictionary representation. """
return
{
key
:
'expected'
for
key
in
self
.
FEATURES
}
attr_dict
=
{
key
:
getattr
(
self
,
key
)
for
key
in
self
.
FEATURES
}
attr_dict
[
'created'
]
=
attr_dict
[
'created'
]
.
isoformat
()
return
attr_dict
def
setUp
(
self
):
def
setUp
(
self
):
self
.
instructor
=
AdminFactory
.
create
()
self
.
instructor
=
AdminFactory
.
create
()
...
@@ -797,58 +841,78 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
...
@@ -797,58 +841,78 @@ class TestInstructorAPITaskLists(ModuleStoreTestCase, LoginEnrollmentTestCase):
),
),
state
=
json
.
dumps
({
'attempts'
:
10
}),
state
=
json
.
dumps
({
'attempts'
:
10
}),
)
)
mock_factory
=
MockCompletionInfo
()
self
.
tasks
=
[
self
.
FakeTask
(
mock_factory
.
mock_get_task_completion_info
)
for
_
in
xrange
(
7
)]
self
.
tasks
[
-
1
]
.
make_invalid_output
()
self
.
tasks
=
[
self
.
FakeTask
()
for
_
in
xrange
(
6
)]
def
tearDown
(
self
):
"""
Undo all patches.
"""
patch
.
stopall
()
@patch.object
(
instructor_task
.
api
,
'get_running_instructor_tasks'
)
@patch.object
(
instructor_task
.
api
,
'get_running_instructor_tasks'
)
def
test_list_instructor_tasks_running
(
self
,
act
):
def
test_list_instructor_tasks_running
(
self
,
act
):
""" Test list of all running tasks. """
""" Test list of all running tasks. """
act
.
return_value
=
self
.
tasks
act
.
return_value
=
self
.
tasks
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
response
=
self
.
client
.
get
(
url
,
{})
mock_factory
=
MockCompletionInfo
()
print
response
.
content
with
patch
(
'instructor.views.api.get_task_completion_info'
)
as
mock_completion_info
:
mock_completion_info
.
side_effect
=
mock_factory
.
mock_get_task_completion_info
response
=
self
.
client
.
get
(
url
,
{})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# check response
# check response
self
.
assertTrue
(
act
.
called
)
self
.
assertTrue
(
act
.
called
)
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_res
=
{
'tasks'
:
expected_tasks
}
actual_tasks
=
json
.
loads
(
response
.
content
)[
'tasks'
]
self
.
assertEqual
(
json
.
loads
(
response
.
content
),
expected_res
)
for
exp_task
,
act_task
in
zip
(
expected_tasks
,
actual_tasks
):
self
.
assertDictEqual
(
exp_task
,
act_task
)
self
.
assertEqual
(
actual_tasks
,
expected_tasks
)
@patch.object
(
instructor_task
.
api
,
'get_instructor_task_history'
)
@patch.object
(
instructor_task
.
api
,
'get_instructor_task_history'
)
def
test_list_instructor_tasks_problem
(
self
,
act
):
def
test_list_instructor_tasks_problem
(
self
,
act
):
""" Test list task history for problem. """
""" Test list task history for problem. """
act
.
return_value
=
self
.
tasks
act
.
return_value
=
self
.
tasks
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
response
=
self
.
client
.
get
(
url
,
{
mock_factory
=
MockCompletionInfo
()
'problem_urlname'
:
self
.
problem_urlname
,
with
patch
(
'instructor.views.api.get_task_completion_info'
)
as
mock_completion_info
:
})
mock_completion_info
.
side_effect
=
mock_factory
.
mock_get_task_completion_info
print
response
.
content
response
=
self
.
client
.
get
(
url
,
{
'problem_urlname'
:
self
.
problem_urlname
,
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# check response
# check response
self
.
assertTrue
(
act
.
called
)
self
.
assertTrue
(
act
.
called
)
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_res
=
{
'tasks'
:
expected_tasks
}
actual_tasks
=
json
.
loads
(
response
.
content
)[
'tasks'
]
self
.
assertEqual
(
json
.
loads
(
response
.
content
),
expected_res
)
for
exp_task
,
act_task
in
zip
(
expected_tasks
,
actual_tasks
):
self
.
assertDictEqual
(
exp_task
,
act_task
)
self
.
assertEqual
(
actual_tasks
,
expected_tasks
)
@patch.object
(
instructor_task
.
api
,
'get_instructor_task_history'
)
@patch.object
(
instructor_task
.
api
,
'get_instructor_task_history'
)
def
test_list_instructor_tasks_problem_student
(
self
,
act
):
def
test_list_instructor_tasks_problem_student
(
self
,
act
):
""" Test list task history for problem AND student. """
""" Test list task history for problem AND student. """
act
.
return_value
=
self
.
tasks
act
.
return_value
=
self
.
tasks
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
url
=
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
self
.
course
.
id
})
response
=
self
.
client
.
get
(
url
,
{
mock_factory
=
MockCompletionInfo
()
'problem_urlname'
:
self
.
problem_urlname
,
with
patch
(
'instructor.views.api.get_task_completion_info'
)
as
mock_completion_info
:
'unique_student_identifier'
:
self
.
student
.
email
,
mock_completion_info
.
side_effect
=
mock_factory
.
mock_get_task_completion_info
})
response
=
self
.
client
.
get
(
url
,
{
print
response
.
content
'problem_urlname'
:
self
.
problem_urlname
,
'unique_student_identifier'
:
self
.
student
.
email
,
})
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
# check response
# check response
self
.
assertTrue
(
act
.
called
)
self
.
assertTrue
(
act
.
called
)
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_tasks
=
[
ftask
.
to_dict
()
for
ftask
in
self
.
tasks
]
expected_res
=
{
'tasks'
:
expected_tasks
}
actual_tasks
=
json
.
loads
(
response
.
content
)[
'tasks'
]
self
.
assertEqual
(
json
.
loads
(
response
.
content
),
expected_res
)
for
exp_task
,
act_task
in
zip
(
expected_tasks
,
actual_tasks
):
self
.
assertDictEqual
(
exp_task
,
act_task
)
self
.
assertEqual
(
actual_tasks
,
expected_tasks
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
@override_settings
(
MODULESTORE
=
TEST_DATA_MIXED_MODULESTORE
)
...
...
lms/djangoapps/instructor/views/api.py
View file @
cb4025b1
...
@@ -8,6 +8,7 @@ Many of these GETs may become PUTs in the future.
...
@@ -8,6 +8,7 @@ Many of these GETs may become PUTs in the future.
import
re
import
re
import
logging
import
logging
import
json
import
requests
import
requests
from
django.conf
import
settings
from
django.conf
import
settings
from
django_future.csrf
import
ensure_csrf_cookie
from
django_future.csrf
import
ensure_csrf_cookie
...
@@ -30,6 +31,7 @@ from courseware.models import StudentModule
...
@@ -30,6 +31,7 @@ from courseware.models import StudentModule
from
student.models
import
unique_id_for_user
from
student.models
import
unique_id_for_user
import
instructor_task.api
import
instructor_task.api
from
instructor_task.api_helper
import
AlreadyRunningError
from
instructor_task.api_helper
import
AlreadyRunningError
from
instructor_task.views
import
get_task_completion_info
import
instructor.enrollment
as
enrollment
import
instructor.enrollment
as
enrollment
from
instructor.enrollment
import
enroll_email
,
unenroll_email
from
instructor.enrollment
import
enroll_email
,
unenroll_email
from
instructor.views.tools
import
strip_if_string
,
get_student_from_identifier
from
instructor.views.tools
import
strip_if_string
,
get_student_from_identifier
...
@@ -675,9 +677,42 @@ def list_instructor_tasks(request, course_id):
...
@@ -675,9 +677,42 @@ def list_instructor_tasks(request, course_id):
tasks
=
instructor_task
.
api
.
get_running_instructor_tasks
(
course_id
)
tasks
=
instructor_task
.
api
.
get_running_instructor_tasks
(
course_id
)
def
extract_task_features
(
task
):
def
extract_task_features
(
task
):
""" Convert task to dict for json rendering """
"""
features
=
[
'task_type'
,
'task_input'
,
'task_id'
,
'requester'
,
'created'
,
'task_state'
]
Convert task to dict for json rendering.
return
dict
((
feature
,
str
(
getattr
(
task
,
feature
)))
for
feature
in
features
)
Expects tasks have the following features:
* task_type (str, type of task)
* task_input (dict, input(s) to the task)
* task_id (str, celery id of the task)
* requester (str, username who submitted the task)
* task_state (str, state of task eg PROGRESS, COMPLETED)
* created (datetime, when the task was completed)
* task_output (optional)
"""
# Pull out information from the task
features
=
[
'task_type'
,
'task_input'
,
'task_id'
,
'requester'
,
'task_state'
]
task_feature_dict
=
{
feature
:
str
(
getattr
(
task
,
feature
))
for
feature
in
features
}
# Some information (created, duration, status, task message) require additional formatting
task_feature_dict
[
'created'
]
=
task
.
created
.
isoformat
()
# Get duration info, if known
duration_sec
=
'unknown'
if
hasattr
(
task
,
'task_output'
)
and
task
.
task_output
is
not
None
:
try
:
task_output
=
json
.
loads
(
task
.
task_output
)
except
ValueError
:
log
.
error
(
"Could not parse task output as valid json; task output:
%
s"
,
task
.
task_output
)
else
:
if
'duration_ms'
in
task_output
:
duration_sec
=
int
(
task_output
[
'duration_ms'
]
/
1000.0
)
task_feature_dict
[
'duration_sec'
]
=
duration_sec
# Get progress status message & success information
success
,
task_message
=
get_task_completion_info
(
task
)
status
=
_
(
"Complete"
)
if
success
else
_
(
"Incomplete"
)
task_feature_dict
[
'status'
]
=
status
task_feature_dict
[
'task_message'
]
=
task_message
return
task_feature_dict
response_payload
=
{
response_payload
=
{
'tasks'
:
map
(
extract_task_features
,
tasks
),
'tasks'
:
map
(
extract_task_features
,
tasks
),
...
...
lms/djangoapps/instructor/views/instructor_dashboard.py
View file @
cb4025b1
...
@@ -18,7 +18,7 @@ from xmodule.modulestore.django import modulestore
...
@@ -18,7 +18,7 @@ from xmodule.modulestore.django import modulestore
from
xblock.field_data
import
DictFieldData
from
xblock.field_data
import
DictFieldData
from
xblock.fields
import
ScopeIds
from
xblock.fields
import
ScopeIds
from
courseware.access
import
has_access
from
courseware.access
import
has_access
from
courseware.courses
import
get_course_by_id
from
courseware.courses
import
get_course_by_id
,
get_cms_course_link_by_id
from
django_comment_client.utils
import
has_forum_access
from
django_comment_client.utils
import
has_forum_access
from
django_comment_common.models
import
FORUM_ROLE_ADMINISTRATOR
from
django_comment_common.models
import
FORUM_ROLE_ADMINISTRATOR
from
student.models
import
CourseEnrollment
from
student.models
import
CourseEnrollment
...
@@ -45,27 +45,32 @@ def instructor_dashboard_2(request, course_id):
...
@@ -45,27 +45,32 @@ def instructor_dashboard_2(request, course_id):
raise
Http404
()
raise
Http404
()
sections
=
[
sections
=
[
_section_course_info
(
course_id
,
access
),
_section_course_info
(
course_id
),
_section_membership
(
course_id
,
access
),
_section_membership
(
course_id
,
access
),
_section_student_admin
(
course_id
,
access
),
_section_student_admin
(
course_id
,
access
),
_section_data_download
(
course_id
),
_section_data_download
(
course_id
),
_section_analytics
(
course_id
),
_section_analytics
(
course_id
),
]
]
# Gate access to course email by feature flag & by course-specific authorization
if
settings
.
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_EMAIL'
]
and
\
is_studio_course
and
CourseAuthorization
.
instructor_email_enabled
(
course_id
):
sections
.
append
(
_section_send_email
(
course_id
,
access
,
course
))
studio_url
=
None
if
is_studio_course
:
studio_url
=
get_cms_course_link_by_id
(
course_id
)
enrollment_count
=
sections
[
0
][
'enrollment_count'
]
enrollment_count
=
sections
[
0
][
'enrollment_count'
]
disable_buttons
=
False
disable_buttons
=
False
max_enrollment_for_buttons
=
settings
.
MITX_FEATURES
.
get
(
"MAX_ENROLLMENT_INSTR_BUTTONS"
)
max_enrollment_for_buttons
=
settings
.
MITX_FEATURES
.
get
(
"MAX_ENROLLMENT_INSTR_BUTTONS"
)
if
max_enrollment_for_buttons
is
not
None
:
if
max_enrollment_for_buttons
is
not
None
:
disable_buttons
=
enrollment_count
>
max_enrollment_for_buttons
disable_buttons
=
enrollment_count
>
max_enrollment_for_buttons
# Gate access by feature flag & by course-specific authorization
if
settings
.
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_EMAIL'
]
and
\
is_studio_course
and
CourseAuthorization
.
instructor_email_enabled
(
course_id
):
sections
.
append
(
_section_send_email
(
course_id
,
access
,
course
))
context
=
{
context
=
{
'course'
:
course
,
'course'
:
course
,
'old_dashboard_url'
:
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
course_id
}),
'old_dashboard_url'
:
reverse
(
'instructor_dashboard'
,
kwargs
=
{
'course_id'
:
course_id
}),
'studio_url'
:
studio_url
,
'sections'
:
sections
,
'sections'
:
sections
,
'disable_buttons'
:
disable_buttons
,
'disable_buttons'
:
disable_buttons
,
}
}
...
@@ -86,15 +91,19 @@ section_display_name will be used to generate link titles in the nav bar.
...
@@ -86,15 +91,19 @@ section_display_name will be used to generate link titles in the nav bar.
"""
# pylint: disable=W0105
"""
# pylint: disable=W0105
def
_section_course_info
(
course_id
,
access
):
def
_section_course_info
(
course_id
):
""" Provide data for the corresponding dashboard section """
""" Provide data for the corresponding dashboard section """
course
=
get_course_by_id
(
course_id
,
depth
=
None
)
course
=
get_course_by_id
(
course_id
,
depth
=
None
)
course_org
,
course_num
,
course_name
=
course_id
.
split
(
'/'
)
section_data
=
{
section_data
=
{
'section_key'
:
'course_info'
,
'section_key'
:
'course_info'
,
'section_display_name'
:
_
(
'Course Info'
),
'section_display_name'
:
_
(
'Course Info'
),
'course_id'
:
course_id
,
'course_id'
:
course_id
,
'access'
:
access
,
'course_org'
:
course_org
,
'course_num'
:
course_num
,
'course_name'
:
course_name
,
'course_display_name'
:
course
.
display_name
,
'course_display_name'
:
course
.
display_name
,
'enrollment_count'
:
CourseEnrollment
.
objects
.
filter
(
course_id
=
course_id
)
.
count
(),
'enrollment_count'
:
CourseEnrollment
.
objects
.
filter
(
course_id
=
course_id
)
.
count
(),
'has_started'
:
course
.
has_started
(),
'has_started'
:
course
.
has_started
(),
...
@@ -156,6 +165,7 @@ def _section_data_download(course_id):
...
@@ -156,6 +165,7 @@ def _section_data_download(course_id):
'get_grading_config_url'
:
reverse
(
'get_grading_config'
,
kwargs
=
{
'course_id'
:
course_id
}),
'get_grading_config_url'
:
reverse
(
'get_grading_config'
,
kwargs
=
{
'course_id'
:
course_id
}),
'get_students_features_url'
:
reverse
(
'get_students_features'
,
kwargs
=
{
'course_id'
:
course_id
}),
'get_students_features_url'
:
reverse
(
'get_students_features'
,
kwargs
=
{
'course_id'
:
course_id
}),
'get_anon_ids_url'
:
reverse
(
'get_anon_ids'
,
kwargs
=
{
'course_id'
:
course_id
}),
'get_anon_ids_url'
:
reverse
(
'get_anon_ids'
,
kwargs
=
{
'course_id'
:
course_id
}),
'list_instructor_tasks_url'
:
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
course_id
}),
}
}
return
section_data
return
section_data
...
@@ -171,7 +181,8 @@ def _section_send_email(course_id, access, course):
...
@@ -171,7 +181,8 @@ def _section_send_email(course_id, access, course):
'section_display_name'
:
_
(
'Email'
),
'section_display_name'
:
_
(
'Email'
),
'access'
:
access
,
'access'
:
access
,
'send_email'
:
reverse
(
'send_email'
,
kwargs
=
{
'course_id'
:
course_id
}),
'send_email'
:
reverse
(
'send_email'
,
kwargs
=
{
'course_id'
:
course_id
}),
'editor'
:
email_editor
'editor'
:
email_editor
,
'list_instructor_tasks_url'
:
reverse
(
'list_instructor_tasks'
,
kwargs
=
{
'course_id'
:
course_id
}),
}
}
return
section_data
return
section_data
...
...
lms/djangoapps/instructor/views/legacy.py
View file @
cb4025b1
...
@@ -1589,14 +1589,16 @@ def get_background_task_table(course_id, problem_url=None, student=None, task_ty
...
@@ -1589,14 +1589,16 @@ def get_background_task_table(course_id, problem_url=None, student=None, task_ty
success
,
task_message
=
get_task_completion_info
(
instructor_task
)
success
,
task_message
=
get_task_completion_info
(
instructor_task
)
status
=
"Complete"
if
success
else
"Incomplete"
status
=
"Complete"
if
success
else
"Incomplete"
# generate row for this task:
# generate row for this task:
row
=
[
str
(
instructor_task
.
task_type
),
row
=
[
str
(
instructor_task
.
task_id
),
str
(
instructor_task
.
task_type
),
str
(
instructor_task
.
requester
),
str
(
instructor_task
.
task_id
),
instructor_task
.
created
.
isoformat
(
' '
),
str
(
instructor_task
.
requester
),
duration_sec
,
instructor_task
.
created
.
isoformat
(
' '
),
str
(
instructor_task
.
task_state
),
duration_sec
,
status
,
str
(
instructor_task
.
task_state
),
task_message
]
status
,
task_message
]
datatable
[
'data'
]
.
append
(
row
)
datatable
[
'data'
]
.
append
(
row
)
if
problem_url
is
None
:
if
problem_url
is
None
:
...
...
lms/envs/dev.py
View file @
cb4025b1
...
@@ -29,7 +29,8 @@ MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
...
@@ -29,7 +29,8 @@ MITX_FEATURES['ENABLE_MANUAL_GIT_RELOAD'] = True
MITX_FEATURES
[
'ENABLE_PSYCHOMETRICS'
]
=
False
# real-time psychometrics (eg item response theory analysis in instructor dashboard)
MITX_FEATURES
[
'ENABLE_PSYCHOMETRICS'
]
=
False
# real-time psychometrics (eg item response theory analysis in instructor dashboard)
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_ANALYTICS'
]
=
True
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_ANALYTICS'
]
=
True
MITX_FEATURES
[
'ENABLE_SERVICE_STATUS'
]
=
True
MITX_FEATURES
[
'ENABLE_SERVICE_STATUS'
]
=
True
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_EMAIL'
]
=
True
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_EMAIL'
]
=
True
# Enable email for all Studio courses
MITX_FEATURES
[
'REQUIRE_COURSE_EMAIL_AUTH'
]
=
False
# Give all courses email (don't require django-admin perms)
MITX_FEATURES
[
'ENABLE_HINTER_INSTRUCTOR_VIEW'
]
=
True
MITX_FEATURES
[
'ENABLE_HINTER_INSTRUCTOR_VIEW'
]
=
True
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_BETA_DASHBOARD'
]
=
True
MITX_FEATURES
[
'ENABLE_INSTRUCTOR_BETA_DASHBOARD'
]
=
True
MITX_FEATURES
[
'MULTIPLE_ENROLLMENT_ROLES'
]
=
True
MITX_FEATURES
[
'MULTIPLE_ENROLLMENT_ROLES'
]
=
True
...
...
lms/static/coffee/src/instructor_dashboard/analytics.coffee
View file @
cb4025b1
...
@@ -230,9 +230,7 @@ class Analytics
...
@@ -230,9 +230,7 @@ class Analytics
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
Analytics
:
Analytics
_
.
defaults
window
.
InstructorDashboard
.
sections
,
Analytics
:
Analytics
lms/static/coffee/src/instructor_dashboard/course_info.coffee
View file @
cb4025b1
###
###
Course Info Section
Course Info Section
This is the implementation of the simplest section
of the instructor dashboard.
imports from other modules.
imports from other modules.
wrap in (-> ... apply) to defer evaluation
wrap in (-> ... apply) to defer evaluation
such that the value can be defined later than this assignment (file load order).
such that the value can be defined later than this assignment (file load order).
###
###
plantTimeout
=
->
window
.
InstructorDashboard
.
util
.
plantTimeout
.
apply
this
,
argument
s
# Load utilitie
s
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
argument
s
PendingInstructorTasks
=
->
window
.
InstructorDashboard
.
util
.
PendingInstructorTask
s
# A typical section object.
# A typical section object.
# constructed with $section, a jquery object
# constructed with $section, a jquery object
# which holds the section body container.
# which holds the section body container.
class
CourseInfo
class
CourseInfo
constructor
:
(
@
$section
)
->
constructor
:
(
@
$section
)
->
# attach self to html so that instructor_dashboard.coffee can find
# this object to call event handlers like 'onClickTitle'
@
$section
.
data
'wrapper'
,
@
# gather elements
@
instructor_tasks
=
new
(
PendingInstructorTasks
())
@
$section
@
$course_errors_wrapper
=
@
$section
.
find
'.course-errors-wrapper'
@
$course_errors_wrapper
=
@
$section
.
find
'.course-errors-wrapper'
# if there are errors
# if there are errors
...
@@ -37,12 +41,15 @@ class CourseInfo
...
@@ -37,12 +41,15 @@ class CourseInfo
else
else
@
$course_errors_wrapper
.
addClass
'open'
@
$course_errors_wrapper
.
addClass
'open'
# handler for when the section title is clicked.
onClickTitle
:
->
@
instructor_tasks
.
task_poller
.
start
()
# handler for when the section is closed
onExit
:
->
@
instructor_tasks
.
task_poller
.
stop
()
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
CourseInfo
:
CourseInfo
_
.
defaults
window
.
InstructorDashboard
.
sections
,
CourseInfo
:
CourseInfo
lms/static/coffee/src/instructor_dashboard/data_download.coffee
View file @
cb4025b1
...
@@ -6,13 +6,16 @@ wrap in (-> ... apply) to defer evaluation
...
@@ -6,13 +6,16 @@ wrap in (-> ... apply) to defer evaluation
such that the value can be defined later than this assignment (file load order).
such that the value can be defined later than this assignment (file load order).
###
###
plantTimeout
=
->
window
.
InstructorDashboard
.
util
.
plantTimeout
.
apply
this
,
argument
s
# Load utilitie
s
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
PendingInstructorTasks
=
->
window
.
InstructorDashboard
.
util
.
PendingInstructorTasks
# Data Download Section
# Data Download Section
class
DataDownload
class
DataDownload
constructor
:
(
@
$section
)
->
constructor
:
(
@
$section
)
->
# attach self to html so that instructor_dashboard.coffee can find
# this object to call event handlers like 'onClickTitle'
@
$section
.
data
'wrapper'
,
@
# gather elements
# gather elements
@
$display
=
@
$section
.
find
'.data-display'
@
$display
=
@
$section
.
find
'.data-display'
@
$display_text
=
@
$display
.
find
'.data-display-text'
@
$display_text
=
@
$display
.
find
'.data-display-text'
...
@@ -21,9 +24,9 @@ class DataDownload
...
@@ -21,9 +24,9 @@ class DataDownload
@
$list_studs_btn
=
@
$section
.
find
(
"input[name='list-profiles']'"
)
@
$list_studs_btn
=
@
$section
.
find
(
"input[name='list-profiles']'"
)
@
$list_anon_btn
=
@
$section
.
find
(
"input[name='list-anon-ids']'"
)
@
$list_anon_btn
=
@
$section
.
find
(
"input[name='list-anon-ids']'"
)
@
$grade_config_btn
=
@
$section
.
find
(
"input[name='dump-gradeconf']'"
)
@
$grade_config_btn
=
@
$section
.
find
(
"input[name='dump-gradeconf']'"
)
@
instructor_tasks
=
new
(
PendingInstructorTasks
())
@
$section
# attach click handlers
# attach click handlers
# The list-anon case is always CSV
# The list-anon case is always CSV
@
$list_anon_btn
.
click
(
e
)
=>
@
$list_anon_btn
.
click
(
e
)
=>
url
=
@
$list_anon_btn
.
data
'endpoint'
url
=
@
$list_anon_btn
.
data
'endpoint'
...
@@ -80,6 +83,11 @@ class DataDownload
...
@@ -80,6 +83,11 @@ class DataDownload
@
clear_display
()
@
clear_display
()
@
$display_text
.
html
data
[
'grading_config_summary'
]
@
$display_text
.
html
data
[
'grading_config_summary'
]
# handler for when the section title is clicked.
onClickTitle
:
->
@
instructor_tasks
.
task_poller
.
start
()
# handler for when the section is closed
onExit
:
->
@
instructor_tasks
.
task_poller
.
stop
()
clear_display
:
->
clear_display
:
->
@
$display_text
.
empty
()
@
$display_text
.
empty
()
...
@@ -89,9 +97,7 @@ class DataDownload
...
@@ -89,9 +97,7 @@ class DataDownload
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
DataDownload
:
DataDownload
_
.
defaults
window
.
InstructorDashboard
.
sections
,
DataDownload
:
DataDownload
lms/static/coffee/src/instructor_dashboard/instructor_dashboard.coffee
View file @
cb4025b1
...
@@ -118,7 +118,7 @@ setup_instructor_dashboard = (idash_content) =>
...
@@ -118,7 +118,7 @@ setup_instructor_dashboard = (idash_content) =>
location
.
hash
=
"
#{
HASH_LINK_PREFIX
}#{
section_name
}
"
location
.
hash
=
"
#{
HASH_LINK_PREFIX
}#{
section_name
}
"
sections_have_loaded
.
after
->
sections_have_loaded
.
after
->
$section
.
data
(
'wrapper'
)
?
.
onClickTitle
?
()
$section
.
data
(
'wrapper'
)
.
onClickTitle
()
# call onExit handler if exiting a section to a different section.
# call onExit handler if exiting a section to a different section.
unless
$section
.
is
$active_section
unless
$section
.
is
$active_section
...
...
lms/static/coffee/src/instructor_dashboard/membership.coffee
View file @
cb4025b1
...
@@ -487,9 +487,7 @@ class Membership
...
@@ -487,9 +487,7 @@ class Membership
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
Membership
:
Membership
_
.
defaults
window
.
InstructorDashboard
.
sections
,
Membership
:
Membership
lms/static/coffee/src/instructor_dashboard/send_email.coffee
View file @
cb4025b1
...
@@ -6,8 +6,10 @@ wrap in (-> ... apply) to defer evaluation
...
@@ -6,8 +6,10 @@ wrap in (-> ... apply) to defer evaluation
such that the value can be defined later than this assignment (file load order).
such that the value can be defined later than this assignment (file load order).
###
###
# Load utilities
plantTimeout
=
->
window
.
InstructorDashboard
.
util
.
plantTimeout
.
apply
this
,
arguments
plantTimeout
=
->
window
.
InstructorDashboard
.
util
.
plantTimeout
.
apply
this
,
arguments
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
PendingInstructorTasks
=
->
window
.
InstructorDashboard
.
util
.
PendingInstructorTasks
class
SendEmail
class
SendEmail
constructor
:
(
@
$container
)
->
constructor
:
(
@
$container
)
->
...
@@ -79,23 +81,25 @@ class SendEmail
...
@@ -79,23 +81,25 @@ class SendEmail
class
Email
class
Email
# enable subsections.
# enable subsections.
constructor
:
(
@
$section
)
->
constructor
:
(
@
$section
)
->
# attach self to html
# attach self to html so that instructor_dashboard.coffee can find
# so that instructor_dashboard.coffee can find this object
# this object to call event handlers like 'onClickTitle'
# to call event handlers like 'onClickTitle'
@
$section
.
data
'wrapper'
,
@
@
$section
.
data
'wrapper'
,
@
# isolate # initialize SendEmail subsection
# isolate # initialize SendEmail subsection
plantTimeout
0
,
=>
new
SendEmail
@
$section
.
find
'.send-email'
plantTimeout
0
,
=>
new
SendEmail
@
$section
.
find
'.send-email'
@
instructor_tasks
=
new
(
PendingInstructorTasks
())
@
$section
# handler for when the section title is clicked.
# handler for when the section title is clicked.
onClickTitle
:
->
onClickTitle
:
->
@
instructor_tasks
.
task_poller
.
start
()
# handler for when the section is closed
onExit
:
->
@
instructor_tasks
.
task_poller
.
stop
()
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
Email
:
Email
_
.
defaults
window
.
InstructorDashboard
.
sections
,
Email
:
Email
lms/static/coffee/src/instructor_dashboard/student_admin.coffee
View file @
cb4025b1
...
@@ -6,10 +6,10 @@ wrap in (-> ... apply) to defer evaluation
...
@@ -6,10 +6,10 @@ wrap in (-> ... apply) to defer evaluation
such that the value can be defined later than this assignment (file load order).
such that the value can be defined later than this assignment (file load order).
###
###
plantTimeout
=
->
window
.
InstructorDashboard
.
util
.
plantTimeout
.
apply
this
,
arguments
# Load utilities
plantInterval
=
->
window
.
InstructorDashboard
.
util
.
plantInterval
.
apply
this
,
arguments
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
std_ajax_err
=
->
window
.
InstructorDashboard
.
util
.
std_ajax_err
.
apply
this
,
arguments
load_IntervalManager
=
->
window
.
InstructorDashboard
.
util
.
IntervalManager
create_task_list_table
=
->
window
.
InstructorDashboard
.
util
.
create_task_list_table
.
apply
this
,
arguments
PendingInstructorTasks
=
->
window
.
InstructorDashboard
.
util
.
PendingInstructorTasks
# get jquery element and assert its existance
# get jquery element and assert its existance
...
@@ -21,57 +21,11 @@ find_and_assert = ($root, selector) ->
...
@@ -21,57 +21,11 @@ find_and_assert = ($root, selector) ->
else
else
item
item
# render a task list table to the DOM
# `$table_tasks` the $element in which to put the table
# `tasks_data`
create_task_list_table
=
(
$table_tasks
,
tasks_data
)
->
$table_tasks
.
empty
()
options
=
enableCellNavigation
:
true
enableColumnReorder
:
false
autoHeight
:
true
rowHeight
:
60
forceFitColumns
:
true
columns
=
[
id
:
'task_type'
field
:
'task_type'
name
:
'Task Type'
,
id
:
'requester'
field
:
'requester'
name
:
'Requester'
width
:
30
,
id
:
'task_input'
field
:
'task_input'
name
:
'Input'
,
id
:
'task_state'
field
:
'task_state'
name
:
'State'
width
:
30
,
id
:
'task_id'
field
:
'task_id'
name
:
'Task ID'
width
:
50
,
id
:
'created'
field
:
'created'
name
:
'Created'
]
table_data
=
tasks_data
$table_placeholder
=
$
'<div/>'
,
class
:
'slickgrid'
$table_tasks
.
append
$table_placeholder
grid
=
new
Slick
.
Grid
(
$table_placeholder
,
table_data
,
columns
,
options
)
class
StudentAdmin
class
StudentAdmin
constructor
:
(
@
$section
)
->
constructor
:
(
@
$section
)
->
# attach self to html so that instructor_dashboard.coffee can find
# this object to call event handlers like 'onClickTitle'
@
$section
.
data
'wrapper'
,
@
@
$section
.
data
'wrapper'
,
@
# gather buttons
# gather buttons
...
@@ -93,22 +47,13 @@ class StudentAdmin
...
@@ -93,22 +47,13 @@ class StudentAdmin
@
$btn_rescore_problem_all
=
@
$section
.
find
"input[name='rescore-problem-all']"
@
$btn_rescore_problem_all
=
@
$section
.
find
"input[name='rescore-problem-all']"
@
$btn_task_history_all
=
@
$section
.
find
"input[name='task-history-all']"
@
$btn_task_history_all
=
@
$section
.
find
"input[name='task-history-all']"
@
$table_task_history_all
=
@
$section
.
find
".task-history-all-table"
@
$table_task_history_all
=
@
$section
.
find
".task-history-all-table"
@
$table_running_tasks
=
@
$section
.
find
".running-tasks-table"
@
instructor_tasks
=
new
(
PendingInstructorTasks
())
@
$section
# response areas
# response areas
@
$request_response_error_progress
=
find_and_assert
@
$section
,
".student-specific-container .request-response-error"
@
$request_response_error_progress
=
find_and_assert
@
$section
,
".student-specific-container .request-response-error"
@
$request_response_error_grade
=
find_and_assert
@
$section
,
".student-grade-container .request-response-error"
@
$request_response_error_grade
=
find_and_assert
@
$section
,
".student-grade-container .request-response-error"
@
$request_response_error_all
=
@
$section
.
find
".course-specific-container .request-response-error"
@
$request_response_error_all
=
@
$section
.
find
".course-specific-container .request-response-error"
# start polling for task list
# if the list is in the DOM
if
@
$table_running_tasks
.
length
>
0
# reload every 20 seconds.
TASK_LIST_POLL_INTERVAL
=
20000
@
reload_running_tasks_list
()
@
task_poller
=
new
(
load_IntervalManager
())
TASK_LIST_POLL_INTERVAL
,
=>
@
reload_running_tasks_list
()
# attach click handlers
# attach click handlers
# go to student progress page
# go to student progress page
...
@@ -294,14 +239,6 @@ class StudentAdmin
...
@@ -294,14 +239,6 @@ class StudentAdmin
create_task_list_table
@
$table_task_history_all
,
data
.
tasks
create_task_list_table
@
$table_task_history_all
,
data
.
tasks
error
:
std_ajax_err
=>
@
$request_response_error_all
.
text
gettext
(
"Error listing task history for this student and problem."
)
error
:
std_ajax_err
=>
@
$request_response_error_all
.
text
gettext
(
"Error listing task history for this student and problem."
)
reload_running_tasks_list
:
=>
list_endpoint
=
@
$table_running_tasks
.
data
'endpoint'
$
.
ajax
dataType
:
'json'
url
:
list_endpoint
success
:
(
data
)
=>
create_task_list_table
@
$table_running_tasks
,
data
.
tasks
error
:
std_ajax_err
=>
console
.
warn
"error listing all instructor tasks"
# wraps a function, but first clear the error displays
# wraps a function, but first clear the error displays
clear_errors_then
:
(
cb
)
->
clear_errors_then
:
(
cb
)
->
@
$request_response_error_progress
.
empty
()
@
$request_response_error_progress
.
empty
()
...
@@ -317,17 +254,15 @@ class StudentAdmin
...
@@ -317,17 +254,15 @@ class StudentAdmin
@
$request_response_error_all
.
empty
()
@
$request_response_error_all
.
empty
()
# handler for when the section title is clicked.
# handler for when the section title is clicked.
onClickTitle
:
->
@
task_poller
?
.
start
()
onClickTitle
:
->
@
instructor_tasks
.
task_poller
.
start
()
# handler for when the section is closed
# handler for when the section is closed
onExit
:
->
@
task_poller
?
.
stop
()
onExit
:
->
@
instructor_tasks
.
task_poller
.
stop
()
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
_
.
defaults
window
,
InstructorDashboard
:
{}
if
_
?
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
_
.
defaults
window
,
InstructorDashboard
:
{}
_
.
defaults
window
.
InstructorDashboard
.
sections
,
_
.
defaults
window
.
InstructorDashboard
,
sections
:
{}
StudentAdmin
:
StudentAdmin
_
.
defaults
window
.
InstructorDashboard
.
sections
,
StudentAdmin
:
StudentAdmin
lms/static/coffee/src/instructor_dashboard/util.coffee
View file @
cb4025b1
...
@@ -6,6 +6,15 @@ plantTimeout = (ms, cb) -> setTimeout cb, ms
...
@@ -6,6 +6,15 @@ plantTimeout = (ms, cb) -> setTimeout cb, ms
plantInterval
=
(
ms
,
cb
)
->
setInterval
cb
,
ms
plantInterval
=
(
ms
,
cb
)
->
setInterval
cb
,
ms
# get jquery element and assert its existance
find_and_assert
=
(
$root
,
selector
)
->
item
=
$root
.
find
selector
if
item
.
length
!=
1
console
.
error
"element selection failed for '
#{
selector
}
' resulted in length
#{
item
.
length
}
"
throw
"Failed Element Selection"
else
item
# standard ajax error wrapper
# standard ajax error wrapper
#
#
# wraps a `handler` function so that first
# wraps a `handler` function so that first
...
@@ -17,6 +26,72 @@ std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
...
@@ -17,6 +26,72 @@ std_ajax_err = (handler) -> (jqXHR, textStatus, errorThrown) ->
handler
.
apply
this
,
arguments
handler
.
apply
this
,
arguments
# render a task list table to the DOM
# `$table_tasks` the $element in which to put the table
# `tasks_data`
create_task_list_table
=
(
$table_tasks
,
tasks_data
)
->
$table_tasks
.
empty
()
options
=
enableCellNavigation
:
true
enableColumnReorder
:
false
autoHeight
:
true
rowHeight
:
60
forceFitColumns
:
true
columns
=
[
id
:
'task_type'
field
:
'task_type'
name
:
'Task Type'
minWidth
:
100
,
id
:
'task_input'
field
:
'task_input'
name
:
'Task inputs'
minWidth
:
150
,
id
:
'task_id'
field
:
'task_id'
name
:
'Task ID'
minWidth
:
150
,
id
:
'requester'
field
:
'requester'
name
:
'Requester'
minWidth
:
80
,
id
:
'created'
field
:
'created'
name
:
'Submitted'
minWidth
:
120
,
id
:
'duration_sec'
field
:
'duration_sec'
name
:
'Duration (sec)'
minWidth
:
80
,
id
:
'task_state'
field
:
'task_state'
name
:
'State'
minWidth
:
80
,
id
:
'status'
field
:
'status'
name
:
'Task Status'
minWidth
:
80
,
id
:
'task_message'
field
:
'task_message'
name
:
'Task Progress'
minWidth
:
120
]
table_data
=
tasks_data
$table_placeholder
=
$
'<div/>'
,
class
:
'slickgrid'
$table_tasks
.
append
$table_placeholder
grid
=
new
Slick
.
Grid
(
$table_placeholder
,
table_data
,
columns
,
options
)
# Helper class for managing the execution of interval tasks.
# Helper class for managing the execution of interval tasks.
# Handles pausing and restarting.
# Handles pausing and restarting.
class
IntervalManager
class
IntervalManager
...
@@ -26,8 +101,8 @@ class IntervalManager
...
@@ -26,8 +101,8 @@ class IntervalManager
@
intervalID
=
null
@
intervalID
=
null
# Start or restart firing every `ms` milliseconds.
# Start or restart firing every `ms` milliseconds.
# Soes not fire immediately.
start
:
->
start
:
->
@
fn
()
if
@
intervalID
is
null
if
@
intervalID
is
null
@
intervalID
=
setInterval
@
fn
,
@
ms
@
intervalID
=
setInterval
@
fn
,
@
ms
...
@@ -37,6 +112,30 @@ class IntervalManager
...
@@ -37,6 +112,30 @@ class IntervalManager
@
intervalID
=
null
@
intervalID
=
null
class
PendingInstructorTasks
### Pending Instructor Tasks Section ####
constructor
:
(
@
$section
)
->
# Currently running tasks
@
$table_running_tasks
=
find_and_assert
@
$section
,
".running-tasks-table"
# start polling for task list
# if the list is in the DOM
if
@
$table_running_tasks
.
length
>
0
# reload every 20 seconds.
TASK_LIST_POLL_INTERVAL
=
20000
@
reload_running_tasks_list
()
@
task_poller
=
new
IntervalManager
(
TASK_LIST_POLL_INTERVAL
,
=>
@
reload_running_tasks_list
())
# Populate the running tasks list
reload_running_tasks_list
:
=>
list_endpoint
=
@
$table_running_tasks
.
data
'endpoint'
$
.
ajax
dataType
:
'json'
url
:
list_endpoint
success
:
(
data
)
=>
create_task_list_table
@
$table_running_tasks
,
data
.
tasks
error
:
std_ajax_err
=>
console
.
warn
"error listing all instructor tasks"
### /Pending Instructor Tasks Section ####
# export for use
# export for use
# create parent namespaces if they do not already exist.
# create parent namespaces if they do not already exist.
# abort if underscore can not be found.
# abort if underscore can not be found.
...
@@ -47,3 +146,5 @@ if _?
...
@@ -47,3 +146,5 @@ if _?
plantInterval
:
plantInterval
plantInterval
:
plantInterval
std_ajax_err
:
std_ajax_err
std_ajax_err
:
std_ajax_err
IntervalManager
:
IntervalManager
IntervalManager
:
IntervalManager
create_task_list_table
:
create_task_list_table
PendingInstructorTasks
:
PendingInstructorTasks
lms/static/sass/course/instructor/_instructor_2.scss
View file @
cb4025b1
...
@@ -14,9 +14,16 @@
...
@@ -14,9 +14,16 @@
.olddash-button-wrapper
{
.olddash-button-wrapper
{
position
:
absolute
;
position
:
absolute
;
top
:
1
7
px
;
top
:
1
6
px
;
right
:
15px
;
right
:
15px
;
@include
font-size
(
14
);
@include
font-size
(
16
);
}
.studio-edit-link
{
position
:
absolute
;
top
:
40px
;
right
:
15px
;
@include
font-size
(
16
);
}
}
// system feedback - messages
// system feedback - messages
...
...
lms/templates/instructor/instructor_dashboard_2/course_info.html
View file @
cb4025b1
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<
%
page
args=
"section_data"
/>
<
%
page
args=
"section_data"
/>
<h2>
${_("Course Information")}
</h2>
<div
class=
"enrollment-wrapper"
>
<h2>
${_("Enrollment Information")}
</h2>
<span
class=
"tip"
>
${_("Total number of enrollees (instructors, staff members, and students)")}
</span>
<br/><br/>
<span
style=
"color: green;"
><b>
${ section_data['enrollment_count'] }
</b></span>
<div
class=
"basic-data"
>
${_("Course Name")}:
${ section_data['course_display_name'] }
</div>
</div>
<hr>
<div
class=
"basic-data"
>
<div
class=
"basic-wrapper"
>
${_("Course ID")}:
<h2>
${_("Basic Course Information")}
</h2>
${ section_data['course_id'] }
</div>
<div
class=
"basic-data"
>
<ul
class=
"list-input"
>
${_("Students Enrolled")}:
<li
class=
"field text is-not-editable"
id=
"field-course-organization"
>
${ section_data['enrollment_count'] }
<label
for=
"course-organization"
>
${_("Organization:")}
</label>
</div>
<b>
${ section_data['course_org'] }
</b>
</li>
<div
class=
"basic-data
"
>
<li
class=
"field text is-not-editable"
id=
"field-course-number
"
>
${_("Started")}:
<label
for=
"course-number"
>
${_("Course Number:")}
</label>
${ section_data['has_started'] }
<b>
${ section_data['course_num'] }
</b>
</div
>
</li
>
<div
class=
"basic-data"
>
<li
class=
"field text is-not-editable"
id=
"field-course-name"
>
${_("Ended")}:
<label
for=
"course-name"
>
${_("Course Name:")}
</label>
${ section_data['has_ended'] }
<b>
${ section_data['course_name'] }
</b>
</div>
</li>
<li
class=
"field text is-not-editable"
id=
"field-course-display-name"
>
<label
for=
"course-display-name"
>
${_("Course Display Name:")}
</label>
<b>
${ section_data['course_display_name'] }
</b>
</li>
<li
class=
"field text is-not-editable"
id=
"field-course-started"
>
<label
for=
"start-date"
>
${_("Has the course started?")}
</label>
<div
class=
"basic-data"
>
<b>
${_("Yes") if section_data['grade_cutoffs'] else _("No")}
</b>
${_("Grade Cutoffs")}:
${ section_data['grade_cutoffs'] }
</li>
<li
class=
"field text is-not-editable"
id=
"field-course-ended"
>
<label
for=
"start-date"
>
${_("Has the course ended?")}
</label>
%if section_data['has_ended']:
<b>
${_("Yes")}
</b>
%else:
<b>
${_("No")}
</b>
%endif
</li>
<li
class=
"field text is-not-editable"
id=
"field-grade-cutoffs"
>
<label
for=
"start-date"
>
${_("Grade Cutoffs:")}
</label>
<b>
${ section_data['grade_cutoffs'] }
</b>
</li>
</ul>
</div>
</div>
##
<div
class=
"basic-data"
>
## Offline Grades Available:
## ${ section_data['offline_grades'] }
##
</div>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS')
and section_data['access']['instructor']
:
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
<div
class=
"running-tasks-container action-type-container"
>
<div
class=
"running-tasks-container action-type-container"
>
<hr>
<hr>
<h2>
${_("Pending Instructor Tasks")}
</h2>
<h2>
${_("Pending Instructor Tasks")}
</h2>
<p>
${_("The status for any active tasks appears in a table below.")}
</p>
<p>
${_("The status for any active tasks appears in a table below.")}
</p>
<br
/>
<div
class=
"running-tasks-table"
data-endpoint=
"${ section_data['list_instructor_tasks_url'] }"
></div>
<div
class=
"running-tasks-table"
data-endpoint=
"${ section_data['list_instructor_tasks_url'] }"
></div>
</div>
</div>
...
@@ -69,6 +90,3 @@
...
@@ -69,6 +90,3 @@
</div>
</div>
<br>
<br>
%endif
%endif
lms/templates/instructor/instructor_dashboard_2/data_download.html
View file @
cb4025b1
...
@@ -19,4 +19,16 @@
...
@@ -19,4 +19,16 @@
<div
class=
"data-display-text"
></div>
<div
class=
"data-display-text"
></div>
<div
class=
"data-display-table"
></div>
<div
class=
"data-display-table"
></div>
<div
class=
"request-response-error"
></div>
<div
class=
"request-response-error"
></div>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
<div
class=
"running-tasks-container action-type-container"
>
<hr>
<h2>
${_("Pending Instructor Tasks")}
</h2>
<p>
${_("The status for any active tasks appears in a table below.")}
</p>
<br
/>
<div
class=
"running-tasks-table"
data-endpoint=
"${ section_data['list_instructor_tasks_url'] }"
></div>
</div>
%endif
</div>
</div>
lms/templates/instructor/instructor_dashboard_2/instructor_dashboard_2.html
View file @
cb4025b1
...
@@ -50,10 +50,14 @@
...
@@ -50,10 +50,14 @@
<section
class=
"container"
>
<section
class=
"container"
>
<div
class=
"instructor-dashboard-wrapper-2"
>
<div
class=
"instructor-dashboard-wrapper-2"
>
<div
class=
"olddash-button-wrapper"
><a
href=
"${ old_dashboard_url }"
>
${_("Back to Standard Dashboard")}
</a></div>
<div
class=
"olddash-button-wrapper"
><a
href=
"${ old_dashboard_url }"
>
${_("Back to Standard Dashboard")}
</a></div>
%if studio_url:
## not checking access because if user can see this, they are at least course staff (with studio edit access)
<div
class=
"studio-edit-link"
><a
href=
"${studio_url}"
target=
"_blank"
>
${_('Edit Course In Studio')}
</a></div>
%endif
<section
class=
"instructor-dashboard-content-2"
>
<section
class=
"instructor-dashboard-content-2"
>
##
<h1>
Instructor Dashboard
</h1>
<h1>
${_("Instructor Dashboard")}
</h1>
<hr
/>
## links which are tied to idash-sections below.
## links which are tied to idash-sections below.
## the links are acativated and handled in instructor_dashboard.coffee
## the links are acativated and handled in instructor_dashboard.coffee
## when the javascript loads, it clicks on the first section
## when the javascript loads, it clicks on the first section
...
...
lms/templates/instructor/instructor_dashboard_2/send_email.html
View file @
cb4025b1
...
@@ -54,4 +54,16 @@
...
@@ -54,4 +54,16 @@
<br
/>
<br
/>
<input
type=
"button"
name=
"send"
value=
"${_("
Send
Email
")}"
data-endpoint=
"${ section_data['send_email'] }"
>
<input
type=
"button"
name=
"send"
value=
"${_("
Send
Email
")}"
data-endpoint=
"${ section_data['send_email'] }"
>
<div
class=
"request-response-error"
></div>
<div
class=
"request-response-error"
></div>
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
<div
class=
"running-tasks-container action-type-container"
>
<hr>
<h2>
${_("Pending Instructor Tasks")}
</h2>
<p>
${_("The status for any active tasks appears in a table below.")}
</p>
<br
/>
<div
class=
"running-tasks-table"
data-endpoint=
"${ section_data['list_instructor_tasks_url'] }"
></div>
</div>
%endif
</div>
</div>
lms/templates/instructor/instructor_dashboard_2/student_admin.html
View file @
cb4025b1
...
@@ -109,3 +109,15 @@
...
@@ -109,3 +109,15 @@
</p>
</p>
</div>
</div>
%endif
%endif
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BACKGROUND_TASKS'):
<div
class=
"running-tasks-container action-type-container"
>
<hr>
<h2>
${_("Pending Instructor Tasks")}
</h2>
<p>
${_("The status for any active tasks appears in a table below.")}
</p>
<br
/>
<div
class=
"running-tasks-table"
data-endpoint=
"${ section_data['list_instructor_tasks_url'] }"
></div>
</div>
%endif
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