add hide_points to json response on lmgb
closes OUT-2300 test plan (dev-qa): - create a new outcome - create a new rubric and align the outcome - align the rubric to a new assignment, ensure you choose the box to remove points from rubric - assess a student - go to the LMGB - the outcome_rollups response should include a hide_points field in the json - hide_points should be 'true' on the rollup for the new outcome with the newly created outcome - attach the newly create outcome to a different assignment, one that doesn't have its points hidden on the rubric association - assess the student - return to lmgb - hide_points should be 'false' for the rollup on the new outcome Change-Id: I0f9accb4ff0cc76f40c08ce08e21091e35df6af8 Reviewed-on: https://gerrit.instructure.com/153547 Tested-by: Jenkins Reviewed-by: Michael Brewer-Davis <mbd@instructure.com> Reviewed-by: Augusto Callejas <acallejas@instructure.com> Product-Review: Michael Brewer-Davis <mbd@instructure.com> QA-Review: Michael Brewer-Davis <mbd@instructure.com>
This commit is contained in:
parent
cb73718207
commit
e924133cb8
|
@ -17,7 +17,9 @@
|
|||
|
||||
module RollupScoreAggregatorHelper
|
||||
def aggregate_score
|
||||
(scores.sum.to_f / scores.size).round(2)
|
||||
scores = score_sets.pluck(:score)
|
||||
agg_score = (scores.sum.to_f / scores.size).round(2)
|
||||
{score: agg_score, results: score_sets.pluck(:result)}
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -42,7 +44,8 @@ module RollupScoreAggregatorHelper
|
|||
|
||||
def retrieve_scores(results)
|
||||
results.map do |result|
|
||||
quiz_score?(result) ? scaled_score_from_result(result) : result_score(result)
|
||||
score = quiz_score?(result) ? scaled_score_from_result(result) : result_score(result)
|
||||
{score: score, result: result}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -90,15 +93,15 @@ module RollupScoreAggregatorHelper
|
|||
end
|
||||
end
|
||||
|
||||
def scores
|
||||
@scores || begin
|
||||
def score_sets
|
||||
@score_sets || begin
|
||||
case @calculation_method
|
||||
when 'decaying_average'
|
||||
@scores = retrieve_scores(@aggregate ? @outcome_results : sorted_results)
|
||||
@score_sets = retrieve_scores(@aggregate ? @outcome_results : sorted_results)
|
||||
when 'n_mastery', 'highest'
|
||||
@scores = retrieve_scores(@outcome_results)
|
||||
@score_sets = retrieve_scores(@outcome_results)
|
||||
when 'latest'
|
||||
@scores = retrieve_scores(@aggregate ? @outcome_results : [sorted_results.last])
|
||||
@score_sets = retrieve_scores(@aggregate ? @outcome_results : [sorted_results.last])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ class RollupScore
|
|||
|
||||
PRECISION = 2
|
||||
|
||||
attr_reader :outcome_results, :outcome, :score, :count, :title, :submitted_at
|
||||
attr_reader :outcome_results, :outcome, :score, :count, :title, :submitted_at, :hide_points
|
||||
def initialize(outcome_results, opts={})
|
||||
@outcome_results = outcome_results
|
||||
@aggregate = opts[:aggregate_score]
|
||||
|
@ -30,7 +30,9 @@ class RollupScore
|
|||
@mastery_points = @outcome.rubric_criterion[:mastery_points]
|
||||
@calculation_method = @outcome.calculation_method || "highest"
|
||||
@calculation_int = @outcome.calculation_int
|
||||
@score = @aggregate ? aggregate_score : calculate_results
|
||||
score_set = @aggregate ? aggregate_score : calculate_results
|
||||
@score = score_set[:score] if score_set
|
||||
@hide_points = score_set[:results].all?(&:hide_points) if score_set
|
||||
latest_result unless @aggregate
|
||||
end
|
||||
|
||||
|
@ -40,27 +42,32 @@ class RollupScore
|
|||
case @calculation_method
|
||||
when 'decaying_average'
|
||||
return nil if @outcome_results.empty?
|
||||
decaying_average
|
||||
decaying_average_set
|
||||
when 'n_mastery'
|
||||
return nil if @outcome_results.length < @calculation_int
|
||||
n_mastery
|
||||
n_mastery_set
|
||||
when 'latest'
|
||||
scores.first.round(PRECISION)
|
||||
latest_set = score_sets.first
|
||||
{score: latest_set[:score].round(PRECISION), results: [latest_set[:result]]}
|
||||
when 'highest'
|
||||
scores.max.round(PRECISION)
|
||||
highest_set = score_sets.max_by{|set| set[:score]}
|
||||
{score: highest_set[:score].round(PRECISION), results: [highest_set[:result]]}
|
||||
end
|
||||
end
|
||||
|
||||
def n_mastery
|
||||
def n_mastery_set
|
||||
return unless @outcome.rubric_criterion
|
||||
# mastery_points represents the cutoff score for which results
|
||||
# will be considered towards mastery
|
||||
tmp_scores = scores.compact.delete_if{|score| score < @mastery_points}
|
||||
return nil if tmp_scores.length < @calculation_int
|
||||
(tmp_scores.sum.to_f / tmp_scores.size).round(PRECISION)
|
||||
tmp_score_sets = score_sets.compact.delete_if{|set| set[:score] < @mastery_points}
|
||||
return nil if tmp_score_sets.length < @calculation_int
|
||||
|
||||
tmp_scores = tmp_score_sets.pluck(:score)
|
||||
n_mastery_score = (tmp_scores.sum.to_f / tmp_scores.size).round(PRECISION)
|
||||
{score: n_mastery_score, results: tmp_score_sets.pluck(:result)}
|
||||
end
|
||||
|
||||
def decaying_average
|
||||
def decaying_average_set
|
||||
# The term "decaying average" can mean different things depending on the user.
|
||||
# There are multiple, reasonable, accurate interpretations. We have chosen
|
||||
# to go with one that is more mathematically a "weighted average", but is
|
||||
|
@ -69,13 +76,18 @@ class RollupScore
|
|||
|
||||
# default grading method with weight of 65 if none selected.
|
||||
weight = @calculation_int || 65
|
||||
tmp_scores = scores
|
||||
latest = tmp_scores.pop
|
||||
return latest.round(PRECISION) if tmp_scores.empty?
|
||||
tmp_score_sets = score_sets
|
||||
latest = tmp_score_sets.pop
|
||||
|
||||
latest_weighted = latest * (0.01 * weight)
|
||||
if tmp_score_sets.empty?
|
||||
return { score: latest[:score].round(PRECISION), results: [latest[:result]] }
|
||||
end
|
||||
|
||||
tmp_scores = tmp_score_sets.pluck(:score)
|
||||
latest_weighted = latest[:score] * (0.01 * weight)
|
||||
older_avg_weighted = (tmp_scores.sum / tmp_scores.length) * (0.01 * (100 - weight))
|
||||
(latest_weighted + older_avg_weighted).round(PRECISION)
|
||||
decaying_avg_score = (latest_weighted + older_avg_weighted).round(PRECISION)
|
||||
{score: decaying_avg_score, results: tmp_score_sets.pluck(:result).push(latest[:result])}
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -204,6 +204,7 @@ module Api::V1::OutcomeResults
|
|||
title: score.title,
|
||||
submitted_at: score.submitted_at,
|
||||
count: score.count,
|
||||
hide_points: score.hide_points,
|
||||
links: {outcome: score.outcome.id.to_s},
|
||||
}
|
||||
end
|
||||
|
|
|
@ -20,7 +20,7 @@ module Outcomes
|
|||
module ResultAnalytics
|
||||
|
||||
Rollup = Struct.new(:context, :scores)
|
||||
Result = Struct.new(:learning_outcome, :score, :count)
|
||||
Result = Struct.new(:learning_outcome, :score, :count, :hide_points)
|
||||
|
||||
# Public: Queries learning_outcome_results for rollup.
|
||||
#
|
||||
|
@ -81,7 +81,7 @@ module Outcomes
|
|||
rollup_scores = rollups.map(&:scores).flatten
|
||||
outcome_results = rollup_scores.group_by(&:outcome).values
|
||||
aggregate_results = outcome_results.map do |scores|
|
||||
scores.map{|score| Result.new(score.outcome, score.score, score.count)}
|
||||
scores.map{|score| Result.new(score.outcome, score.score, score.count, score.hide_points)}
|
||||
end
|
||||
aggregate_rollups = aggregate_results.map do |result|
|
||||
RollupScore.new(result,{aggregate_score: true})
|
||||
|
|
|
@ -248,7 +248,7 @@ describe "Outcome Results API", type: :request do
|
|||
expect(rollup['links']['user']).to eq outcome_student.id.to_s
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq 1
|
||||
expect(score['score']).to eq first_outcome_rating[:points]
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
@ -283,7 +283,7 @@ describe "Outcome Results API", type: :request do
|
|||
expect(student_ids).to be_include(rollup['links']['user'])
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq 1
|
||||
expect([0,1]).to be_include(score['score'])
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
@ -342,7 +342,7 @@ describe "Outcome Results API", type: :request do
|
|||
expect(outcome_course_sections[0].student_ids.map(&:to_s)).to be_include(rollup['links']['user'])
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq 1
|
||||
expect([0,2]).to be_include(score['score'])
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
@ -491,7 +491,7 @@ describe "Outcome Results API", type: :request do
|
|||
rollup['links']['course'] == @course.id.to_s
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq 1
|
||||
expect(score['score']).to eq first_outcome_rating[:points]
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
@ -516,7 +516,7 @@ describe "Outcome Results API", type: :request do
|
|||
expect(rollup['links']['course']).to eq @course.id.to_s
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq 2
|
||||
expect(score['score']).to eq 0.5
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
@ -541,7 +541,7 @@ describe "Outcome Results API", type: :request do
|
|||
expect(rollup['links']['course']).to eq outcome_course.id.to_s
|
||||
expect(rollup['scores'].size).to eq 1
|
||||
rollup['scores'].each do |score|
|
||||
expect(score.keys.sort).to eq %w(count links score submitted_at title)
|
||||
expect(score.keys.sort).to eq %w(count hide_points links score submitted_at title)
|
||||
expect(score['count']).to eq outcome_course_sections[0].enrollments.count
|
||||
expect(score['score']).to eq 1
|
||||
expect(score['links'].keys.sort).to eq %w(outcome)
|
||||
|
|
|
@ -29,7 +29,7 @@ describe Outcomes::ResultAnalytics do
|
|||
# the surrounding database logic
|
||||
MockUser = Struct.new(:id, :name)
|
||||
MockOutcome = Struct.new(:id, :calculation_method, :calculation_int, :rubric_criterion)
|
||||
class MockOutcomeResult < Struct.new(:user, :learning_outcome, :score, :title, :submitted_at, :assessed_at, :artifact_type, :percent, :possible, :association_id, :association_type)
|
||||
class MockOutcomeResult < Struct.new(:user, :learning_outcome, :score, :title, :submitted_at, :assessed_at, :hide_points, :artifact_type, :percent, :possible, :association_id, :association_type)
|
||||
def initialize *args
|
||||
return super unless (args.first.is_a?(Hash) && args.length == 1)
|
||||
args.first.each_pair do |k, v|
|
||||
|
@ -50,7 +50,7 @@ describe Outcomes::ResultAnalytics do
|
|||
title = args[:title] || "name, o1"
|
||||
outcome = args[:outcome] || create_outcome(args)
|
||||
user = args[:user] || MockUser[10, 'a']
|
||||
MockOutcomeResult[user, outcome, score, title, args[:submitted_time], args[:assessed_time]]
|
||||
MockOutcomeResult[user, outcome, score, title, args[:submitted_time], args[:assessed_time], args[:hide_points]]
|
||||
end
|
||||
|
||||
def create_outcome(args)
|
||||
|
@ -206,6 +206,24 @@ describe Outcomes::ResultAnalytics do
|
|||
expect(rollup.scores.map(&:outcome_results).flatten).to eq rollup_scores.find_all{|score| score.user.id == rollup.context.id}
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns hide_points value of true if all results have hide_points set to true' do
|
||||
results = [
|
||||
outcome_from_score(4.0,{hide_points: true}),
|
||||
outcome_from_score(5.0, {hide_points: true}),
|
||||
]
|
||||
rollups = ra.rollup_user_results(results)
|
||||
expect(rollups[0].hide_points).to be true
|
||||
end
|
||||
|
||||
it 'returns hide_points value of false if any results have hide_points set to false' do
|
||||
results = [
|
||||
outcome_from_score(4.0,{hide_points: true}),
|
||||
outcome_from_score(5.0,{hide_points: false}),
|
||||
]
|
||||
rollups = ra.rollup_user_results(results)
|
||||
expect(rollups[0].hide_points).to be false
|
||||
end
|
||||
end
|
||||
|
||||
describe '#aggregate_outcome_results_rollup' do
|
||||
|
|
Loading…
Reference in New Issue