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
bb7eed58
Commit
bb7eed58
authored
Jan 14, 2015
by
Carlos de la Guardia
Committed by
cewing
Apr 10, 2015
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
MIT: CCX. Refactore using backbone.js
parent
96ca1da0
Show whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
605 additions
and
455 deletions
+605
-455
lms/djangoapps/pocs/views.py
+10
-0
lms/envs/common.py
+6
-0
lms/static/js/pocs/schedule.js
+490
-0
lms/templates/pocs/coach_dashboard.html
+0
-11
lms/templates/pocs/schedule.html
+45
-444
lms/templates/pocs/schedule.underscore
+52
-0
lms/urls.py
+2
-0
No files found.
lms/djangoapps/pocs/views.py
View file @
bb7eed58
...
...
@@ -317,6 +317,16 @@ def get_poc_schedule(course, poc):
@ensure_csrf_cookie
@cache_control
(
no_cache
=
True
,
no_store
=
True
,
must_revalidate
=
True
)
@coach_dashboard
def
poc_schedule
(
request
,
course
):
poc
=
get_poc_for_coach
(
course
,
request
.
user
)
schedule
=
get_poc_schedule
(
course
,
poc
)
json_schedule
=
json
.
dumps
(
schedule
,
indent
=
4
)
return
HttpResponse
(
json_schedule
,
mimetype
=
'application/json'
)
@ensure_csrf_cookie
@cache_control
(
no_cache
=
True
,
no_store
=
True
,
must_revalidate
=
True
)
@coach_dashboard
def
poc_invite
(
request
,
course
):
"""
Invite users to new poc
...
...
lms/envs/common.py
View file @
bb7eed58
...
...
@@ -1203,6 +1203,8 @@ reverify_js = [
'js/verify_student/incourse_reverify.js'
,
]
pocs_js
=
sorted
(
rooted_glob
(
PROJECT_ROOT
/
'static'
,
'js/pocs/**/*.js'
))
PIPELINE_CSS
=
{
'style-vendor'
:
{
'source_filenames'
:
[
...
...
@@ -1396,6 +1398,10 @@ PIPELINE_JS = {
'reverify'
:
{
'source_filenames'
:
reverify_js
,
'output_filename'
:
'js/reverify.js'
},
'pocs'
:
{
'source_filenames'
:
pocs_js
,
'output_filename'
:
'js/pocs.js'
}
}
...
...
lms/static/js/pocs/schedule.js
0 → 100644
View file @
bb7eed58
var
edx
=
edx
||
{};
(
function
(
$
,
_
,
Backbone
,
gettext
)
{
'use strict'
;
edx
.
pocs
=
edx
.
pocs
||
{};
edx
.
pocs
.
schedule
=
edx
.
pocs
.
schedule
||
{};
var
syncErrorMessage
=
gettext
(
"The data could not be saved."
);
var
self
;
edx
.
pocs
.
schedule
.
reloadPage
=
function
()
{
location
.
reload
();
};
edx
.
pocs
.
schedule
.
UnitModel
=
Backbone
.
Model
.
extend
({
defaults
:
{
location
:
''
,
display_name
:
''
,
start
:
null
,
due
:
null
,
category
:
''
,
hidden
:
false
,
children
:
[]
},
});
edx
.
pocs
.
schedule
.
Schedule
=
Backbone
.
Collection
.
extend
({
model
:
edx
.
pocs
.
schedule
.
UnitModel
,
url
:
'poc_schedule'
});
edx
.
pocs
.
schedule
.
ScheduleView
=
Backbone
.
View
.
extend
({
initialize
:
function
()
{
_
.
bindAll
(
this
,
'render'
);
this
.
schedule_collection
=
new
edx
.
pocs
.
schedule
.
Schedule
();
this
.
schedule
=
{};
this
.
schedule_collection
.
bind
(
'reset'
,
this
.
render
);
this
.
schedule_collection
.
fetch
({
reset
:
true
});
this
.
chapter_select
=
$
(
'form#add-unit select[name="chapter"]'
),
this
.
sequential_select
=
$
(
'form#add-unit select[name="sequential"]'
),
this
.
vertical_select
=
$
(
'form#add-unit select[name="vertical"]'
);
this
.
dirty
=
false
;
self
=
this
;
$
(
'#add-all'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
this
.
schedule_apply
(
self
.
schedule
,
show
);
self
.
dirty
=
true
;
self
.
render
();
});
},
render
:
function
()
{
this
.
schedule
=
this
.
schedule_collection
.
toJSON
();
this
.
hidden
=
this
.
pruned
(
this
.
schedule
,
function
(
node
)
{
return
node
.
hidden
||
node
.
category
!==
'vertical'
});
this
.
showing
=
this
.
pruned
(
this
.
schedule
,
function
(
node
)
{
return
!
node
.
hidden
});
this
.
$el
.
html
(
schedule_template
({
chapters
:
this
.
showing
}));
$
(
'table.poc-schedule .sequential,.vertical'
).
hide
();
$
(
'table.poc-schedule .toggle-collapse'
).
on
(
'click'
,
this
.
toggle_collapse
);
//
// Hidden hover fields for empty date fields
$
(
'table.poc-schedule .date a'
).
each
(
function
()
{
if
(
!
$
(
this
).
text
())
{
$
(
this
).
text
(
'Set date'
).
addClass
(
'empty'
);
}
});
// Handle date edit clicks
$
(
'table.poc-schedule .date a'
).
attr
(
'href'
,
'#enter-date-modal'
)
.
leanModal
({
closeButton
:
'.close-modal'
});
$
(
'table.poc-schedule .due-date a'
).
on
(
'click'
,
this
.
enterNewDate
(
'due'
));
$
(
'table.poc-schedule .start-date a'
).
on
(
'click'
,
this
.
enterNewDate
(
'start'
));
// Click handler for remove all
$
(
'table.poc-schedule a#remove-all'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
this
.
schedule_apply
(
self
.
schedule
,
hide
);
self
.
dirty
=
true
;
self
.
render
();
});
// Show or hide form
if
(
this
.
hidden
.
length
)
{
// Populate chapters select, depopulate others
this
.
chapter_select
.
html
(
''
)
.
append
(
'<option value="none">'
+
gettext
(
"Select a chapter"
)
+
'...</option>'
)
.
append
(
this
.
schedule_options
(
this
.
hidden
));
this
.
sequential_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
this
.
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
$
(
'form#add-unit'
).
show
();
$
(
'#all-units-added'
).
hide
();
$
(
'#add-unit-button'
).
prop
(
'disabled'
,
true
);
}
else
{
$
(
'form#add-unit'
).
hide
();
$
(
'#all-units-added'
).
show
();
}
// Add unit handlers
this
.
chapter_select
.
on
(
'change'
,
function
(
event
)
{
var
chapter_location
=
self
.
chapter_select
.
val
();
self
.
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
if
(
chapter_location
!==
'none'
)
{
var
chapter
=
self
.
find_unit
(
self
.
hidden
,
chapter_location
);
self
.
sequential_select
.
html
(
''
)
.
append
(
'<option value="all">'
+
gettext
(
"All subsections"
)
+
'</option>'
)
.
append
(
self
.
schedule_options
(
chapter
.
children
));
self
.
sequential_select
.
prop
(
'disabled'
,
false
);
$
(
'#add-unit-button'
).
prop
(
'disabled'
,
false
);
self
.
set_datetime
(
'start'
,
chapter
.
start
);
self
.
set_datetime
(
'due'
,
chapter
.
due
);
}
else
{
self
.
sequential_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
}
});
this
.
sequential_select
.
on
(
'change'
,
function
(
event
)
{
var
sequential_location
=
self
.
sequential_select
.
val
();
if
(
sequential_location
!==
'all'
)
{
var
chapter
=
self
.
chapter_select
.
val
();
sequential
=
self
.
find_unit
(
self
.
hidden
,
chapter
,
sequential_location
);
self
.
vertical_select
.
html
(
''
)
.
append
(
'<option value="all">'
+
gettext
(
"All units"
)
+
'</option>'
)
.
append
(
schedule_options
(
sequential
.
children
));
self
.
vertical_select
.
prop
(
'disabled'
,
false
);
self
.
set_datetime
(
'start'
,
sequential
.
start
);
self
.
set_datetime
(
'due'
,
sequential
.
due
);
}
else
{
self
.
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
}
});
this
.
vertical_select
.
on
(
'change'
,
function
(
event
)
{
var
vertical_location
=
self
.
vertical_select
.
val
();
if
(
vertical_location
!==
'all'
)
{
var
chapter
=
chapter_select
.
val
(),
sequential
=
self
.
sequential_select
.
val
();
vertical
=
self
.
find_unit
(
self
.
hidden
,
chapter
,
sequential
,
vertical_location
);
self
.
set_datetime
(
'start'
,
vertical
.
start
);
self
.
set_datetime
(
'due'
,
vertical
.
due
);
}
});
// Add unit handler
$
(
'#add-unit-button'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
var
chapter
=
self
.
chapter_select
.
val
(),
sequential
=
self
.
sequential_select
.
val
(),
vertical
=
self
.
vertical_select
.
val
(),
units
=
self
.
find_lineage
(
self
.
schedule
,
chapter
,
sequential
==
'all'
?
null
:
sequential
,
vertical
==
'all'
?
null
:
vertical
),
start
=
self
.
get_datetime
(
'start'
),
due
=
self
.
get_datetime
(
'due'
);
units
.
map
(
show
);
unit
=
units
[
units
.
length
-
1
]
self
.
schedule_apply
([
unit
],
show
);
if
(
start
)
unit
.
start
=
start
;
if
(
due
)
unit
.
due
=
due
;
self
.
dirty
=
true
;
self
.
render
();
});
// Remove unit handler
$
(
'table.poc-schedule a.remove-unit'
).
on
(
'click'
,
function
(
event
)
{
var
row
=
$
(
this
).
closest
(
'tr'
),
path
=
row
.
data
(
'location'
).
split
(
' '
),
unit
=
self
.
find_unit
(
self
.
schedule
,
path
[
0
],
path
[
1
],
path
[
2
]);
self
.
schedule_apply
([
unit
],
self
.
hide
);
self
.
dirty
=
true
;
self
.
render
();
});
// Show or hide save button
if
(
this
.
dirty
)
$
(
'#dirty-schedule'
).
show
()
else
$
(
'#dirty-schedule'
).
hide
();
// Handle save button
$
(
'#dirty-schedule #save-changes'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
self
.
save
();
});
$
(
'#ajax-error'
).
hide
();
return
this
;
},
save
:
function
()
{
var
button
=
$
(
'#dirty-schedule #save-changes'
);
button
.
prop
(
'disabled'
,
true
).
text
(
gettext
(
"Saving"
)
+
'...'
);
$
.
ajax
({
url
:
save_url
,
type
:
'POST'
,
contentType
:
'application/json'
,
data
:
JSON
.
stringify
(
this
.
schedule
),
success
:
function
(
data
,
textStatus
,
jqXHR
)
{
self
.
schedule
=
data
.
schedule
;
self
.
dirty
=
false
;
self
.
render
();
button
.
prop
(
'disabled'
,
false
).
text
(
gettext
(
"Save changes"
));
// Update textarea with grading policy JSON, since grading policy
// may have changed.
$
(
'#grading-policy'
).
text
(
data
.
grading_policy
);
},
error
:
function
(
jqXHR
,
textStatus
,
error
)
{
console
.
log
(
jqXHR
.
responseText
);
$
(
'#ajax-error'
).
show
();
$
(
'#dirty-schedule'
).
hide
();
$
(
'form#add-unit select,input,button'
).
prop
(
'disabled'
,
true
);
}
});
},
hide
:
function
(
unit
)
{
unit
.
hidden
=
true
;
},
show
:
function
(
unit
)
{
unit
.
hidden
=
false
;
},
get_datetime
:
function
(
which
)
{
var
date
=
$
(
'form#add-unit input[name='
+
which
+
'_date]'
).
val
();
var
time
=
$
(
'form#add-unit input[name='
+
which
+
'_time]'
).
val
();
if
(
date
&&
time
)
return
date
+
' '
+
time
;
return
null
;
},
set_datetime
:
function
(
which
,
value
)
{
var
parts
=
value
?
value
.
split
(
' '
)
:
[
''
,
''
],
date
=
parts
[
0
],
time
=
parts
[
1
];
$
(
'form#add-unit input[name='
+
which
+
'_date]'
).
val
(
date
);
$
(
'form#add-unit input[name='
+
which
+
'_time]'
).
val
(
time
);
},
schedule_options
:
function
(
nodes
)
{
return
nodes
.
map
(
function
(
node
)
{
return
$
(
'<option>'
)
.
attr
(
'value'
,
node
.
location
)
.
text
(
node
.
display_name
)[
0
];
});
},
schedule_apply
:
function
(
nodes
,
f
)
{
nodes
.
map
(
function
(
node
)
{
f
(
node
);
if
(
node
.
children
!==
undefined
)
self
.
schedule_apply
(
node
.
children
,
f
);
});
},
pruned
:
function
(
tree
,
filter
)
{
return
tree
.
filter
(
filter
)
.
map
(
function
(
node
)
{
var
copy
=
{};
$
.
extend
(
copy
,
node
);
if
(
node
.
children
)
copy
.
children
=
self
.
pruned
(
node
.
children
,
filter
);
return
copy
;
})
.
filter
(
function
(
node
)
{
return
node
.
children
===
undefined
||
node
.
children
.
length
;
});
},
toggle_collapse
:
function
(
event
)
{
event
.
preventDefault
();
var
row
=
$
(
this
).
closest
(
'tr'
);
var
children
=
self
.
get_children
(
row
);
if
(
row
.
is
(
'.expanded'
))
{
$
(
this
).
removeClass
(
'icon-caret-down'
).
addClass
(
'icon-caret-right'
);
row
.
removeClass
(
'expanded'
).
addClass
(
'collapsed'
);
children
.
hide
();
}
else
{
$
(
this
).
removeClass
(
'icon-caret-right'
).
addClass
(
'icon-caret-down'
);
row
.
removeClass
(
'collapsed'
).
addClass
(
'expanded'
);
children
.
filter
(
'.collapsed'
).
each
(
function
()
{
children
=
children
.
not
(
self
.
get_children
(
this
));
});
children
.
show
();
}
},
enterNewDate
:
function
(
what
)
{
return
function
(
event
)
{
var
row
=
$
(
this
).
closest
(
'tr'
);
var
modal
=
$
(
'#enter-date-modal'
)
.
data
(
'what'
,
what
)
.
data
(
'location'
,
row
.
data
(
'location'
));
modal
.
find
(
'h2'
).
text
(
what
==
'due'
?
gettext
(
"Enter Due Date"
)
:
gettext
(
"Enter Start Date"
));
modal
.
find
(
'label'
).
text
(
row
.
find
(
'td:first'
).
text
());
var
path
=
row
.
data
(
'location'
).
split
(
' '
),
unit
=
self
.
find_unit
(
self
.
schedule
,
path
[
0
],
path
[
1
],
path
[
2
]),
parts
=
unit
[
what
]
?
unit
[
what
].
split
(
' '
)
:
[
''
,
''
],
date
=
parts
[
0
],
time
=
parts
[
1
];
modal
.
find
(
'input[name=date]'
).
val
(
date
);
modal
.
find
(
'input[name=time]'
).
val
(
time
);
modal
.
find
(
'form'
).
off
(
'submit'
).
on
(
'submit'
,
function
(
event
)
{
event
.
preventDefault
();
var
date
=
$
(
this
).
find
(
'input[name=date]'
).
val
(),
time
=
$
(
this
).
find
(
'input[name=time]'
).
val
();
var
valid_date
=
new
Date
(
date
);
if
(
isNaN
(
valid_date
.
valueOf
()))
{
alert
(
'Please enter a valid date'
);
return
;
}
var
valid_time
=
/^
\d{1,2}
:
\d{2}?
$/
;
if
(
!
time
.
match
(
valid_time
))
{
alert
(
'Please enter a valid time'
);
return
;
}
unit
[
what
]
=
date
+
' '
+
time
;
modal
.
find
(
'.close-modal'
).
click
();
self
.
dirty
=
true
;
self
.
render
();
});
}
},
find_unit
:
function
(
tree
,
chapter
,
sequential
,
vertical
)
{
var
units
=
self
.
find_lineage
(
tree
,
chapter
,
sequential
,
vertical
);
return
units
[
units
.
length
-
1
];
},
find_lineage
:
function
(
tree
,
chapter
,
sequential
,
vertical
)
{
function
find_in
(
seq
,
location
)
{
for
(
var
i
=
0
;
i
<
seq
.
length
;
i
++
)
if
(
seq
[
i
].
location
===
location
)
return
seq
[
i
];
}
var
units
=
[],
unit
=
find_in
(
tree
,
chapter
);
units
[
units
.
length
]
=
unit
;
if
(
sequential
)
{
units
[
units
.
length
]
=
unit
=
find_in
(
unit
.
children
,
sequential
);
if
(
vertical
)
units
[
units
.
length
]
=
unit
=
find_in
(
unit
.
children
,
vertical
);
}
return
units
;
},
get_children
:
function
(
row
)
{
var
depth
=
$
(
row
).
data
(
'depth'
);
return
$
(
row
).
nextUntil
(
$
(
row
).
siblings
().
filter
(
function
()
{
return
$
(
this
).
data
(
'depth'
)
<=
depth
;
})
);
}
});
edx
.
pocs
.
schedule
.
XScheduleView
=
Backbone
.
View
.
extend
({
events
:
{
'submit'
:
'submit'
,
'change'
:
'change'
},
initialize
:
function
()
{
_
.
bindAll
(
this
,
'render'
,
'change'
,
'submit'
,
'invalidProfile'
,
'invalidPreference'
,
'error'
,
'sync'
,
'clearStatus'
);
this
.
scheduleModel
=
new
edx
.
pocs
.
schedule
.
ProfileModel
();
this
.
scheduleModel
.
on
(
'invalid'
,
this
.
invalidProfile
);
this
.
scheduleModel
.
on
(
'error'
,
this
.
error
);
this
.
scheduleModel
.
on
(
'sync'
,
this
.
sync
);
this
.
preferencesModel
=
new
edx
.
pocs
.
schedule
.
PreferencesModel
();
this
.
preferencesModel
.
on
(
'invalid'
,
this
.
invalidPreference
);
this
.
preferencesModel
.
on
(
'error'
,
this
.
error
);
this
.
preferencesModel
.
on
(
'sync'
,
this
.
sync
);
},
render
:
function
()
{
this
.
$el
.
html
(
_
.
template
(
$
(
'#schedule-tpl'
).
html
()));
this
.
$nameField
=
$
(
'#schedule-name'
,
this
.
$el
);
this
.
$nameStatus
=
$
(
'#schedule-name-status'
,
this
.
$el
);
this
.
$languageChoices
=
$
(
'#preference-language'
,
this
.
$el
);
this
.
$languageStatus
=
$
(
'#preference-language-status'
,
this
.
$el
);
this
.
$submitStatus
=
$
(
'#submit-status'
,
this
.
$el
);
var
self
=
this
;
$
.
getJSON
(
'preferences/languages'
)
.
done
(
function
(
json
)
{
/** Asynchronously populate the language choices. */
self
.
$languageChoices
.
html
(
_
.
template
(
$
(
'#languages-tpl'
).
html
(),
{
languageInfo
:
json
}));
})
.
fail
(
function
()
{
self
.
$languageStatus
.
addClass
(
'language-list-error'
)
.
text
(
gettext
(
"We couldn't populate the list of language choices."
));
});
return
this
;
},
change
:
function
()
{
this
.
scheduleModel
.
set
({
fullName
:
this
.
$nameField
.
val
()
});
this
.
preferencesModel
.
set
({
language
:
this
.
$languageChoices
.
val
()
});
},
submit
:
function
(
event
)
{
event
.
preventDefault
();
this
.
clearStatus
();
this
.
scheduleModel
.
save
();
this
.
preferencesModel
.
save
();
},
invalidProfile
:
function
(
model
)
{
var
errors
=
model
.
validationError
;
if
(
errors
.
hasOwnProperty
(
'fullName'
))
{
this
.
$nameStatus
.
addClass
(
'validation-error'
)
.
text
(
errors
.
fullName
);
}
},
invalidPreference
:
function
(
model
)
{
var
errors
=
model
.
validationError
;
if
(
errors
.
hasOwnProperty
(
'language'
))
{
this
.
$languageStatus
.
addClass
(
'validation-error'
)
.
text
(
errors
.
language
);
}
},
error
:
function
(
error
)
{
this
.
$submitStatus
.
addClass
(
'error'
)
.
text
(
error
);
},
sync
:
function
()
{
this
.
$submitStatus
.
addClass
(
'success'
)
.
text
(
gettext
(
"Saved"
));
},
clearStatus
:
function
()
{
this
.
$nameStatus
.
removeClass
(
'validation-error'
)
.
text
(
""
);
this
.
$languageStatus
.
removeClass
(
'validation-error'
)
.
text
(
""
);
this
.
$submitStatus
.
removeClass
(
'error'
)
.
text
(
""
);
}
});
})(
jQuery
,
_
,
Backbone
,
gettext
);
lms/templates/pocs/coach_dashboard.html
View file @
bb7eed58
...
...
@@ -14,16 +14,6 @@
<
%
static:css
group=
'style-course'
/>
</
%
block>
<
%
block
name=
"jsextra"
>
<script
src=
"${static.url('js/vendor/timepicker/jquery.timepicker.js')}"
></script>
<link
rel=
"stylesheet"
type=
"text/css"
href=
"${static.url('js/vendor/timepicker/jquery.timepicker.css')}"
/>
<style>
.ui-timepicker-list
{
z-index
:
100000
;
}
.ui-datepicker
{
z-index
:
100000
!important
;
}
input
.date
,
input
.time
{
width
:
auto
!important
;
display
:
inline
!important
;
}
</style>
</
%
block>
<
%
include
file=
"/courseware/course_navigation.html"
args=
"active_page='poc_coach'"
/>
<section
class=
"container"
>
...
...
@@ -148,5 +138,4 @@
$
(
setup_tabs
);
$
(
setup_management_form
)
</script>
lms/templates/pocs/schedule.html
View file @
bb7eed58
<
%!
from
django
.
utils
.
translation
import
ugettext
as
_
%
>
<script
id=
"poc-schedule-template"
type=
"text/x-handlerbars-template"
>
<
table
class
=
"poc-schedule"
>
<
thead
>
<
tr
>
<
th
>
$
{
_
(
'Unit'
)}
<
/th
>
<
th
>
$
{
_
(
'Start Date'
)}
<
/th
>
<
th
>
$
{
_
(
'Due Date'
)}
<
/th
>
<
th
><
a
href
=
"#"
id
=
"remove-all"
>
<
i
class
=
"icon-remove-sign icon"
><
/i> ${_
(
'remove all'
)
}
<
/a></
th
>
<
/tr
>
<
/thead
>
<
tbody
>
{{
#
each
chapters
}}
<
tr
class
=
"chapter collapsed"
data
-
location
=
"{{location}}"
data
-
depth
=
"1"
>
<
td
class
=
"unit"
>
<
a
href
=
"#"
><
i
class
=
"icon-caret-right icon toggle-collapse"
><
/i></
a
>
{{
display_name
}}
<
/td
>
<
td
class
=
"date start-date"
><
a
>
{{
start
}}
<
/a></
td
>
<
td
class
=
"date due-date"
><
a
>
{{
due
}}
<
/a></
td
>
<
td
><
a
href
=
"#"
class
=
"remove-unit"
>
<
i
class
=
"icon-remove-sign icon"
><
/i> ${_
(
'remove'
)
}
<
/a></
td
>
<
/tr
>
{{
#
each
children
}}
<
tr
class
=
"sequential collapsed"
data
-
depth
=
"2"
data
-
location
=
"{{../location}} {{location}}"
>
<
td
class
=
"unit"
>
<
a
href
=
"#"
><
i
class
=
"icon-caret-right icon toggle-collapse"
><
/i></
a
>
{{
display_name
}}
<
/td
>
<
td
class
=
"date start-date"
><
a
>
{{
start
}}
<
/a></
td
>
<
td
class
=
"date due-date"
><
a
>
{{
due
}}
<
/a></
td
>
<
td
><
a
href
=
"#"
class
=
"remove-unit"
>
<
i
class
=
"icon-remove-sign icon"
><
/i> ${_
(
'remove'
)
}
<
/a></
td
>
<
/tr
>
{{
#
each
children
}}
<
tr
class
=
"vertical"
data
-
dapth
=
"3"
data
-
location
=
"{{../../location}} {{../location}} {{location}}"
>
<
td
class
=
"unit"
>&
nbsp
;{{
display_name
}}
<
/td
>
<
td
class
=
"date start-date"
><
a
>
{{
start
}}
<
/a></
td
>
<
td
class
=
"date due-date"
><
a
>
{{
due
}}
<
/a></
td
>
<
td
><
a
href
=
"#"
class
=
"remove-unit"
>
<
i
class
=
"icon-remove-sign icon"
><
/i> ${_
(
'remove'
)
}
<
/a></
td
>
{{
/
each
}}
{{
/each}
}
{{
/
each
}}
<
/tbody
>
<
/table
>
</script>
<
%
namespace
name=
'static'
file=
'/static_content.html'
/>
<
%
block
name=
"jsextra"
>
<script>
var
save_url
=
'${save_url}'
;
var
schedule
=
$
{
schedule
};
</script>
<script
src=
"${static.url('js/vendor/backbone-min.js')}"
></script>
<script
src=
"${static.url('js/vendor/timepicker/jquery.timepicker.js')}"
></script>
<link
rel=
"stylesheet"
type=
"text/css"
href=
"${static.url('js/vendor/timepicker/jquery.timepicker.css')}"
/>
<style>
.ui-timepicker-list
{
z-index
:
100000
;
}
.ui-datepicker
{
z-index
:
100000
!important
;
}
input
.date
,
input
.time
{
width
:
auto
!important
;
display
:
inline
!important
;
}
</style>
<
%
static:js
group=
'pocs'
/>
</
%
block>
%for template_name in ["schedule"]:
<script
type=
"text/template"
id=
"poc-${template_name}-template"
>
<%
static
:
include
path
=
"pocs/${template_name}.underscore"
/>
</script>
%endfor
<div
class=
"poc-schedule-container"
>
<div
id=
"poc-schedule"
></div>
<div
id=
"new-poc-schedule"
></div>
</div>
<section
id=
"enter-date-modal"
class=
"modal"
aria-hidden=
"true"
>
...
...
@@ -70,7 +41,7 @@
<h2></h2>
</header>
<form
role=
"form"
>
<div
class=
"field
"
id=
"
datepair"
>
<div
class=
"field
datepair"
>
<label></label>
<input
placeholder=
"Date"
class=
"date"
type=
"text"
name=
"date"
/
size=
"11"
>
<input
placeholder=
"Time"
class=
"time"
type=
"text"
name=
"time"
/
size=
"6"
>
...
...
@@ -101,26 +72,26 @@
<h2>
${_('Schedule a Unit')}
</h2>
<form
role=
"form"
id=
"add-unit"
name=
"add-unit"
class=
"poc-form"
>
<div
class=
"field"
>
<b>
${_('
Chapter
')}
</b><br/>
<b>
${_('
Section
')}
</b><br/>
<select
name=
"chapter"
></select>
</div>
<div
class=
"field"
>
<b>
${_('S
equential
')}
</b><br/>
<b>
${_('S
ubsection
')}
</b><br/>
<select
name=
"sequential"
></select>
</div>
<div
class=
"field"
>
<b>
${_('
Vertical
')}
</b><br/>
<b>
${_('
Unit
')}
</b><br/>
<select
name=
"vertical"
></select>
</div>
<div
class=
"field"
>
<div
class=
"field
datepair
"
>
<b>
${_('Start Date')}
</b><br/>
<input
type
=
"date"
name=
"start_date"
/>
<input
type
=
"time"
name=
"start_time"
/>
<input
placeholder=
"Date"
type=
"date"
class
=
"date"
name=
"start_date"
/>
<input
placeholder=
"time"
type=
"time"
class
=
"time"
name=
"start_time"
/>
</div>
<div
class=
"field"
>
<div
class=
"field
datepair
"
>
<b>
${_('Due Date')}
</b>
${_('(Optional)')}
<br/>
<input
type
=
"date"
name=
"due_date"
/>
<input
type
=
"time"
name=
"due_time"
/>
<input
placeholder=
"Date"
type=
"date"
class
=
"date"
name=
"due_date"
/>
<input
placeholder=
"time"
type=
"time"
class
=
"time"
name=
"due_time"
/>
</div>
<div
class=
"field"
>
<br/>
...
...
@@ -138,390 +109,20 @@
</div>
<script>
$
(
'#datepair .time'
).
timepicker
({
$
(
function
()
{
schedule_template
=
_
.
template
(
$
(
'#poc-schedule-template'
).
html
());
var
view
=
new
edx
.
pocs
.
schedule
.
ScheduleView
({
el
:
$
(
'#new-poc-schedule'
)
});
view
.
render
();
//poc_schedule.render();
$
(
'.datepair .time'
).
timepicker
({
'showDuration'
:
true
,
'timeFormat'
:
'G:i'
});
$
(
'#
datepair .date'
).
datepicker
({
});
$
(
'.
datepair .date'
).
datepicker
({
'dateFormat'
:
'yy-mm-dd'
,
'autoclose'
:
true
});
var
poc_schedule
=
(
function
()
{
var
save_url
=
'${save_url}'
;
var
schedule
=
$
{
schedule
};
var
template
=
Handlebars
.
compile
(
$
(
'#poc-schedule-template'
).
html
());
var
chapter_select
=
$
(
'form#add-unit select[name="chapter"]'
),
sequential_select
=
$
(
'form#add-unit select[name="sequential"]'
),
vertical_select
=
$
(
'form#add-unit select[name="vertical"]'
);
var
self
=
{
schedule
:
schedule
,
dirty
:
false
};
function
hide
(
unit
)
{
unit
.
hidden
=
true
;
}
function
show
(
unit
)
{
unit
.
hidden
=
false
;
}
function
get_datetime
(
which
)
{
var
date
=
$
(
'form#add-unit input[name='
+
which
+
'_date]'
).
val
();
var
time
=
$
(
'form#add-unit input[name='
+
which
+
'_time]'
).
val
();
if
(
date
&&
time
)
return
date
+
' '
+
time
;
return
null
;
}
function
set_datetime
(
which
,
value
)
{
var
parts
=
value
?
value
.
split
(
' '
)
:
[
''
,
''
],
date
=
parts
[
0
],
time
=
parts
[
1
];
$
(
'form#add-unit input[name='
+
which
+
'_date]'
).
val
(
date
);
$
(
'form#add-unit input[name='
+
which
+
'_time]'
).
val
(
time
);
}
/**
* Render the course tree view.
*/
self
.
render
=
function
()
{
this
.
hidden
=
pruned
(
this
.
schedule
,
function
(
node
)
{
return
node
.
hidden
||
node
.
category
!==
'vertical'
});
this
.
showing
=
pruned
(
this
.
schedule
,
function
(
node
)
{
return
!
node
.
hidden
});
// Render template
$
(
'#poc-schedule'
).
html
(
template
({
chapters
:
this
.
showing
}));
// Start collapsed
$
(
'table.poc-schedule .sequential,.vertical'
).
hide
();
// Click handlers for collapsible tree
$
(
'table.poc-schedule .toggle-collapse'
).
on
(
'click'
,
toggle_collapse
);
// Hidden hover fields for empty date fields
$
(
'table.poc-schedule .date a'
).
each
(
function
()
{
if
(
!
$
(
this
).
text
())
{
$
(
this
).
text
(
'Set date'
).
addClass
(
'empty'
);
}
});
// Handle date edit clicks
$
(
'table.poc-schedule .date a'
).
attr
(
'href'
,
'#enter-date-modal'
)
.
leanModal
({
closeButton
:
'.close-modal'
});
$
(
'table.poc-schedule .due-date a'
).
on
(
'click'
,
enterNewDate
(
'due'
));
$
(
'table.poc-schedule .start-date a'
).
on
(
'click'
,
enterNewDate
(
'start'
));
// Click handler for remove all
$
(
'table.poc-schedule a#remove-all'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
schedule_apply
(
self
.
schedule
,
hide
);
self
.
dirty
=
true
;
self
.
render
();
});
// Show or hide form
if
(
this
.
hidden
.
length
)
{
// Populate chapters select, depopulate others
chapter_select
.
html
(
''
)
.
append
(
'<option value="none">${_("Select a chapter")}...</option>'
)
.
append
(
schedule_options
(
this
.
hidden
));
sequential_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
$
(
'form#add-unit'
).
show
();
$
(
'#all-units-added'
).
hide
();
$
(
'#add-unit-button'
).
prop
(
'disabled'
,
true
);
}
else
{
$
(
'form#add-unit'
).
hide
();
$
(
'#all-units-added'
).
show
();
}
// Add unit handlers
chapter_select
.
on
(
'change'
,
function
(
event
)
{
var
chapter_location
=
chapter_select
.
val
();
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
if
(
chapter_location
!==
'none'
)
{
chapter
=
find_unit
(
self
.
hidden
,
chapter_location
);
sequential_select
.
html
(
''
)
.
append
(
'<option value="all">${_("All sequentials")}</option>'
)
.
append
(
schedule_options
(
chapter
.
children
));
sequential_select
.
prop
(
'disabled'
,
false
);
$
(
'#add-unit-button'
).
prop
(
'disabled'
,
false
);
set_datetime
(
'start'
,
chapter
.
start
);
set_datetime
(
'due'
,
chapter
.
due
);
}
else
{
sequential_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
}
});
sequential_select
.
on
(
'change'
,
function
(
event
)
{
var
sequential_location
=
sequential_select
.
val
();
if
(
sequential_location
!==
'all'
)
{
var
chapter
=
chapter_select
.
val
();
sequential
=
find_unit
(
self
.
hidden
,
chapter
,
sequential_location
);
vertical_select
.
html
(
''
)
.
append
(
'<option value="all">${_("All verticals")}</option>'
)
.
append
(
schedule_options
(
sequential
.
children
));
vertical_select
.
prop
(
'disabled'
,
false
);
set_datetime
(
'start'
,
sequential
.
start
);
set_datetime
(
'due'
,
sequential
.
due
);
}
else
{
vertical_select
.
html
(
''
).
prop
(
'disabled'
,
true
);
}
});
vertical_select
.
on
(
'change'
,
function
(
event
)
{
var
vertical_location
=
vertical_select
.
val
();
if
(
vertical_location
!==
'all'
)
{
var
chapter
=
chapter_select
.
val
(),
sequential
=
sequential_select
.
val
();
vertical
=
find_unit
(
self
.
hidden
,
chapter
,
sequential
,
vertical_location
);
set_datetime
(
'start'
,
vertical
.
start
);
set_datetime
(
'due'
,
vertical
.
due
);
}
});
// Add unit handler
$
(
'#add-unit-button'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
var
chapter
=
chapter_select
.
val
(),
sequential
=
sequential_select
.
val
(),
vertical
=
vertical_select
.
val
(),
units
=
find_lineage
(
self
.
schedule
,
chapter
,
sequential
==
'all'
?
null
:
sequential
,
vertical
==
'all'
?
null
:
vertical
),
start
=
get_datetime
(
'start'
),
due
=
get_datetime
(
'due'
);
units
.
map
(
show
);
unit
=
units
[
units
.
length
-
1
]
schedule_apply
([
unit
],
show
);
if
(
start
)
unit
.
start
=
start
;
if
(
due
)
unit
.
due
=
due
;
self
.
dirty
=
true
;
self
.
render
();
});
// Remove unit handler
$
(
'table.poc-schedule a.remove-unit'
).
on
(
'click'
,
function
(
event
)
{
var
row
=
$
(
this
).
closest
(
'tr'
),
path
=
row
.
data
(
'location'
).
split
(
' '
),
unit
=
find_unit
(
self
.
schedule
,
path
[
0
],
path
[
1
],
path
[
2
]);
schedule_apply
([
unit
],
hide
);
self
.
dirty
=
true
;
self
.
render
();
});
// Show or hide save button
if
(
this
.
dirty
)
$
(
'#dirty-schedule'
).
show
()
else
$
(
'#dirty-schedule'
).
hide
();
// Handle save button
$
(
'#dirty-schedule #save-changes'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
self
.
save
();
});
$
(
'#ajax-error'
).
hide
();
}
/**
* Handle date entry.
*/
function
enterNewDate
(
what
)
{
return
function
(
event
)
{
var
row
=
$
(
this
).
closest
(
'tr'
);
var
modal
=
$
(
'#enter-date-modal'
)
.
data
(
'what'
,
what
)
.
data
(
'location'
,
row
.
data
(
'location'
));
modal
.
find
(
'h2'
).
text
(
what
==
'due'
?
'${_("Enter Due Date")}'
:
'${_("Enter Start Date")}'
);
modal
.
find
(
'label'
).
text
(
row
.
find
(
'td:first'
).
text
());
var
path
=
row
.
data
(
'location'
).
split
(
' '
),
unit
=
find_unit
(
self
.
schedule
,
path
[
0
],
path
[
1
],
path
[
2
]),
parts
=
unit
[
what
]
?
unit
[
what
].
split
(
' '
)
:
[
''
,
''
],
date
=
parts
[
0
],
time
=
parts
[
1
];
modal
.
find
(
'input[name=date]'
).
val
(
date
);
modal
.
find
(
'input[name=time]'
).
val
(
time
);
modal
.
find
(
'form'
).
off
(
'submit'
).
on
(
'submit'
,
function
(
event
)
{
event
.
preventDefault
();
var
date
=
$
(
this
).
find
(
'input[name=date]'
).
val
(),
time
=
$
(
this
).
find
(
'input[name=time]'
).
val
();
var
valid_date
=
new
Date
(
date
);
if
(
isNaN
(
valid_date
.
valueOf
()))
{
alert
(
'Please enter a valid date'
);
return
;
}
var
valid_time
=
/^
\d{1,2}
:
\d{2}?
$/
;
if
(
!
time
.
match
(
valid_time
))
{
alert
(
'Please enter a valid time'
);
return
;
}
unit
[
what
]
=
date
+
' '
+
time
;
modal
.
find
(
'.close-modal'
).
click
();
self
.
dirty
=
true
;
self
.
render
();
});
}
}
/**
* Returns a sequence of <option> DOM elements for a particular sequence
* of schedule nodes.
*/
function
schedule_options
(
nodes
)
{
return
nodes
.
map
(
function
(
node
)
{
return
$
(
'<option>'
)
.
attr
(
'value'
,
node
.
location
)
.
text
(
node
.
display_name
)[
0
];
});
}
/**
* Return a schedule pruned by the given filter function.
*/
function
pruned
(
tree
,
filter
)
{
return
tree
.
filter
(
filter
)
.
map
(
function
(
node
)
{
var
copy
=
{};
$
.
extend
(
copy
,
node
);
if
(
node
.
children
)
copy
.
children
=
pruned
(
node
.
children
,
filter
);
return
copy
;
})
.
filter
(
function
(
node
)
{
return
node
.
children
===
undefined
||
node
.
children
.
length
;
});
}
/**
* Get table rows that represent the children of given row in the schedule
* tree.
*/
function
get_children
(
row
)
{
var
depth
=
$
(
row
).
data
(
'depth'
);
return
$
(
row
).
nextUntil
(
$
(
row
).
siblings
().
filter
(
function
()
{
return
$
(
this
).
data
(
'depth'
)
<=
depth
;
})
);
}
/**
* Handle click event for expanding/collapsing nodes in the schedule tree.
*/
function
toggle_collapse
(
event
)
{
event
.
preventDefault
();
var
row
=
$
(
this
).
closest
(
'tr'
);
var
children
=
get_children
(
row
);
if
(
row
.
is
(
'.expanded'
))
{
$
(
this
).
removeClass
(
'icon-caret-down'
).
addClass
(
'icon-caret-right'
);
row
.
removeClass
(
'expanded'
).
addClass
(
'collapsed'
);
children
.
hide
();
}
else
{
$
(
this
).
removeClass
(
'icon-caret-right'
).
addClass
(
'icon-caret-down'
);
row
.
removeClass
(
'collapsed'
).
addClass
(
'expanded'
);
children
.
filter
(
'.collapsed'
).
each
(
function
()
{
children
=
children
.
not
(
get_children
(
this
));
});
children
.
show
();
}
}
/**
* Save changes.
*/
self
.
save
=
function
()
{
var
button
=
$
(
'#dirty-schedule #save-changes'
);
button
.
prop
(
'disabled'
,
true
).
text
(
'${_("Saving")}...'
);
$
.
ajax
({
url
:
save_url
,
type
:
'POST'
,
contentType
:
'application/json'
,
data
:
JSON
.
stringify
(
this
.
schedule
),
success
:
function
(
data
,
textStatus
,
jqXHR
)
{
self
.
schedule
=
data
.
schedule
;
self
.
dirty
=
false
;
self
.
render
();
button
.
prop
(
'disabled'
,
false
).
text
(
'${_("Save changes")}'
);
// Update textarea with grading policy JSON, since grading policy
// may have changed.
$
(
'#grading-policy'
).
text
(
data
.
grading_policy
);
},
error
:
function
(
jqXHR
,
textStatus
,
error
)
{
console
.
log
(
jqXHR
.
responseText
);
$
(
'#ajax-error'
).
show
();
$
(
'#dirty-schedule'
).
hide
();
$
(
'form#add-unit select,input,button'
).
prop
(
'disabled'
,
true
);
}
});
}
/**
* Add all nodes.
*/
$
(
'#add-all'
).
on
(
'click'
,
function
(
event
)
{
event
.
preventDefault
();
schedule_apply
(
self
.
schedule
,
show
);
self
.
dirty
=
true
;
self
.
render
();
});
/**
* Visit every tree node, applying function.
*/
function
schedule_apply
(
nodes
,
f
)
{
nodes
.
map
(
function
(
node
)
{
f
(
node
);
if
(
node
.
children
!==
undefined
)
schedule_apply
(
node
.
children
,
f
);
});
}
/**
* Find a unit in the tree.
*/
function
find_unit
(
tree
,
chapter
,
sequential
,
vertical
)
{
var
units
=
find_lineage
(
tree
,
chapter
,
sequential
,
vertical
);
return
units
[
units
.
length
-
1
];
}
function
find_lineage
(
tree
,
chapter
,
sequential
,
vertical
)
{
function
find_in
(
seq
,
location
)
{
for
(
var
i
=
0
;
i
<
seq
.
length
;
i
++
)
if
(
seq
[
i
].
location
===
location
)
return
seq
[
i
];
}
var
units
=
[],
unit
=
find_in
(
tree
,
chapter
);
units
[
units
.
length
]
=
unit
;
if
(
sequential
)
{
units
[
units
.
length
]
=
unit
=
find_in
(
unit
.
children
,
sequential
);
if
(
vertical
)
units
[
units
.
length
]
=
unit
=
find_in
(
unit
.
children
,
vertical
);
}
return
units
;
}
return
self
;
})();
$
(
function
()
{
poc_schedule
.
render
();
});
});
</script>
lms/templates/pocs/schedule.underscore
0 → 100644
View file @
bb7eed58
<table class="poc-schedule">
<thead>
<tr>
<th><%- gettext('Unit') %></th>
<th><%- gettext('Start Date') %></th>
<th><%- gettext('Due Date') %></th>
<th><a href="#" id="remove-all">
<i class="icon-remove-sign icon"></i> <%- gettext('remove all') %>
</a></th>
</tr>
</thead>
<tbody>
<% _.each(chapters, function(chapter) { %>
<tr class="chapter collapsed" data-location="<%= chapter.location %>" data-depth="1">
<td class="unit">
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<%= chapter.display_name %>
</td>
<td class="date start-date"><a><%= chapter.start %></a></td>
<td class="date due-date"><a><%= chapter.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="icon-remove-sign icon"></i> <%- gettext('remove') %>
</a></td>
</tr>
<% _.each(chapter.children, function(child) { %>
<tr class="sequential collapsed" data-depth="2"
data-location="<%= chapter.location %> <%= child.location %>">
<td class="unit">
<a href="#"><i class="icon-caret-right icon toggle-collapse"></i></a>
<%= child.display_name %>
</td>
<td class="date start-date"><a><%= child.start %></a></td>
<td class="date due-date"><a><%= child.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="icon-remove-sign icon"></i> <%- gettext('remove') %>
</a></td>
</tr>
<% _.each(child.children, function(subchild) { %>
<tr class="vertical" data-dapth="3"
data-location="<%= chapter.location %> <%= child.location %> <%= subchild.location %>">
<td class="unit"> <%= subchild.display_name %></td>
<td class="date start-date"><a><%= subchild.start %></a></td>
<td class="date due-date"><a><%= subchild.due %></a></td>
<td><a href="#" class="remove-unit">
<i class="icon-remove-sign icon"></i> <%- gettext('remove') %>
</a></td>
<% }); %>
<% }); %>
<% }); %>
</tbody>
</table>
lms/urls.py
View file @
bb7eed58
...
...
@@ -351,6 +351,8 @@ if settings.COURSEWARE_ENABLED:
'pocs.views.save_poc'
,
name
=
'save_poc'
),
url
(
r'^courses/{}/poc_invite$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'pocs.views.poc_invite'
,
name
=
'poc_invite'
),
url
(
r'^courses/{}/poc_schedule$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'pocs.views.poc_schedule'
,
name
=
'poc_schedule'
),
url
(
r'^courses/{}/poc_manage_student$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
'pocs.views.poc_student_management'
,
name
=
'poc_manage_student'
),
url
(
r'^courses/{}/poc_gradebook$'
.
format
(
settings
.
COURSE_ID_PATTERN
),
...
...
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