Handle rubric criteria/ratings with large IDs
When handling rubric creation requests via the API, do not use the ID of incoming rubric criteria (or ratings on an individual criterion) as an index to store the item in an array. Doing so caused IDs like 297203574 to create an array with 297 million elements to store rubric criteria and then operate on that array before throwing away the empty values (and doing the same thing when storing ratings for specific criteria), which slowed the process to a crawl. fixes EVAL-1029 flag=none Test plan: - Make a POST request to the rubric creation API endpoint for an existing course, using excessively large values for the IDs (for a sample request see APPENDIX below): - /api/v1/courses/<courseID>/rubrics - This should complete quickly (within a couple seconds if Rails doesn't have to reload) and certainly should not take over a minute - Adding additional criteria should not cause a noticeable increase in the time taken - Test that updating an existing rubric (using a PUT request) does not take forever - Test that creating/updating a rubric via Canvas itself still works as before APPENDIX: sample request that took over a minute without this patchset but should finish very quickly with it { "rubric": { "title": "Canvas Test Rubric - edit", "free_form_criterion_comments": false, "criteria": { "297203574": { "id": "297203574", "points": 5.0, "description": "First item", "long_description": "", "criterion_use_range": false, "ratings": { "229567777": { "id": "229567777", "points": 5.0, "description": "Very High", "long_description": "no" }, "229567778": { "id": "229567778", "points": 4.0, "description": "High", "long_description": "no" }, "229567779": { "id": "229567779", "points": 3.0, "description": "Achievement Standard", "long_description": "no" }, "229567780": { "id": "229567780", "points": 2.0, "description": "Developing", "long_description": "no" }, "229567781": { "id": "229567781", "points": 1.0, "description": "Emerging", "long_description": "no" } } }, "297203575": { "id": "297203575", "points": 5.0, "description": "Second item", "long_description": "", "criterion_use_range": false, "ratings": { "229568289": { "id": "229568289", "points": 5.0, "description": "Very High", "long_description": "no" }, "229568290": { "id": "229568290", "points": 4.0, "description": "High", "long_description": "no" }, "229568291": { "id": "229568291", "points": 3.0, "description": "Achievement Standard", "long_description": "no" }, "229568292": { "id": "229568292", "points": 2.0, "description": "Developing", "long_description": "no" }, "229568293": { "id": "229568293", "points": 1.0, "description": "Emerging", "long_description": "no" } } }, "297203576": { "id": "297203576", "points": 5.0, "description": "third item", "long_description": "", "criterion_use_range": false, "ratings": { "229568801": { "id": "229568801", "points": 5.0, "description": "Very High", "long_description": "no" }, "229568802": { "id": "229568802", "points": 4.0, "description": "High", "long_description": "no" }, "229568803": { "id": "229568803", "points": 3.0, "description": "Achievement Standard", "long_description": "no" }, "229568804": { "id": "229568804", "points": 2.0, "description": "Developing", "long_description": "no" }, "229568805": { "id": "229568805", "points": 1.0, "description": "Emerging", "long_description": "no" } } }, "297203577": { "id": "297203577", "points": 5.0, "description": "fourth item", "long_description": "", "criterion_use_range": false, "ratings": { "229569313": { "id": "229569313", "points": 5.0, "description": "Very High", "long_description": "no" }, "229569314": { "id": "229569314", "points": 4.0, "description": "High", "long_description": "no" }, "229569315": { "id": "229569315", "points": 3.0, "description": "Achievement Standard", "long_description": "no" }, "229569316": { "id": "229569316", "points": 2.0, "description": "Developing", "long_description": "no" }, "229569317": { "id": "229569317", "points": 1.0, "description": "Emerging", "long_description": "no" } } }, "297203578": { "id": "297203578", "points": 5.0, "description": "fifth item", "long_description": "", "criterion_use_range": false, "ratings": { "229569825": { "id": "229569825", "points": 5.0, "description": "Very High", "long_description": "no" }, "229569826": { "id": "229569826", "points": 4.0, "description": "High", "long_description": "no" }, "229569827": { "id": "229569827", "points": 3.0, "description": "Achievement Standard", "long_description": "no" }, "229569828": { "id": "229569828", "points": 2.0, "description": "Developing", "long_description": "no" }, "229569829": { "id": "229569829", "points": 1.0, "description": "Emerging", "long_description": "no" } } } } } } Change-Id: I33a6cb77c8cda3a4ce0ea949da83281d822e0a12 Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/249271 Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com> Product-Review: Syed Hussain <shussain@instructure.com> Reviewed-by: Gary Mei <gmei@instructure.com> Reviewed-by: Spencer Olson <solson@instructure.com> QA-Review: Kai Bjorkman <kbjorkman@instructure.com>
This commit is contained in:
parent
a0e230a850
commit
bb6abb0c38
|
@ -346,7 +346,6 @@ class Rubric < ActiveRecord::Base
|
||||||
criterion_data[:id] = nil if criterion_data[:id] && criterion_data[:id].empty?
|
criterion_data[:id] = nil if criterion_data[:id] && criterion_data[:id].empty?
|
||||||
criterion[:id] = unique_item_id(criterion_data[:id])
|
criterion[:id] = unique_item_id(criterion_data[:id])
|
||||||
criterion[:criterion_use_range] = [true, 'true'].include?(criterion_data[:criterion_use_range])
|
criterion[:criterion_use_range] = [true, 'true'].include?(criterion_data[:criterion_use_range])
|
||||||
ratings = []
|
|
||||||
if criterion_data[:learning_outcome_id].present?
|
if criterion_data[:learning_outcome_id].present?
|
||||||
outcome = LearningOutcome.where(id: criterion_data[:learning_outcome_id]).first
|
outcome = LearningOutcome.where(id: criterion_data[:learning_outcome_id]).first
|
||||||
criterion[:long_description] = outcome&.description || ''
|
criterion[:long_description] = outcome&.description || ''
|
||||||
|
@ -356,16 +355,20 @@ class Rubric < ActiveRecord::Base
|
||||||
criterion[:ignore_for_scoring] = criterion_data[:ignore_for_scoring] == '1'
|
criterion[:ignore_for_scoring] = criterion_data[:ignore_for_scoring] == '1'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
(criterion_data[:ratings] || {}).each do |jdx, rating_data|
|
|
||||||
|
ratings = (criterion_data[:ratings] || {}).values.map do |rating_data|
|
||||||
rating_data[:id]&.strip!
|
rating_data[:id]&.strip!
|
||||||
rating = criterion_rating(rating_data, criterion[:id])
|
criterion_rating(rating_data, criterion[:id])
|
||||||
ratings[jdx.to_i] = rating
|
|
||||||
end
|
end
|
||||||
criterion[:ratings] = ratings.select{|r| r}.sort_by{|r| [-1 * (r[:points] || 0), r[:description] || CanvasSort::First]}
|
criterion[:ratings] = ratings.sort_by { |r| [-1 * (r[:points] || 0), r[:description] || CanvasSort::First] }
|
||||||
criterion[:points] = criterion[:ratings].map{|r| r[:points]}.max || 0
|
criterion[:points] = criterion[:ratings].map{|r| r[:points]}.max || 0
|
||||||
criteria[idx.to_i] = criterion
|
|
||||||
|
# Record both the criterion data and the original ID that was passed in
|
||||||
|
# (we'll use the ID when we sort the criteria below)
|
||||||
|
criteria.push([idx, criterion])
|
||||||
end
|
end
|
||||||
criteria = criteria.compact
|
criteria = criteria.sort_by { |criterion| criterion.first&.to_i || CanvasSort::First }.
|
||||||
|
map(&:second)
|
||||||
points_possible = total_points_from_criteria(criteria)&.round(POINTS_POSSIBLE_PRECISION)
|
points_possible = total_points_from_criteria(criteria)&.round(POINTS_POSSIBLE_PRECISION)
|
||||||
CriteriaData.new(criteria, points_possible, title)
|
CriteriaData.new(criteria, points_possible, title)
|
||||||
end
|
end
|
||||||
|
|
|
@ -526,6 +526,74 @@ describe Rubric do
|
||||||
expect(@rubric.criteria[1][:long_description]).to eq '<p>This is <b>awesome</b>.</p>'
|
expect(@rubric.criteria[1][:long_description]).to eq '<p>This is <b>awesome</b>.</p>'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe "ordering of contents" do
|
||||||
|
let(:rubric) { Rubric.new }
|
||||||
|
|
||||||
|
it "sorts criteria based on the numerical values of their hash keys" do
|
||||||
|
rubric.update_criteria(
|
||||||
|
criteria: {
|
||||||
|
"206000" => {
|
||||||
|
description: "aaaaa",
|
||||||
|
ratings: { '0' => { description: "" } }
|
||||||
|
},
|
||||||
|
"106215" => {
|
||||||
|
description: "bbbbb",
|
||||||
|
ratings: { '0' => { description: "" } }
|
||||||
|
},
|
||||||
|
"6043341" => {
|
||||||
|
description: "ccccc",
|
||||||
|
ratings: { '0' => { description: "" } }
|
||||||
|
},
|
||||||
|
"fred" => {
|
||||||
|
description: "ddddd",
|
||||||
|
ratings: { '0' => { description: "" } }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: "my rubric"
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(rubric.criteria.pluck(:description)).to eq ["ddddd", "bbbbb", "aaaaa", "ccccc"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sorts ratings within each criterion by the number of points in descending order" do
|
||||||
|
rubric.update_criteria(
|
||||||
|
criteria: {
|
||||||
|
"1" => {
|
||||||
|
description: "aaaaa",
|
||||||
|
ratings: {
|
||||||
|
"0" => { description: "ok", points: 5 },
|
||||||
|
"1" => { description: "good", points: 10 },
|
||||||
|
"2" => { description: "bad" }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: "my rubric"
|
||||||
|
)
|
||||||
|
|
||||||
|
criterion = rubric.criteria.first
|
||||||
|
expect(criterion[:ratings].pluck(:description)).to eq ["good", "ok", "bad"]
|
||||||
|
end
|
||||||
|
|
||||||
|
it "sorts ratings with the same number of points by description" do
|
||||||
|
rubric.update_criteria(
|
||||||
|
criteria: {
|
||||||
|
"1" => {
|
||||||
|
description: "aaaaa",
|
||||||
|
ratings: {
|
||||||
|
"0" => { description: "ok", points: 5 },
|
||||||
|
"1" => { description: "also ok", points: 5 },
|
||||||
|
"2" => { description: "ok too", points: 5 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
title: "my rubric"
|
||||||
|
)
|
||||||
|
|
||||||
|
criterion = rubric.criteria.first
|
||||||
|
expect(criterion[:ratings].pluck(:description)).to eq ["also ok", "ok", "ok too"]
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'create' do
|
describe 'create' do
|
||||||
|
|
Loading…
Reference in New Issue