Merge pull request #1094 from MITx/feature/victor/instructor-grading
Feature/victor/instructor grading
This commit is contained in:
@@ -1,63 +0,0 @@
|
||||
/* IE 6 & 7 */
|
||||
|
||||
/* Proper fixed width for dashboard in IE6 */
|
||||
|
||||
.dashboard #content {
|
||||
*width: 768px;
|
||||
}
|
||||
|
||||
.dashboard #content-main {
|
||||
*width: 535px;
|
||||
}
|
||||
|
||||
/* IE 6 ONLY */
|
||||
|
||||
/* Keep header from flowing off the page */
|
||||
|
||||
#container {
|
||||
_position: static;
|
||||
}
|
||||
|
||||
/* Put the right sidebars back on the page */
|
||||
|
||||
.colMS #content-related {
|
||||
_margin-right: 0;
|
||||
_margin-left: 10px;
|
||||
_position: static;
|
||||
}
|
||||
|
||||
/* Put the left sidebars back on the page */
|
||||
|
||||
.colSM #content-related {
|
||||
_margin-right: 10px;
|
||||
_margin-left: -115px;
|
||||
_position: static;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
_height: 1%;
|
||||
}
|
||||
|
||||
/* Fix right margin for changelist filters in IE6 */
|
||||
|
||||
#changelist-filter ul {
|
||||
_margin-right: -10px;
|
||||
}
|
||||
|
||||
/* IE ignores min-height, but treats height as if it were min-height */
|
||||
|
||||
.change-list .filtered {
|
||||
_height: 400px;
|
||||
}
|
||||
|
||||
/* IE doesn't know alpha transparency in PNGs */
|
||||
|
||||
.inline-deletelink {
|
||||
background: transparent url(../img/inline-delete-8bit.png) no-repeat;
|
||||
}
|
||||
|
||||
/* IE7 doesn't support inline-block */
|
||||
.change-list ul.toplinks li {
|
||||
zoom: 1;
|
||||
*display: inline;
|
||||
}
|
||||
266
lms/static/coffee/src/staff_grading/staff_grading.coffee
Normal file
266
lms/static/coffee/src/staff_grading/staff_grading.coffee
Normal file
@@ -0,0 +1,266 @@
|
||||
# wrap everything in a class in case we want to use inside xmodules later
|
||||
|
||||
get_random_int: (min, max) ->
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min
|
||||
|
||||
# states
|
||||
state_grading = "grading"
|
||||
state_graded = "graded"
|
||||
state_no_data = "no_data"
|
||||
state_error = "error"
|
||||
|
||||
class StaffGradingBackend
|
||||
constructor: (ajax_url, mock_backend) ->
|
||||
@ajax_url = ajax_url
|
||||
@mock_backend = mock_backend
|
||||
if @mock_backend
|
||||
@mock_cnt = 0
|
||||
|
||||
mock: (cmd, data) ->
|
||||
# Return a mock response to cmd and data
|
||||
# should take a location as an argument
|
||||
if cmd == 'get_next'
|
||||
@mock_cnt++
|
||||
response =
|
||||
success: true
|
||||
problem_name: 'Problem 1'
|
||||
num_left: 3
|
||||
num_total: 5
|
||||
prompt: 'This is a fake prompt'
|
||||
submission: 'submission! ' + @mock_cnt
|
||||
rubric: 'A rubric! ' + @mock_cnt
|
||||
submission_id: @mock_cnt
|
||||
max_score: 2 + @mock_cnt % 3
|
||||
ml_error_info : 'ML accuracy info: ' + @mock_cnt
|
||||
|
||||
else if cmd == 'save_grade'
|
||||
console.log("eval: #{data.score} pts, Feedback: #{data.feedback}")
|
||||
response =
|
||||
@mock('get_next', {})
|
||||
# get_probblem_list
|
||||
# sends in a course_id and a grader_id
|
||||
# should get back a list of problem_ids, problem_names, num_left, num_total
|
||||
else if cmd == 'get_problem_list'
|
||||
response =
|
||||
success: true
|
||||
problem_list: [
|
||||
{location: 'i4x://MITx/3.091x/problem/open_ended_demo', \
|
||||
problem_name: "Problem 1", num_left: 3, num_total: 5},
|
||||
{location: 'i4x://MITx/3.091x/problem/open_ended_demo', \
|
||||
problem_name: "Problem 2", num_left: 1, num_total: 5}
|
||||
]
|
||||
else
|
||||
response =
|
||||
success: false
|
||||
error: 'Unknown command ' + cmd
|
||||
|
||||
if @mock_cnt % 5 == 0
|
||||
response =
|
||||
success: true
|
||||
message: 'No more submissions'
|
||||
|
||||
|
||||
if @mock_cnt % 7 == 0
|
||||
response =
|
||||
success: false
|
||||
error: 'An error for testing'
|
||||
|
||||
return response
|
||||
|
||||
|
||||
post: (cmd, data, callback) ->
|
||||
if @mock_backend
|
||||
callback(@mock(cmd, data))
|
||||
else
|
||||
# TODO: replace with postWithPrefix when that's loaded
|
||||
$.post(@ajax_url + cmd, data, callback)
|
||||
|
||||
|
||||
class StaffGrading
|
||||
constructor: (backend) ->
|
||||
@backend = backend
|
||||
|
||||
# all the jquery selectors
|
||||
@error_container = $('.error-container')
|
||||
@message_container = $('.message-container')
|
||||
|
||||
@prompt_container = $('.prompt-container')
|
||||
@prompt_wrapper = $('.prompt-wrapper')
|
||||
|
||||
@submission_container = $('.submission-container')
|
||||
@submission_wrapper = $('.submission-wrapper')
|
||||
|
||||
@rubric_container = $('.rubric-container')
|
||||
@rubric_wrapper = $('.rubric-wrapper')
|
||||
|
||||
@feedback_area = $('.feedback-area')
|
||||
@score_selection_container = $('.score-selection-container')
|
||||
@submit_button = $('.submit-button')
|
||||
@ml_error_info_container = $('.ml-error-info-container')
|
||||
|
||||
# model state
|
||||
@state = state_no_data
|
||||
@submission_id = null
|
||||
@prompt = ''
|
||||
@submission = ''
|
||||
@rubric = ''
|
||||
@error_msg = ''
|
||||
@message = ''
|
||||
@max_score = 0
|
||||
@ml_error_info= ''
|
||||
|
||||
@score = null
|
||||
|
||||
# action handlers
|
||||
@submit_button.click @submit
|
||||
|
||||
# render intial state
|
||||
@render_view()
|
||||
|
||||
# send initial request automatically
|
||||
@get_next_submission()
|
||||
|
||||
|
||||
setup_score_selection: =>
|
||||
# first, get rid of all the old inputs, if any.
|
||||
@score_selection_container.html('Choose score: ')
|
||||
|
||||
# Now create new labels and inputs for each possible score.
|
||||
for score in [0..@max_score]
|
||||
id = 'score-' + score
|
||||
label = """<label for="#{id}">#{score}</label>"""
|
||||
|
||||
input = """
|
||||
<input type="radio" name="score-selection" id="#{id}" value="#{score}"/>
|
||||
""" # " fix broken parsing in emacs
|
||||
@score_selection_container.append(input + label)
|
||||
|
||||
# And now hook up an event handler again
|
||||
$("input[name='score-selection']").change @graded_callback
|
||||
|
||||
|
||||
set_button_text: (text) =>
|
||||
@submit_button.attr('value', text)
|
||||
|
||||
graded_callback: (event) =>
|
||||
@score = event.target.value
|
||||
@state = state_graded
|
||||
@render_view()
|
||||
|
||||
ajax_callback: (response) =>
|
||||
# always clear out errors and messages on transition.
|
||||
@error_msg = ''
|
||||
@message = ''
|
||||
|
||||
if response.success
|
||||
if response.submission
|
||||
@data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info)
|
||||
else
|
||||
@no_more(response.message)
|
||||
else
|
||||
@error(response.error)
|
||||
|
||||
@render_view()
|
||||
|
||||
get_next_submission: () ->
|
||||
@backend.post('get_next', {}, @ajax_callback)
|
||||
|
||||
submit_and_get_next: () ->
|
||||
data =
|
||||
score: @score
|
||||
feedback: @feedback_area.val()
|
||||
submission_id: @submission_id
|
||||
|
||||
@backend.post('save_grade', data, @ajax_callback)
|
||||
|
||||
error: (msg) ->
|
||||
@error_msg = msg
|
||||
@state = state_error
|
||||
|
||||
data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info) ->
|
||||
@prompt = prompt
|
||||
@submission = submission
|
||||
@rubric = rubric
|
||||
@submission_id = submission_id
|
||||
@feedback_area.val('')
|
||||
@max_score = max_score
|
||||
@score = null
|
||||
@ml_error_info=ml_error_info
|
||||
@state = state_grading
|
||||
if not @max_score?
|
||||
@error("No max score specified for submission.")
|
||||
|
||||
no_more: (message) ->
|
||||
@prompt = null
|
||||
@submission = null
|
||||
@rubric = null
|
||||
@ml_error_info = null
|
||||
@submission_id = null
|
||||
@message = message
|
||||
@score = null
|
||||
@max_score = 0
|
||||
@state = state_no_data
|
||||
|
||||
render_view: () ->
|
||||
# make the view elements match the state. Idempotent.
|
||||
show_grading_elements = false
|
||||
show_submit_button = true
|
||||
|
||||
@message_container.html(@message)
|
||||
if @backend.mock_backend
|
||||
@message_container.append("<p>NOTE: Mocking backend.</p>")
|
||||
|
||||
@error_container.html(@error_msg)
|
||||
|
||||
if @state == state_error
|
||||
@set_button_text('Try loading again')
|
||||
|
||||
else if @state == state_grading
|
||||
@ml_error_info_container.html(@ml_error_info)
|
||||
@prompt_container.html(@prompt)
|
||||
@submission_container.html(@submission)
|
||||
@rubric_container.html(@rubric)
|
||||
show_grading_elements = true
|
||||
|
||||
# no submit button until user picks grade.
|
||||
show_submit_button = false
|
||||
|
||||
@setup_score_selection()
|
||||
|
||||
else if @state == state_graded
|
||||
show_grading_elements = true
|
||||
@set_button_text('Submit')
|
||||
|
||||
else if @state == state_no_data
|
||||
@message_container.html(@message)
|
||||
@set_button_text('Re-check for submissions')
|
||||
|
||||
else
|
||||
@error('System got into invalid state ' + @state)
|
||||
|
||||
@submit_button.toggle(show_submit_button)
|
||||
@prompt_wrapper.toggle(show_grading_elements)
|
||||
@submission_wrapper.toggle(show_grading_elements)
|
||||
@rubric_wrapper.toggle(show_grading_elements)
|
||||
@ml_error_info_container.toggle(show_grading_elements)
|
||||
|
||||
|
||||
submit: (event) =>
|
||||
event.preventDefault()
|
||||
|
||||
if @state == state_error
|
||||
@get_next_submission()
|
||||
else if @state == state_graded
|
||||
@submit_and_get_next()
|
||||
else if @state == state_no_data
|
||||
@get_next_submission()
|
||||
else
|
||||
@error('System got into invalid state for submission: ' + @state)
|
||||
|
||||
|
||||
# for now, just create an instance and load it...
|
||||
mock_backend = false
|
||||
ajax_url = $('.staff-grading').data('ajax_url')
|
||||
backend = new StaffGradingBackend(ajax_url, mock_backend)
|
||||
|
||||
$(document).ready(() -> new StaffGrading(backend))
|
||||
45
lms/static/coffee/src/staff_grading/test_grading.html
Normal file
45
lms/static/coffee/src/staff_grading/test_grading.html
Normal file
@@ -0,0 +1,45 @@
|
||||
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
|
||||
<html> <head>
|
||||
<title></title>
|
||||
<!-- <script src="http://code.jquery.com/jquery-latest.js"></script> -->
|
||||
<script src="../../../admin/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="staff_grading.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="staff-grading" data-ajax_url="/some_url/">
|
||||
<h1>Staff grading</h1>
|
||||
|
||||
<div class="error-container">
|
||||
</div>
|
||||
|
||||
<div class="message-container">
|
||||
</div>
|
||||
|
||||
<section class="submission-wrapper">
|
||||
<h3>Submission</h3>
|
||||
<div class="submission-container">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="rubric-wrapper">
|
||||
<h3>Rubric</h3>
|
||||
<div class="rubric-container">
|
||||
</div>
|
||||
|
||||
<div class="evaluation">
|
||||
<textarea name="feedback" placeholder="Feedback for student..."
|
||||
class="feedback-area" cols="70" rows="10"></textarea>
|
||||
<p class="score-selection-container">
|
||||
</p>
|
||||
</div>
|
||||
|
||||
</section>
|
||||
|
||||
<div class="submission">
|
||||
<input type="button" value="Submit" class="submit-button" name="show"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</body> </html>
|
||||
@@ -43,6 +43,7 @@
|
||||
@import "course/profile";
|
||||
@import "course/gradebook";
|
||||
@import "course/tabs";
|
||||
@import "course/staff_grading";
|
||||
|
||||
// instructor
|
||||
@import "course/instructor/instructor";
|
||||
|
||||
29
lms/static/sass/course/_staff_grading.scss
Normal file
29
lms/static/sass/course/_staff_grading.scss
Normal file
@@ -0,0 +1,29 @@
|
||||
div.staff-grading {
|
||||
textarea.feedback-area {
|
||||
height: 100px;
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
div {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
label {
|
||||
margin: 10px;
|
||||
padding: 5px;
|
||||
display: inline-block;
|
||||
min-width: 50px;
|
||||
background-color: #CCC;
|
||||
text-size: 1.5em;
|
||||
}
|
||||
|
||||
/* Toggled State */
|
||||
input[type=radio]:checked + label {
|
||||
background: #666;
|
||||
color: white;
|
||||
}
|
||||
|
||||
input[name='score-selection'] {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user