From 48b9043213093473452ff3243f3fa2354c8b99d4 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 5 Dec 2012 13:08:22 -0500 Subject: [PATCH 01/17] Initial problem list functionality --- .../src/staff_grading/staff_grading.coffee | 93 +++++++++++++------ 1 file changed, 64 insertions(+), 29 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index f4831e35c4..629efdfd50 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -36,19 +36,20 @@ class StaffGradingBackend else if cmd == 'save_grade' console.log("eval: #{data.score} pts, Feedback: #{data.feedback}") response = - @mock('get_next', {}) - # get_probblem_list + @mock('get_next', {location}) + # get_problem_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} - ] + @mock_cnt++ + 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 @@ -79,8 +80,13 @@ class StaffGradingBackend class StaffGrading constructor: (backend) -> @backend = backend + @list_view = true # all the jquery selectors + + @problem_list_container = $('.problem-list-container') + @problem_list = $('.problem-list') + @error_container = $('.error-container') @message_container = $('.message-container') @@ -108,17 +114,19 @@ class StaffGrading @message = '' @max_score = 0 @ml_error_info= '' + @location = '' @score = null + @problems = null # action handlers @submit_button.click @submit # render intial state - @render_view() + #@render_view() # send initial request automatically - @get_next_submission() + @get_problem_list() setup_score_selection: => @@ -153,7 +161,9 @@ class StaffGrading @message = '' if response.success - if response.submission + if response.problem_list + @problems = response.problem_list + else 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) @@ -162,8 +172,13 @@ class StaffGrading @render_view() - get_next_submission: () -> - @backend.post('get_next', {}, @ajax_callback) + get_next_submission: (location) -> + @location = location + @list_view = false + @backend.post('get_next', {location}, @ajax_callback) + + get_problem_list: () -> + @backend.post('get_problem_list', {}, @ajax_callback) submit_and_get_next: () -> data = @@ -202,14 +217,41 @@ class StaffGrading @state = state_no_data render_view: () -> - # make the view elements match the state. Idempotent. - show_grading_elements = false - show_submit_button = true - + # clear the problem list + @problem_list.html('') @message_container.html(@message) + # only show the grading elements when we are not in list view or the state + # is invalid + show_grading_elements = !(@list_view || @state == state_error || + @state == state_no_data) + @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_button.hide() + if @backend.mock_backend @message_container.append("

NOTE: Mocking backend.

") - + if @list_view + @render_list() + else + @render_problem() + + problem_link:(problem) -> + link = $('').attr('href', "javascript:void(0)").append( + "#{problem.problem_name} (#{problem.num_left} / #{problem.num_total})") + .click => + @get_next_submission problem.location + + + render_list: () -> + for problem in @problems + @problem_list.append($('
  • ').append(@problem_link(problem))) + + render_problem: () -> + # make the view elements match the state. Idempotent. + show_submit_button = true + @error_container.html(@error_msg) if @state == state_error @@ -220,7 +262,6 @@ class StaffGrading @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 @@ -228,7 +269,6 @@ class StaffGrading @setup_score_selection() else if @state == state_graded - show_grading_elements = true @set_button_text('Submit') else if @state == state_no_data @@ -239,21 +279,16 @@ class StaffGrading @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() + @get_next_submission(@location) else if @state == state_graded @submit_and_get_next() else if @state == state_no_data - @get_next_submission() + @get_next_submission(@location) else @error('System got into invalid state for submission: ' + @state) From 94e1a9ffd3cb465a23dbe757619ca565e8a756bd Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 5 Dec 2012 14:38:53 -0500 Subject: [PATCH 02/17] Clean up some minor issues with list view and display some new information on the problem-specific page --- .../src/staff_grading/staff_grading.coffee | 18 +++++++++++++++--- lms/templates/instructor/staff_grading.html | 7 +++++++ 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 629efdfd50..1e0f89228c 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -80,7 +80,6 @@ class StaffGradingBackend class StaffGrading constructor: (backend) -> @backend = backend - @list_view = true # all the jquery selectors @@ -90,6 +89,7 @@ class StaffGrading @error_container = $('.error-container') @message_container = $('.message-container') + @prompt_name_container = $('.prompt-name') @prompt_container = $('.prompt-container') @prompt_wrapper = $('.prompt-wrapper') @@ -115,6 +115,9 @@ class StaffGrading @max_score = 0 @ml_error_info= '' @location = '' + @prompt_name = '' + @num_total = 0 + @num_left = 0 @score = null @problems = null @@ -164,7 +167,7 @@ class StaffGrading if response.problem_list @problems = response.problem_list else if response.submission - @data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info) + @data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info, response.problem_name, response.num_left, response.num_total) else @no_more(response.message) else @@ -178,6 +181,7 @@ class StaffGrading @backend.post('get_next', {location}, @ajax_callback) get_problem_list: () -> + @list_view = true @backend.post('get_problem_list', {}, @ajax_callback) submit_and_get_next: () -> @@ -192,7 +196,7 @@ class StaffGrading @error_msg = msg @state = state_error - data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info) -> + data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info, prompt_name, num_left, num_total) -> @prompt = prompt @submission = submission @rubric = rubric @@ -201,12 +205,18 @@ class StaffGrading @max_score = max_score @score = null @ml_error_info=ml_error_info + @prompt_name = prompt_name + @num_left = num_left + @num_total = num_total @state = state_grading if not @max_score? @error("No max score specified for submission.") no_more: (message) -> @prompt = null + @prompt_name = '' + @num_left = 0 + @num_total = 0 @submission = null @rubric = null @ml_error_info = null @@ -219,6 +229,7 @@ class StaffGrading render_view: () -> # clear the problem list @problem_list.html('') + @problem_list_container.toggle(@list_view) @message_container.html(@message) # only show the grading elements when we are not in list view or the state # is invalid @@ -260,6 +271,7 @@ class StaffGrading else if @state == state_grading @ml_error_info_container.html(@ml_error_info) @prompt_container.html(@prompt) + @prompt_name_container.html("#{@prompt_name} (#{@num_left} / #{@num_total})") @submission_container.html(@submission) @rubric_container.html(@rubric) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index a44ef68831..0666b3d7ec 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -19,6 +19,8 @@

    Staff grading

    +
    @@ -27,8 +29,13 @@
    +
    +
      +
    +
    +

    Question prompt

    From 5914b1cf916939e4b1a5f4502a06b3aaf60beba7 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 5 Dec 2012 16:15:49 -0500 Subject: [PATCH 03/17] Make the mock backend support different problems, and make sure the location is passed around properly. Clarify some of the numbers for the user --- .../src/staff_grading/staff_grading.coffee | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 1e0f89228c..57366842d1 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -21,22 +21,40 @@ class StaffGradingBackend # 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 + switch data.location + when 'i4x://MITx/3.091x/problem/open_ended_demo1' + 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 + when 'i4x://MITx/3.091x/problem/open_ended_demo2' + response = + success: true + problem_name: 'Problem 2' + num_left: 2 + num_total: 5 + prompt: 'This is a fake second problem' + submission: 'This is the best submission ever! ' + @mock_cnt + rubric: 'I am a rubric for grading things! ' + @mock_cnt + submission_id: @mock_cnt + max_score: 2 + @mock_cnt % 3 + ml_error_info : 'ML accuracy info: ' + @mock_cnt + else + response = + success: false + else if cmd == 'save_grade' console.log("eval: #{data.score} pts, Feedback: #{data.feedback}") response = - @mock('get_next', {location}) + @mock('get_next', {location: data.location}) # get_problem_list # sends in a course_id and a grader_id # should get back a list of problem_ids, problem_names, num_left, num_total @@ -45,9 +63,9 @@ class StaffGradingBackend response = success: true problem_list: [ - {location: 'i4x://MITx/3.091x/problem/open_ended_demo', \ + {location: 'i4x://MITx/3.091x/problem/open_ended_demo1', \ problem_name: "Problem 1", num_left: 3, num_total: 5}, - {location: 'i4x://MITx/3.091x/problem/open_ended_demo', \ + {location: 'i4x://MITx/3.091x/problem/open_ended_demo2', \ problem_name: "Problem 2", num_left: 1, num_total: 5} ] else @@ -178,7 +196,7 @@ class StaffGrading get_next_submission: (location) -> @location = location @list_view = false - @backend.post('get_next', {location}, @ajax_callback) + @backend.post('get_next', {location: location}, @ajax_callback) get_problem_list: () -> @list_view = true @@ -189,6 +207,7 @@ class StaffGrading score: @score feedback: @feedback_area.val() submission_id: @submission_id + location: @location @backend.post('save_grade', data, @ajax_callback) @@ -250,7 +269,7 @@ class StaffGrading problem_link:(problem) -> link = $('
    ').attr('href', "javascript:void(0)").append( - "#{problem.problem_name} (#{problem.num_left} / #{problem.num_total})") + "#{problem.problem_name} (#{problem.num_left} left out of #{problem.num_total})") .click => @get_next_submission problem.location @@ -271,7 +290,7 @@ class StaffGrading else if @state == state_grading @ml_error_info_container.html(@ml_error_info) @prompt_container.html(@prompt) - @prompt_name_container.html("#{@prompt_name} (#{@num_left} / #{@num_total})") + @prompt_name_container.html("#{@prompt_name} (#{@num_left} left out of #{@num_total})") @submission_container.html(@submission) @rubric_container.html(@rubric) From b5f3386d275e2cef1dd3431a9c02076f2a3fbd04 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Wed, 5 Dec 2012 16:38:54 -0500 Subject: [PATCH 04/17] Add breadcrumbing and clarify some of the text on the instructor grading interface. --- .../coffee/src/staff_grading/staff_grading.coffee | 13 +++++++++++-- lms/templates/instructor/staff_grading.html | 6 +++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 57366842d1..38daad61dc 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -59,7 +59,7 @@ class StaffGradingBackend # 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' - @mock_cnt++ + @mock_cnt = 1 response = success: true problem_list: [ @@ -121,6 +121,8 @@ class StaffGrading @score_selection_container = $('.score-selection-container') @submit_button = $('.submit-button') @ml_error_info_container = $('.ml-error-info-container') + + @breadcrumbs = $('.breadcrumbs') # model state @state = state_no_data @@ -246,8 +248,9 @@ class StaffGrading @state = state_no_data render_view: () -> - # clear the problem list + # clear the problem list and breadcrumbs @problem_list.html('') + @breadcrumbs.html('') @problem_list_container.toggle(@list_view) @message_container.html(@message) # only show the grading elements when we are not in list view or the state @@ -283,6 +286,12 @@ class StaffGrading show_submit_button = true @error_container.html(@error_msg) + problem_list_link = $('').attr('href', 'javascript:void(0);') + .append("Problem List") + .click => @get_problem_list() + + # set up the breadcrumbing + @breadcrumbs.append(problem_list_link).append(" > #{@prompt_name}") if @state == state_error @set_button_text('Try loading again') diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 0666b3d7ec..0a3bc0dd82 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -36,19 +36,19 @@

    -

    Question prompt

    +

    Question

    -

    Submission

    +

    Student Submission

    -

    Rubric

    +

    Grading Rubric

    From fcc1ab71f6028eace4dfdd761bf24bf13f517539 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 10:44:03 -0500 Subject: [PATCH 05/17] Update the formatting and layout of the Instructor Grading interface. --- .../src/staff_grading/staff_grading.coffee | 56 +++++++++++++++---- lms/templates/instructor/staff_grading.html | 34 ++++++----- 2 files changed, 65 insertions(+), 25 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 38daad61dc..a70c33fdff 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -28,9 +28,27 @@ class StaffGradingBackend 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 + prompt: ''' +

    S11E3: Metal Bands

    +

    Shown below are schematic band diagrams for two different metals. Both diagrams appear different, yet both of the elements are undisputably metallic in nature.

    + +

    * Why is it that both sodium and magnesium behave as metals, even though the s-band of magnesium is filled?

    +

    This is a self-assessed open response question. Please use as much space as you need in the box below to answer the question.

    + ''' + submission: ''' + Contrary to popular belief, Lorem Ipsum is not simply random text. It has roots in a piece of classical Latin literature from 45 BC, making it over 2000 years old. Richard McClintock, a Latin professor at Hampden-Sydney College in Virginia, looked up one of the more obscure Latin words, consectetur, from a Lorem Ipsum passage, and going through the cites of the word in classical literature, discovered the undoubtable source. Lorem Ipsum comes from sections 1.10.32 and 1.10.33 of "de Finibus Bonorum et Malorum" (The Extremes of Good and Evil) by Cicero, written in 45 BC. This book is a treatise on the theory of ethics, very popular during the Renaissance. The first line of Lorem Ipsum, "Lorem ipsum dolor sit amet..", comes from a line in section 1.10.32. + +The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for those interested. Sections 1.10.32 and 1.10.33 from "de Finibus Bonorum et Malorum" by Cicero are also reproduced in their exact original form, accompanied by English versions from the 1914 translation by H. Rackham. + ''' + rubric: ''' +
      +
    • Metals tend to be good electronic conductors, meaning that they have a large number of electrons which are able to access empty (mobile) energy states within the material.
    • +
    • Sodium has a half-filled s-band, so there are a number of empty states immediately above the highest occupied energy levels within the band.
    • +
    • Magnesium has a full s-band, but the the s-band and p-band overlap in magnesium. Thus are still a large number of available energy states immediately above the s-band highest occupied energy level.
    • +
    + +

    Please score your response according to how many of the above components you identified:

    + ''' submission_id: @mock_cnt max_score: 2 + @mock_cnt % 3 ml_error_info : 'ML accuracy info: ' + @mock_cnt @@ -116,6 +134,7 @@ class StaffGrading @rubric_container = $('.rubric-container') @rubric_wrapper = $('.rubric-wrapper') + @grading_wrapper = $('.grading-wrapper') @feedback_area = $('.feedback-area') @score_selection_container = $('.score-selection-container') @@ -176,6 +195,7 @@ class StaffGrading graded_callback: (event) => @score = event.target.value @state = state_graded + @message = '' @render_view() ajax_callback: (response) => @@ -247,12 +267,23 @@ class StaffGrading @max_score = 0 @state = state_no_data + hide_if_empty: (container,message) -> + if message == '' + container.hide() + else + container.html(message) + render_view: () -> # clear the problem list and breadcrumbs @problem_list.html('') @breadcrumbs.html('') @problem_list_container.toggle(@list_view) - @message_container.html(@message) + if @backend.mock_backend + @message = @message + "

    NOTE: Mocking backend.

    " + @hide_if_empty(@message_container, @message) + @hide_if_empty(@error_container, @error_msg) + + # only show the grading elements when we are not in list view or the state # is invalid show_grading_elements = !(@list_view || @state == state_error || @@ -260,11 +291,10 @@ class StaffGrading @prompt_wrapper.toggle(show_grading_elements) @submission_wrapper.toggle(show_grading_elements) @rubric_wrapper.toggle(show_grading_elements) + @grading_wrapper.toggle(show_grading_elements) @ml_error_info_container.toggle(show_grading_elements) @submit_button.hide() - if @backend.mock_backend - @message_container.append("

    NOTE: Mocking backend.

    ") if @list_view @render_list() else @@ -276,6 +306,12 @@ class StaffGrading .click => @get_next_submission problem.location + make_paragraphs: (text) -> + paragraph_split = text.split("\n") + new_text = '' + for paragraph in paragraph_split + new_text += "

    #{paragraph}

    " + return new_text render_list: () -> for problem in @problems @@ -285,13 +321,13 @@ class StaffGrading # make the view elements match the state. Idempotent. show_submit_button = true - @error_container.html(@error_msg) problem_list_link = $('
    ').attr('href', 'javascript:void(0);') - .append("Problem List") + .append("< Back to problem list") .click => @get_problem_list() # set up the breadcrumbing - @breadcrumbs.append(problem_list_link).append(" > #{@prompt_name}") + @breadcrumbs.append(problem_list_link) + if @state == state_error @set_button_text('Try loading again') @@ -300,7 +336,7 @@ class StaffGrading @ml_error_info_container.html(@ml_error_info) @prompt_container.html(@prompt) @prompt_name_container.html("#{@prompt_name} (#{@num_left} left out of #{@num_total})") - @submission_container.html(@submission) + @submission_container.html(@make_paragraphs(@submission)) @rubric_container.html(@rubric) # no submit button until user picks grade. diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 0a3bc0dd82..3c687c643f 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -18,49 +18,52 @@

    Staff grading

    -
    -
    - +
    -
    +
    -
    +

    +

    Question

    -
    - -
    -

    Student Submission

    -
    -
    - -
    +

    Grading Rubric

    +
    +
    +

    Student Submission

    +
    +
    +
    +
    + + +
    +

    Grading

    + +
    -

    -
    @@ -68,4 +71,5 @@
    +
    From ffa42b6f115914c4cdb740727f1944d57d550813 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 10:45:48 -0500 Subject: [PATCH 06/17] Styles for the new instructor grading interface --- lms/static/sass/course/_staff_grading.scss | 35 ++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/lms/static/sass/course/_staff_grading.scss b/lms/static/sass/course/_staff_grading.scss index 4900f78bd0..935a93ee96 100644 --- a/lms/static/sass/course/_staff_grading.scss +++ b/lms/static/sass/course/_staff_grading.scss @@ -26,4 +26,39 @@ div.staff-grading { input[name='score-selection'] { display: none; } + + .prompt-information-container, + .submission-wrapper, + .rubric-wrapper, + .grading-container + { + border: 1px solid gray; + padding: 15px; + } + .error-container + { + background-color: $error-red; + } + .ml-error-info-container, + { + background-color: #eee; + padding:15px; + margin-left:0px; + } + .message-container + { + background-color: $yellow; + padding: 10px; + margin-left:0px; + } + + .breadcrumbs + { + margin-top:20px; + margin-left:0px; + margin-bottom:5px; + font-size: .8em; + } + + padding: 40px; } From ef82ced2af3f8852e1af71b61bd00f1bd9867059 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 13:24:20 -0500 Subject: [PATCH 07/17] Improve the paragraph maker on the staff grading page --- lms/static/coffee/src/staff_grading/staff_grading.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index a70c33fdff..3bdefb455c 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -307,7 +307,7 @@ class StaffGrading @get_next_submission problem.location make_paragraphs: (text) -> - paragraph_split = text.split("\n") + paragraph_split = text.split(/\n\s*\n/) new_text = '' for paragraph in paragraph_split new_text += "

    #{paragraph}

    " From aee1e5e9a92cff16861a89bbaedc165208c32abe Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 14:29:45 -0500 Subject: [PATCH 08/17] Updates to the list view and error container --- lms/static/sass/course/_staff_grading.scss | 12 +++++++++++- lms/templates/instructor/staff_grading.html | 9 +++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/lms/static/sass/course/_staff_grading.scss b/lms/static/sass/course/_staff_grading.scss index 935a93ee96..1122129b9d 100644 --- a/lms/static/sass/course/_staff_grading.scss +++ b/lms/static/sass/course/_staff_grading.scss @@ -27,6 +27,14 @@ div.staff-grading { display: none; } + ul + { + li + { + margin: 16px 0px; + } + } + .prompt-information-container, .submission-wrapper, .rubric-wrapper, @@ -37,7 +45,9 @@ div.staff-grading { } .error-container { - background-color: $error-red; + background-color: #FFCCCC; + padding: 15px; + margin-left: 0px; } .ml-error-info-container, { diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 3c687c643f..63bc2fa098 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -28,6 +28,12 @@
    +

    Instructions

    +
    +

    This is the list of problems that current need to be graded in order to train the machine learning models. Each problem needs to be trained separately, and we have indicated the number of student submissions that need to be graded in order for a model to be generated. Any number of problems can be graded, not just the amount required to create the model.

    +
    + +

    Problem List

    @@ -52,6 +58,9 @@
    +
    + +

    Grading

    From 7c768cbbff78a6833b13a48eef98da742e8db01a Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 14:31:02 -0500 Subject: [PATCH 09/17] Wire up the separate action button --- .../src/staff_grading/staff_grading.coffee | 52 ++++++++++--------- 1 file changed, 27 insertions(+), 25 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index 3bdefb455c..db40356354 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -26,7 +26,7 @@ class StaffGradingBackend response = success: true problem_name: 'Problem 1' - num_left: 3 + num_graded: 3 num_total: 5 prompt: '''

    S11E3: Metal Bands

    @@ -56,7 +56,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t response = success: true problem_name: 'Problem 2' - num_left: 2 + num_graded: 2 num_total: 5 prompt: 'This is a fake second problem' submission: 'This is the best submission ever! ' + @mock_cnt @@ -75,16 +75,16 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t @mock('get_next', {location: data.location}) # get_problem_list # sends in a course_id and a grader_id - # should get back a list of problem_ids, problem_names, num_left, num_total + # should get back a list of problem_ids, problem_names, num_graded, num_total else if cmd == 'get_problem_list' @mock_cnt = 1 response = success: true problem_list: [ {location: 'i4x://MITx/3.091x/problem/open_ended_demo1', \ - problem_name: "Problem 1", num_left: 3, num_total: 5}, + problem_name: "Problem 1", num_graded: 3, num_total: 5}, {location: 'i4x://MITx/3.091x/problem/open_ended_demo2', \ - problem_name: "Problem 2", num_left: 1, num_total: 5} + problem_name: "Problem 2", num_graded: 1, num_total: 5} ] else response = @@ -97,7 +97,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t message: 'No more submissions' - if @mock_cnt % 7 == 0 + if @mock_cnt % 3 == 0 response = success: false error: 'An error for testing' @@ -139,6 +139,7 @@ class StaffGrading @feedback_area = $('.feedback-area') @score_selection_container = $('.score-selection-container') @submit_button = $('.submit-button') + @action_button = $('.action-button') @ml_error_info_container = $('.ml-error-info-container') @breadcrumbs = $('.breadcrumbs') @@ -156,16 +157,15 @@ class StaffGrading @location = '' @prompt_name = '' @num_total = 0 - @num_left = 0 + @num_graded = 0 @score = null @problems = null # action handlers @submit_button.click @submit - - # render intial state - #@render_view() + # TODO: fix this to do something more intelligent + @action_button.click @submit # send initial request automatically @get_problem_list() @@ -190,7 +190,7 @@ class StaffGrading set_button_text: (text) => - @submit_button.attr('value', text) + @action_button.attr('value', text) graded_callback: (event) => @score = event.target.value @@ -207,7 +207,7 @@ class StaffGrading if response.problem_list @problems = response.problem_list else if response.submission - @data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info, response.problem_name, response.num_left, response.num_total) + @data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info, response.problem_name, response.num_graded, response.num_total) else @no_more(response.message) else @@ -237,7 +237,7 @@ class StaffGrading @error_msg = msg @state = state_error - data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info, prompt_name, num_left, num_total) -> + data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info, prompt_name, num_graded, num_total) -> @prompt = prompt @submission = submission @rubric = rubric @@ -247,7 +247,7 @@ class StaffGrading @score = null @ml_error_info=ml_error_info @prompt_name = prompt_name - @num_left = num_left + @num_graded = num_graded @num_total = num_total @state = state_grading if not @max_score? @@ -256,7 +256,7 @@ class StaffGrading no_more: (message) -> @prompt = null @prompt_name = '' - @num_left = 0 + @num_graded = 0 @num_total = 0 @submission = null @rubric = null @@ -267,11 +267,6 @@ class StaffGrading @max_score = 0 @state = state_no_data - hide_if_empty: (container,message) -> - if message == '' - container.hide() - else - container.html(message) render_view: () -> # clear the problem list and breadcrumbs @@ -280,8 +275,10 @@ class StaffGrading @problem_list_container.toggle(@list_view) if @backend.mock_backend @message = @message + "

    NOTE: Mocking backend.

    " - @hide_if_empty(@message_container, @message) - @hide_if_empty(@error_container, @error_msg) + @message_container.html(@message) + @error_container.html(@error_msg) + @message_container.toggle(@message != "") + @error_container.toggle(@error_msg != "") # only show the grading elements when we are not in list view or the state @@ -293,7 +290,7 @@ class StaffGrading @rubric_wrapper.toggle(show_grading_elements) @grading_wrapper.toggle(show_grading_elements) @ml_error_info_container.toggle(show_grading_elements) - @submit_button.hide() + @action_button.hide() if @list_view @render_list() @@ -302,7 +299,7 @@ class StaffGrading problem_link:(problem) -> link = $('
    ').attr('href', "javascript:void(0)").append( - "#{problem.problem_name} (#{problem.num_left} left out of #{problem.num_total})") + "#{problem.problem_name} (#{problem.num_graded} graded out of #{problem.num_total})") .click => @get_next_submission problem.location @@ -320,6 +317,7 @@ class StaffGrading render_problem: () -> # make the view elements match the state. Idempotent. show_submit_button = true + show_action_button = true problem_list_link = $('').attr('href', 'javascript:void(0);') .append("< Back to problem list") @@ -331,21 +329,24 @@ class StaffGrading if @state == state_error @set_button_text('Try loading again') + show_action_button = true else if @state == state_grading @ml_error_info_container.html(@ml_error_info) @prompt_container.html(@prompt) - @prompt_name_container.html("#{@prompt_name} (#{@num_left} left out of #{@num_total})") + @prompt_name_container.html("#{@prompt_name} (#{@num_graded} completed out of #{@num_total})") @submission_container.html(@make_paragraphs(@submission)) @rubric_container.html(@rubric) # no submit button until user picks grade. show_submit_button = false + show_action_button = false @setup_score_selection() else if @state == state_graded @set_button_text('Submit') + show_action_button = false else if @state == state_no_data @message_container.html(@message) @@ -355,6 +356,7 @@ class StaffGrading @error('System got into invalid state ' + @state) @submit_button.toggle(show_submit_button) + @action_button.toggle(show_action_button) submit: (event) => event.preventDefault() From 275c9ef7fd3c5c30c07b5d9b76a97920eb3e68ea Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 17:19:22 -0500 Subject: [PATCH 10/17] Update API with server and add new meta info box for meta information --- .../src/staff_grading/staff_grading.coffee | 56 +++++++++++-------- lms/templates/instructor/staff_grading.html | 11 +++- 2 files changed, 42 insertions(+), 25 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index db40356354..e5e8d93634 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -27,7 +27,8 @@ class StaffGradingBackend success: true problem_name: 'Problem 1' num_graded: 3 - num_total: 5 + min_for_ml: 5 + num_pending: 4 prompt: '''

    S11E3: Metal Bands

    Shown below are schematic band diagrams for two different metals. Both diagrams appear different, yet both of the elements are undisputably metallic in nature.

    @@ -57,7 +58,8 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t success: true problem_name: 'Problem 2' num_graded: 2 - num_total: 5 + min_for_ml: 5 + num_pending: 4 prompt: 'This is a fake second problem' submission: 'This is the best submission ever! ' + @mock_cnt rubric: 'I am a rubric for grading things! ' + @mock_cnt @@ -74,17 +76,16 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t response = @mock('get_next', {location: data.location}) # get_problem_list - # sends in a course_id and a grader_id - # should get back a list of problem_ids, problem_names, num_graded, num_total + # should get back a list of problem_ids, problem_names, num_graded, min_for_ml else if cmd == 'get_problem_list' @mock_cnt = 1 response = success: true problem_list: [ {location: 'i4x://MITx/3.091x/problem/open_ended_demo1', \ - problem_name: "Problem 1", num_graded: 3, num_total: 5}, + problem_name: "Problem 1", num_graded: 3, num_pending: 5, min_for_ml: 10}, {location: 'i4x://MITx/3.091x/problem/open_ended_demo2', \ - problem_name: "Problem 2", num_graded: 1, num_total: 5} + problem_name: "Problem 2", num_graded: 1, num_pending: 5, min_for_ml: 10} ] else response = @@ -140,6 +141,9 @@ class StaffGrading @score_selection_container = $('.score-selection-container') @submit_button = $('.submit-button') @action_button = $('.action-button') + + @problem_meta_info = $('.problem-meta-info-container') + @meta_info_wrapper = $('.meta-info-wrapper') @ml_error_info_container = $('.ml-error-info-container') @breadcrumbs = $('.breadcrumbs') @@ -156,8 +160,9 @@ class StaffGrading @ml_error_info= '' @location = '' @prompt_name = '' - @num_total = 0 + @min_for_ml = 0 @num_graded = 0 + @num_pending = 0 @score = null @problems = null @@ -207,7 +212,7 @@ class StaffGrading if response.problem_list @problems = response.problem_list else if response.submission - @data_loaded(response.prompt, response.submission, response.rubric, response.submission_id, response.max_score, response.ml_error_info, response.problem_name, response.num_graded, response.num_total) + @data_loaded(response) else @no_more(response.message) else @@ -237,18 +242,19 @@ class StaffGrading @error_msg = msg @state = state_error - data_loaded: (prompt, submission, rubric, submission_id, max_score, ml_error_info, prompt_name, num_graded, num_total) -> - @prompt = prompt - @submission = submission - @rubric = rubric - @submission_id = submission_id + data_loaded: (response) -> + @prompt = response.prompt + @submission = response.submission + @rubric = response.rubric + @submission_id = response.submission_id @feedback_area.val('') - @max_score = max_score + @max_score = response.max_score @score = null - @ml_error_info=ml_error_info - @prompt_name = prompt_name - @num_graded = num_graded - @num_total = num_total + @ml_error_info=response.ml_error_info + @prompt_name = response.problem_name + @num_graded = response.num_graded + @min_for_ml = response.min_for_ml + @num_pending = response.num_pending @state = state_grading if not @max_score? @error("No max score specified for submission.") @@ -257,7 +263,7 @@ class StaffGrading @prompt = null @prompt_name = '' @num_graded = 0 - @num_total = 0 + @min_for_ml = 0 @submission = null @rubric = null @ml_error_info = null @@ -289,7 +295,7 @@ class StaffGrading @submission_wrapper.toggle(show_grading_elements) @rubric_wrapper.toggle(show_grading_elements) @grading_wrapper.toggle(show_grading_elements) - @ml_error_info_container.toggle(show_grading_elements) + @meta_info_wrapper.toggle(show_grading_elements) @action_button.hide() if @list_view @@ -299,7 +305,7 @@ class StaffGrading problem_link:(problem) -> link = $('
    ').attr('href', "javascript:void(0)").append( - "#{problem.problem_name} (#{problem.num_graded} graded out of #{problem.num_total})") + "#{problem.problem_name} (#{problem.num_graded} graded, #{problem.num_pending} pending)") .click => @get_next_submission problem.location @@ -333,8 +339,14 @@ class StaffGrading else if @state == state_grading @ml_error_info_container.html(@ml_error_info) + meta_list = $("
      ") + meta_list.append("
    • Pending: #{@num_pending}
    • ") + meta_list.append("
    • Graded: #{@num_graded}
    • ") + meta_list.append("
    • Needed for ML: #{Math.max(@min_for_ml - @num_graded)}
    • ") + @problem_meta_info.append(meta_list) + @prompt_container.html(@prompt) - @prompt_name_container.html("#{@prompt_name} (#{@num_graded} completed out of #{@num_total})") + @prompt_name_container.html("#{@prompt_name}") @submission_container.html(@make_paragraphs(@submission)) @rubric_container.html(@rubric) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index 63bc2fa098..db500fde84 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -17,6 +17,14 @@
      +
      +

      Meta Information

      +
      +
      +

      Maching Learning Information

      +
      +
      +

      Staff grading

      @@ -24,9 +32,6 @@
      -
      -
      -

      Instructions

      From 1026c6411113256212e93140a2d605ced3716a06 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Thu, 6 Dec 2012 18:15:40 -0500 Subject: [PATCH 11/17] Fix up some more of the meta info container --- lms/static/coffee/src/staff_grading/staff_grading.coffee | 2 +- lms/static/sass/course/_staff_grading.scss | 2 +- lms/templates/instructor/staff_grading.html | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index e5e8d93634..e5fba57710 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -343,7 +343,7 @@ class StaffGrading meta_list.append("
    • Pending: #{@num_pending}
    • ") meta_list.append("
    • Graded: #{@num_graded}
    • ") meta_list.append("
    • Needed for ML: #{Math.max(@min_for_ml - @num_graded)}
    • ") - @problem_meta_info.append(meta_list) + @problem_meta_info.html(meta_list) @prompt_container.html(@prompt) @prompt_name_container.html("#{@prompt_name}") diff --git a/lms/static/sass/course/_staff_grading.scss b/lms/static/sass/course/_staff_grading.scss index 1122129b9d..3de22d40bb 100644 --- a/lms/static/sass/course/_staff_grading.scss +++ b/lms/static/sass/course/_staff_grading.scss @@ -49,7 +49,7 @@ div.staff-grading { padding: 15px; margin-left: 0px; } - .ml-error-info-container, + .meta-info-wrapper { background-color: #eee; padding:15px; diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index db500fde84..dd5aa9ae08 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -18,7 +18,7 @@
      -

      Meta Information

      +

      Problem Information

      Maching Learning Information

      From 5030c9d35b76d4e6da5b898b8ee4fef56b736a38 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 7 Dec 2012 11:20:05 -0500 Subject: [PATCH 12/17] Cleanup of the meta information styles and make the feedback box less important. --- .../src/staff_grading/staff_grading.coffee | 8 ++++---- lms/static/sass/course/_staff_grading.scss | 16 +++++++++++++-- lms/templates/instructor/staff_grading.html | 20 +++++++++---------- 3 files changed, 28 insertions(+), 16 deletions(-) diff --git a/lms/static/coffee/src/staff_grading/staff_grading.coffee b/lms/static/coffee/src/staff_grading/staff_grading.coffee index e5fba57710..abcf99e40b 100644 --- a/lms/static/coffee/src/staff_grading/staff_grading.coffee +++ b/lms/static/coffee/src/staff_grading/staff_grading.coffee @@ -98,7 +98,7 @@ The standard chunk of Lorem Ipsum used since the 1500s is reproduced below for t message: 'No more submissions' - if @mock_cnt % 3 == 0 + if @mock_cnt % 7 == 0 response = success: false error: 'An error for testing' @@ -340,9 +340,9 @@ class StaffGrading else if @state == state_grading @ml_error_info_container.html(@ml_error_info) meta_list = $("
        ") - meta_list.append("
      • Pending: #{@num_pending}
      • ") - meta_list.append("
      • Graded: #{@num_graded}
      • ") - meta_list.append("
      • Needed for ML: #{Math.max(@min_for_ml - @num_graded)}
      • ") + meta_list.append("
      • Pending - #{@num_pending}
      • ") + meta_list.append("
      • Graded - #{@num_graded}
      • ") + meta_list.append("
      • Needed for ML - #{Math.max(@min_for_ml - @num_graded)}
      • ") @problem_meta_info.html(meta_list) @prompt_container.html(@prompt) diff --git a/lms/static/sass/course/_staff_grading.scss b/lms/static/sass/course/_staff_grading.scss index 3de22d40bb..f1b6c5845d 100644 --- a/lms/static/sass/course/_staff_grading.scss +++ b/lms/static/sass/course/_staff_grading.scss @@ -1,6 +1,6 @@ div.staff-grading { textarea.feedback-area { - height: 100px; + height: 75px; margin: 20px; } @@ -53,7 +53,19 @@ div.staff-grading { { background-color: #eee; padding:15px; - margin-left:0px; + h3 + { + font-size:1em; + } + ul + { + list-style-type: none; + font-size: .85em; + li + { + margin: 5px 0px; + } + } } .message-container { diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index dd5aa9ae08..f7c0d4d568 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -17,14 +17,6 @@
        -
        -

        Problem Information

        -
        -
        -

        Maching Learning Information

        -
        -
        -

        Staff grading

        @@ -45,6 +37,14 @@

        +
        +

        Problem Information

        +
        +
        +

        Maching Learning Information

        +
        +
        +

        Question

        @@ -72,10 +72,10 @@
        -

        +
        From 7f9fcfffdfa8a1fbed22fdb399fdf886c516ff49 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 7 Dec 2012 16:29:57 -0500 Subject: [PATCH 13/17] Add support for the new staff grading views on the backend --- .../instructor/staff_grading_service.py | 88 ++++++++++++++++--- lms/urls.py | 4 + 2 files changed, 80 insertions(+), 12 deletions(-) diff --git a/lms/djangoapps/instructor/staff_grading_service.py b/lms/djangoapps/instructor/staff_grading_service.py index b3b0f99e74..6d47767c35 100644 --- a/lms/djangoapps/instructor/staff_grading_service.py +++ b/lms/djangoapps/instructor/staff_grading_service.py @@ -29,11 +29,16 @@ class MockStaffGradingService(object): def __init__(self): self.cnt = 0 - def get_next(self, course_id, grader_id): + def get_next(self,course_id, location, grader_id): self.cnt += 1 return json.dumps({'success': True, 'submission_id': self.cnt, 'submission': 'Test submission {cnt}'.format(cnt=self.cnt), + 'num_graded': 3, + 'min_for_ml': 5, + 'num_pending': 4, + 'prompt': 'This is a fake prompt', + 'ml_error_info': 'ML info', 'max_score': 2 + self.cnt % 3, 'rubric': 'A rubric'}) @@ -53,6 +58,7 @@ class StaffGradingService(object): self.login_url = self.url + '/login/' self.get_next_url = self.url + '/get_next_submission/' self.save_grade_url = self.url + '/save_grade/' + self.get_problem_list_url = self.url + '/get_problem_list/' self.session = requests.session() @@ -83,9 +89,8 @@ class StaffGradingService(object): Returns the result of operation(). Does not catch exceptions. """ response = operation() - if (response.json - and response.json.get('success') == False - and response.json.get('error') == 'login_required'): + log.debug("first response: {0}".format(response.status_code)) + if (response.status_code == 302): # apparrently we aren't logged in. Try to fix that. r = self._login() if r and not r.get('success'): @@ -96,13 +101,44 @@ class StaffGradingService(object): return response + def get_problem_list(self, course_id, grader_id): + """ + Get the list of problems for a given course. - def get_next(self, course_id, grader_id): + Args: + course_id: course id that we want the problems of + grader_id: who is grading this? The anonymous user_id of the grader. + + Returns: + json string with the response from the service. (Deliberately not + writing out the fields here--see the docs on the staff_grading view + in the grading_controller repo) + + Raises: + GradingServiceError: something went wrong with the connection. + """ + op = lambda: self.session.get(self.get_problem_list_url, + allow_redirects = False, + params={'course_id': course_id, + 'grader_id': grader_id}) + try: + r = self._try_with_login(op) + log.debug(r.text) + except (RequestException, ConnectionError, HTTPError) as err: + # reraise as promised GradingServiceError, but preserve stacktrace. + raise GradingServiceError, str(err), sys.exc_info()[2] + + return r.text + + + def get_next(self, course_id, location, grader_id): """ Get the next thing to grade. Args: - course_id: course id to get submission for + course_id: the course that this problem belongs to + location: location of the problem that we are grading and would like the + next submission for grader_id: who is grading this? The anonymous user_id of the grader. Returns: @@ -115,7 +151,7 @@ class StaffGradingService(object): """ op = lambda: self.session.get(self.get_next_url, allow_redirects=False, - params={'course_id': course_id, + params={'location': location, 'grader_id': grader_id}) try: r = self._try_with_login(op) @@ -198,7 +234,8 @@ def _check_access(user, course_id): def get_next(request, course_id): """ - Get the next thing to grade for course_id. + Get the next thing to grade for course_id and with the location specified + in the . Returns a json dict with the following keys: @@ -218,17 +255,44 @@ def get_next(request, course_id): """ _check_access(request.user, course_id) - return HttpResponse(_get_next(course_id, request.user.id), + required = set('location') + if request.method != 'POST': + raise Http404 + actual = set(request.POST.keys()) + missing = required - actual + if len(missing) != 0: + return _err_response('Missing required keys {0}'.format( + ', '.join(missing))) + grader_id = request.user.id + p = request.POST + + return HttpResponse(_get_next(course_id, request.user.id, p.location), mimetype="application/json") -def _get_next(course_id, grader_id): +def get_problem_list(request, course_id): + """ + Get all the problems for the given course id + TODO: fill in all of this stuff + """ + _check_access(request.user, course_id) + try: + response = grading_service().get_problem_list(course_id, request.user.id) + return HttpResponse(response, + mimetype="application/json") + except GradingServiceError: + log.exception("Error from grading service. server url: {0}" + .format(grading_service().url)) + return HttpResponse(json.dumps({'success': False, + 'error': 'Could not connect to grading service'})) + + +def _get_next(course_id, grader_id, location): """ Implementation of get_next (also called from save_grade) -- returns a json string """ - try: - return grading_service().get_next(course_id, grader_id) + return grading_service().get_next(course_id, location, grader_id) except GradingServiceError: log.exception("Error from grading service. server url: {0}" .format(grading_service().url)) diff --git a/lms/urls.py b/lms/urls.py index 01fbc8cc1d..11283d4348 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -240,6 +240,10 @@ if settings.COURSEWARE_ENABLED: 'instructor.staff_grading_service.get_next', name='staff_grading_get_next'), url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/staff_grading/save_grade$', 'instructor.staff_grading_service.save_grade', name='staff_grading_save_grade'), + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/staff_grading/save_grade$', + 'instructor.staff_grading_service.save_grade', name='staff_grading_save_grade'), + url(r'^courses/(?P[^/]+/[^/]+/[^/]+)/staff_grading/get_problem_list$', + 'instructor.staff_grading_service.get_problem_list', name='staff_grading_get_problem_list'), ) # discussion forums live within courseware, so courseware must be enabled first From 4ccf7c351d9dad845d772b40ec7ddafbafe2435b Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 7 Dec 2012 16:56:51 -0500 Subject: [PATCH 14/17] Fix service errors so that individual problems can now be displayed and graded --- .../instructor/staff_grading_service.py | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lms/djangoapps/instructor/staff_grading_service.py b/lms/djangoapps/instructor/staff_grading_service.py index 6d47767c35..65302c2881 100644 --- a/lms/djangoapps/instructor/staff_grading_service.py +++ b/lms/djangoapps/instructor/staff_grading_service.py @@ -89,7 +89,6 @@ class StaffGradingService(object): Returns the result of operation(). Does not catch exceptions. """ response = operation() - log.debug("first response: {0}".format(response.status_code)) if (response.status_code == 302): # apparrently we aren't logged in. Try to fix that. r = self._login() @@ -123,7 +122,6 @@ class StaffGradingService(object): 'grader_id': grader_id}) try: r = self._try_with_login(op) - log.debug(r.text) except (RequestException, ConnectionError, HTTPError) as err: # reraise as promised GradingServiceError, but preserve stacktrace. raise GradingServiceError, str(err), sys.exc_info()[2] @@ -255,18 +253,19 @@ def get_next(request, course_id): """ _check_access(request.user, course_id) - required = set('location') + required = set(['location']) if request.method != 'POST': raise Http404 actual = set(request.POST.keys()) missing = required - actual - if len(missing) != 0: + if len(missing) > 0: return _err_response('Missing required keys {0}'.format( ', '.join(missing))) grader_id = request.user.id p = request.POST + location = p['location'] - return HttpResponse(_get_next(course_id, request.user.id, p.location), + return HttpResponse(_get_next(course_id, request.user.id, location), mimetype="application/json") @@ -319,15 +318,17 @@ def save_grade(request, course_id): if request.method != 'POST': raise Http404 - required = set('score', 'feedback', 'submission_id') + required = set(['score', 'feedback', 'submission_id', 'location']) actual = set(request.POST.keys()) + log.debug(actual) missing = required - actual - if len(missing) != 0: + if len(missing) > 0: return _err_response('Missing required keys {0}'.format( ', '.join(missing))) grader_id = request.user.id p = request.POST + location = p['location'] try: result_json = grading_service().save_grade(course_id, @@ -350,6 +351,6 @@ def save_grade(request, course_id): return _err_response('Grading service failed') # Ok, save_grade seemed to work. Get the next submission to grade. - return HttpResponse(_get_next(course_id, grader_id), + return HttpResponse(_get_next(course_id, grader_id, location), mimetype="application/json") From 76f76a4ff8f3c58755ec0b72a2ee8fb1c7960e61 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Fri, 7 Dec 2012 17:30:03 -0500 Subject: [PATCH 15/17] Update tests and mock objects to work with the new API --- lms/djangoapps/instructor/staff_grading_service.py | 2 +- lms/djangoapps/instructor/tests.py | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/lms/djangoapps/instructor/staff_grading_service.py b/lms/djangoapps/instructor/staff_grading_service.py index 65302c2881..8c9e02bdae 100644 --- a/lms/djangoapps/instructor/staff_grading_service.py +++ b/lms/djangoapps/instructor/staff_grading_service.py @@ -43,7 +43,7 @@ class MockStaffGradingService(object): 'rubric': 'A rubric'}) def save_grade(self, course_id, grader_id, submission_id, score, feedback): - return self.get_next(course_id, grader_id) + return self.get_next(course_id, 'fake location', grader_id) class StaffGradingService(object): diff --git a/lms/djangoapps/instructor/tests.py b/lms/djangoapps/instructor/tests.py index c47eb170fc..4b5ab7e309 100644 --- a/lms/djangoapps/instructor/tests.py +++ b/lms/djangoapps/instructor/tests.py @@ -236,6 +236,7 @@ class TestStaffGradingService(ct.PageLoader): self.student = 'view@test.com' self.instructor = 'view2@test.com' self.password = 'foo' + self.location = 'TestLocation' self.create_account('u1', self.student, self.password) self.create_account('u2', self.instructor, self.password) self.activate_user(self.student) @@ -271,8 +272,9 @@ class TestStaffGradingService(ct.PageLoader): self.login(self.instructor, self.password) url = reverse('staff_grading_get_next', kwargs={'course_id': self.course_id}) + data = {'location': self.location} - r = self.check_for_get_code(200, url) + r = self.check_for_post_code(200, url, data) d = json.loads(r.content) self.assertTrue(d['success']) self.assertEquals(d['submission_id'], self.mock_service.cnt) @@ -285,7 +287,8 @@ class TestStaffGradingService(ct.PageLoader): data = {'score': '12', 'feedback': 'great!', - 'submission_id': '123'} + 'submission_id': '123', + 'location': self.location} r = self.check_for_post_code(200, url, data) d = json.loads(r.content) self.assertTrue(d['success'], str(d)) From c9d399d3bd072c6c391ebbe95e90446974371a37 Mon Sep 17 00:00:00 2001 From: Diana Huang Date: Mon, 10 Dec 2012 09:35:30 -0500 Subject: [PATCH 16/17] Update instructor grading message with more accurate information --- lms/templates/instructor/staff_grading.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lms/templates/instructor/staff_grading.html b/lms/templates/instructor/staff_grading.html index f7c0d4d568..2af7734a74 100644 --- a/lms/templates/instructor/staff_grading.html +++ b/lms/templates/instructor/staff_grading.html @@ -27,7 +27,7 @@

        Instructions

        -

        This is the list of problems that current need to be graded in order to train the machine learning models. Each problem needs to be trained separately, and we have indicated the number of student submissions that need to be graded in order for a model to be generated. Any number of problems can be graded, not just the amount required to create the model.

        +

        This is the list of problems that current need to be graded in order to train the machine learning models. Each problem needs to be trained separately, and we have indicated the number of student submissions that need to be graded in order for a model to be generated. You can grade more than the minimum required number of submissions--this will improve the accuracy of machine learning, though with diminishing returns. You can see the current accuracy of machine learning while grading.

        Problem List

        From 85b5190c29bc365739b1d5a500a7496ae0e97877 Mon Sep 17 00:00:00 2001 From: Victor Shnayder Date: Mon, 10 Dec 2012 10:41:40 -0500 Subject: [PATCH 17/17] fix login hack. Uses new return-error-msg-if-not-logged-in behavior --- lms/djangoapps/instructor/staff_grading_service.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lms/djangoapps/instructor/staff_grading_service.py b/lms/djangoapps/instructor/staff_grading_service.py index 8c9e02bdae..33c1d875af 100644 --- a/lms/djangoapps/instructor/staff_grading_service.py +++ b/lms/djangoapps/instructor/staff_grading_service.py @@ -89,7 +89,9 @@ class StaffGradingService(object): Returns the result of operation(). Does not catch exceptions. """ response = operation() - if (response.status_code == 302): + if (response.json + and response.json.get('success') == False + and response.json.get('error') == 'login_required'): # apparrently we aren't logged in. Try to fix that. r = self._login() if r and not r.get('success'): @@ -116,7 +118,7 @@ class StaffGradingService(object): Raises: GradingServiceError: something went wrong with the connection. """ - op = lambda: self.session.get(self.get_problem_list_url, + op = lambda: self.session.get(self.get_problem_list_url, allow_redirects = False, params={'course_id': course_id, 'grader_id': grader_id})