jsonapi-ify outcome results api
fixes CNVS-10198, CNVS-10201 test plan - test outcome result api as in c/27631 - ensure that jsonapi paging metadata is returned - fetch outcome results for a course that includes students without outcome results (i.e. haven't submitted anything) - ensure that the student is listed in the returned results with an empty scores array Change-Id: I00d8e9de241a243fb6ac1aa9f55150b8955a2452 Reviewed-on: https://gerrit.instructure.com/28015 Tested-by: Jenkins <jenkins@instructure.com> Reviewed-by: Joel Hough <joel@instructure.com> QA-Review: Steven Shepherd <sshepherd@instructure.com> Product-Review: Zach Pendleton <zachp@instructure.com> Product-Review: Joel Hough <joel@instructure.com>
This commit is contained in:
parent
9af963c357
commit
0d1c04cbf1
|
@ -23,14 +23,15 @@
|
||||||
#
|
#
|
||||||
# @object OutcomeRollupScore
|
# @object OutcomeRollupScore
|
||||||
# {
|
# {
|
||||||
#
|
|
||||||
# // The id of the related outcome
|
|
||||||
# "outcome_id": 42,
|
|
||||||
#
|
|
||||||
# // The rollup score for the outcome, based on the student assessment
|
# // The rollup score for the outcome, based on the student assessment
|
||||||
# // scores related to the outcome. This could be null if the student has
|
# // scores related to the outcome. This could be null if the student has
|
||||||
# // no related scores.
|
# // no related scores.
|
||||||
# "score": 3
|
# "score": 3,
|
||||||
|
#
|
||||||
|
# "links": {
|
||||||
|
# // The id of the related outcome
|
||||||
|
# "outcome": 42
|
||||||
|
# }
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# @object OutcomeRollup
|
# @object OutcomeRollup
|
||||||
|
@ -42,7 +43,12 @@
|
||||||
# "id": 42,
|
# "id": 42,
|
||||||
#
|
#
|
||||||
# // The name of the resource for this rollup. For example, the user name.
|
# // The name of the resource for this rollup. For example, the user name.
|
||||||
# "name": "John Doe"
|
# "name": "John Doe",
|
||||||
|
#
|
||||||
|
# "links": {
|
||||||
|
# // (Optional) The id of the section this resource is in
|
||||||
|
# "section": 57
|
||||||
|
# }
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
|
|
||||||
|
@ -69,12 +75,12 @@ class OutcomeResultsController < ApplicationController
|
||||||
# }
|
# }
|
||||||
def rollups
|
def rollups
|
||||||
@outcomes = @context.linked_learning_outcomes
|
@outcomes = @context.linked_learning_outcomes
|
||||||
@users = users_for_outcome_context
|
|
||||||
# TODO: will this work if users are spread across shards?
|
# TODO: will this work if users are spread across shards?
|
||||||
@users = Api.paginate(@users, self, api_v1_course_outcome_rollups_url(@context))
|
@users = Api.paginate(users_for_outcome_context, self, api_v1_course_outcome_rollups_url(@context))
|
||||||
@results = find_outcome_results(users: @users, context: @context, outcomes: @outcomes)
|
@results = find_outcome_results(users: @users, context: @context, outcomes: @outcomes)
|
||||||
rollups = rollup_results(@results)
|
rollups = rollup_results(@results, @users)
|
||||||
json = outcome_results_rollup_json(rollups, @outcomes)
|
json = outcome_results_rollup_json(rollups, @outcomes)
|
||||||
|
json[:meta] = Api.jsonapi_meta(@users, self, api_v1_course_outcome_rollups_url(@context))
|
||||||
render :json => json
|
render :json => json
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -35,20 +35,32 @@ module Api::V1::OutcomeResults
|
||||||
|
|
||||||
# Internal: Returns a suitable output hash for the rollups
|
# Internal: Returns a suitable output hash for the rollups
|
||||||
def sanitize_rollups(rollups)
|
def sanitize_rollups(rollups)
|
||||||
rollups.map do |rollup|
|
# this is uglier than it should be to inject section ids. they really should
|
||||||
{
|
# be in a 'links' section or something.
|
||||||
|
# ideally, we would have some seperate mapping from users to course sections
|
||||||
|
# it will also need to change is context is ever not a course
|
||||||
|
# we're mostly assuming that there is one section enrollment per user. if a user
|
||||||
|
# is in multiple sections, they will have multiple rollup results. pagination is
|
||||||
|
# still by user, so the counts won't match up. again, this is a very rare thing
|
||||||
|
results = []
|
||||||
|
rollups.each do |rollup|
|
||||||
|
row = {
|
||||||
id: rollup[:user].id,
|
id: rollup[:user].id,
|
||||||
name: rollup[:user].name,
|
name: rollup[:user].name,
|
||||||
scores: rollup[:scores].map { |score| sanitize_rollup_score(score) },
|
scores: rollup[:scores].map { |score| sanitize_rollup_score(score) },
|
||||||
}
|
}
|
||||||
|
rollup[:user].sections_for_course(@context).each do |section|
|
||||||
|
results << row.merge(links: {section: section.id})
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
results
|
||||||
end
|
end
|
||||||
|
|
||||||
# Internal: Returns a suitable output hash for the rollup score
|
# Internal: Returns a suitable output hash for the rollup score
|
||||||
def sanitize_rollup_score(score)
|
def sanitize_rollup_score(score)
|
||||||
{
|
{
|
||||||
outcome_id: score[:outcome].id,
|
|
||||||
score: score[:score],
|
score: score[:score],
|
||||||
|
links: {outcome: score[:outcome].id},
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -57,21 +57,27 @@ module Outcomes
|
||||||
# results - An Enumeration of properly sorted LearningOutcomeResult objects.
|
# results - An Enumeration of properly sorted LearningOutcomeResult objects.
|
||||||
# The results should be sorted by user id and then by outcome id.
|
# The results should be sorted by user id and then by outcome id.
|
||||||
#
|
#
|
||||||
# Returns a hash of the results:
|
# users - (Optional) Ensure rollups are included for users in this list.
|
||||||
# {
|
# A listed user with no results will have an empty score array.
|
||||||
|
#
|
||||||
|
# Returns a list of users and their score rollups:
|
||||||
|
# [{
|
||||||
# user: the associated user object,
|
# user: the associated user object,
|
||||||
# scores: [{
|
# scores: [{
|
||||||
# outcome: the outcome object
|
# outcome: the outcome object
|
||||||
# score: the rollup score for all the user's results for the outcome.
|
# score: the rollup score for all the user's results for the outcome.
|
||||||
# }, ..., repeated for each outcome, ...]
|
# }, ..., repeated for each outcome, ...]
|
||||||
# }
|
# }, ...]
|
||||||
def rollup_results(results)
|
def rollup_results(results, users=[])
|
||||||
results.chunk(&:user_id).map do |_, user_results|
|
rollups = results.chunk(&:user_id).map do |_, user_results|
|
||||||
{
|
{
|
||||||
user: user_results.first.user,
|
user: user_results.first.user,
|
||||||
scores: rollup_user_results(user_results),
|
scores: rollup_user_results(user_results),
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
missing_users = users - rollups.map {|r| r[:user]}
|
||||||
|
rollups + missing_users.map {|u| {user: u, scores: []}}
|
||||||
end
|
end
|
||||||
|
|
||||||
# Internal: Generates a rollup of the outcome results, Assuming all the
|
# Internal: Generates a rollup of the outcome results, Assuming all the
|
||||||
|
|
|
@ -123,13 +123,15 @@ describe "Outcome Results API", :type => :integration do
|
||||||
api_call(:get, outcome_rollups_url(outcome_course),
|
api_call(:get, outcome_rollups_url(outcome_course),
|
||||||
controller: 'outcome_results', action: 'rollups', format: 'json', course_id: outcome_course.id.to_s)
|
controller: 'outcome_results', action: 'rollups', format: 'json', course_id: outcome_course.id.to_s)
|
||||||
json = JSON.parse(response.body)
|
json = JSON.parse(response.body)
|
||||||
json.keys.sort.should == %w(linked rollups)
|
json.keys.sort.should == %w(linked meta rollups)
|
||||||
json['rollups'].size.should == 1
|
json['rollups'].size.should == 1
|
||||||
json['rollups'].each do |rollup|
|
json['rollups'].each do |rollup|
|
||||||
rollup.keys.sort.should == %w(id name scores)
|
rollup.keys.sort.should == %w(id links name scores)
|
||||||
|
rollup['links'].keys.should == %w(section)
|
||||||
rollup['scores'].size.should == 1
|
rollup['scores'].size.should == 1
|
||||||
rollup['scores'].each do |score|
|
rollup['scores'].each do |score|
|
||||||
score.keys.sort.should == %w(outcome_id score)
|
score.keys.sort.should == %w(links score)
|
||||||
|
score['links'].keys.should == %w(outcome)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
json['linked'].keys.should == %w(outcomes)
|
json['linked'].keys.should == %w(outcomes)
|
||||||
|
|
|
@ -68,9 +68,11 @@ describe Outcomes::ResultAnalytics do
|
||||||
MockOutcomeResult[MockUser[10, 'a'], MockOutcome[80], 40],
|
MockOutcomeResult[MockUser[10, 'a'], MockOutcome[80], 40],
|
||||||
MockOutcomeResult[MockUser[20, 'b'], MockOutcome[80], 50],
|
MockOutcomeResult[MockUser[20, 'b'], MockOutcome[80], 50],
|
||||||
]
|
]
|
||||||
ra.rollup_results(results).should == [
|
users = [MockUser[10, 'a'], MockUser[30, 'c']]
|
||||||
|
ra.rollup_results(results, users).should == [
|
||||||
{ user: MockUser[10, 'a'], scores: [{outcome: MockOutcome[80], score: 40}] },
|
{ user: MockUser[10, 'a'], scores: [{outcome: MockOutcome[80], score: 40}] },
|
||||||
{ user: MockUser[20, 'b'], scores: [{outcome: MockOutcome[80], score: 50}] },
|
{ user: MockUser[20, 'b'], scores: [{outcome: MockOutcome[80], score: 50}] },
|
||||||
|
{ user: MockUser[30, 'c'], scores: [] },
|
||||||
]
|
]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue