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:
Joel Hough 2013-12-31 18:01:13 -07:00
parent 9af963c357
commit 0d1c04cbf1
5 changed files with 49 additions and 21 deletions

View File

@ -23,14 +23,15 @@
#
# @object OutcomeRollupScore
# {
#
# // The id of the related outcome
# "outcome_id": 42,
#
# // The rollup score for the outcome, based on the student assessment
# // scores related to the outcome. This could be null if the student has
# // no related scores.
# "score": 3
# "score": 3,
#
# "links": {
# // The id of the related outcome
# "outcome": 42
# }
# }
#
# @object OutcomeRollup
@ -42,7 +43,12 @@
# "id": 42,
#
# // 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
@outcomes = @context.linked_learning_outcomes
@users = users_for_outcome_context
# 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)
rollups = rollup_results(@results)
rollups = rollup_results(@results, @users)
json = outcome_results_rollup_json(rollups, @outcomes)
json[:meta] = Api.jsonapi_meta(@users, self, api_v1_course_outcome_rollups_url(@context))
render :json => json
end

View File

@ -35,20 +35,32 @@ module Api::V1::OutcomeResults
# Internal: Returns a suitable output hash for the 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,
name: rollup[:user].name,
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
results
end
# Internal: Returns a suitable output hash for the rollup score
def sanitize_rollup_score(score)
{
outcome_id: score[:outcome].id,
score: score[:score],
links: {outcome: score[:outcome].id},
}
end

View File

@ -57,21 +57,27 @@ module Outcomes
# results - An Enumeration of properly sorted LearningOutcomeResult objects.
# 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,
# scores: [{
# outcome: the outcome object
# score: the rollup score for all the user's results for the outcome.
# }, ..., repeated for each outcome, ...]
# }
def rollup_results(results)
results.chunk(&:user_id).map do |_, user_results|
# }, ...]
def rollup_results(results, users=[])
rollups = results.chunk(&:user_id).map do |_, user_results|
{
user: user_results.first.user,
scores: rollup_user_results(user_results),
}
end
missing_users = users - rollups.map {|r| r[:user]}
rollups + missing_users.map {|u| {user: u, scores: []}}
end
# Internal: Generates a rollup of the outcome results, Assuming all the

View File

@ -123,13 +123,15 @@ describe "Outcome Results API", :type => :integration do
api_call(:get, outcome_rollups_url(outcome_course),
controller: 'outcome_results', action: 'rollups', format: 'json', course_id: outcome_course.id.to_s)
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'].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'].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
json['linked'].keys.should == %w(outcomes)

View File

@ -68,9 +68,11 @@ describe Outcomes::ResultAnalytics do
MockOutcomeResult[MockUser[10, 'a'], MockOutcome[80], 40],
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[20, 'b'], scores: [{outcome: MockOutcome[80], score: 50}] },
{ user: MockUser[30, 'c'], scores: [] },
]
end
end