219 lines
7.8 KiB
Ruby
219 lines
7.8 KiB
Ruby
#
|
|
# Copyright (C) 2013 - present Instructure, Inc.
|
|
#
|
|
# This file is part of Canvas.
|
|
#
|
|
# Canvas is free software: you can redistribute it and/or modify it under
|
|
# the terms of the GNU Affero General Public License as published by the Free
|
|
# Software Foundation, version 3 of the License.
|
|
#
|
|
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
|
|
# details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License along
|
|
# with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
require 'csv'
|
|
|
|
module Api::V1::OutcomeResults
|
|
include Api::V1::Outcome
|
|
|
|
# Public: Serializes OutcomeResults
|
|
#
|
|
# results - The OutcomeResults to serialize
|
|
#
|
|
# Returns a hash that can be converted into json
|
|
def outcome_results_json(results)
|
|
{
|
|
outcome_results: results.map{|r| outcome_result_json(r)}
|
|
}
|
|
end
|
|
|
|
def outcome_result_json(result)
|
|
hash = api_json(result, @current_user, session, {
|
|
methods: :submitted_or_assessed_at,
|
|
only: %w(id score mastery possible percent)
|
|
})
|
|
hash[:links] = {
|
|
user: result.user.id.to_s,
|
|
learning_outcome: result.learning_outcome_id.to_s,
|
|
alignment: result.alignment.content.asset_string
|
|
}
|
|
hash
|
|
end
|
|
|
|
# Public: Serializes the rollups produced by Outcomes::ResultAnalytics.
|
|
#
|
|
# rollups - The rollups from Outcomes::ResultAnalytics to serialize
|
|
#
|
|
# Returns a hash that can be converted into json.
|
|
def outcome_results_rollups_json(rollups)
|
|
{
|
|
rollups: serialize_user_rollups(rollups)
|
|
}
|
|
end
|
|
|
|
# Public: Serializes outcomes in a hash that can be added to the linked hash.
|
|
#
|
|
# Returns a Hash containing serialized outcomes.
|
|
def outcome_results_include_outcomes_json(outcomes)
|
|
outcomes.each_slice(50).each do |outcomes_slice|
|
|
ActiveRecord::Associations::Preloader.new.preload(outcomes_slice, [:context, :alignments])
|
|
end
|
|
assessed_outcomes = []
|
|
outcomes.map(&:id).each_slice(100) do |outcome_ids|
|
|
assessed_outcomes += LearningOutcomeResult.distinct.where(learning_outcome_id: outcome_ids).pluck(:learning_outcome_id)
|
|
end
|
|
outcomes.map do |o|
|
|
hash = outcome_json(o, @current_user, session, assessed_outcomes: assessed_outcomes)
|
|
hash.merge!(alignments: o.alignments.map(&:content_asset_string))
|
|
hash
|
|
end
|
|
end
|
|
|
|
# Public: Serializes outcome groups in a hash that can be added to the linked hash.
|
|
#
|
|
# Returns a Hash containing serialized outcome groups.
|
|
def outcome_results_include_outcome_groups_json(outcome_groups)
|
|
outcome_groups.map { |g| outcome_group_json(g, @current_user, session) }
|
|
end
|
|
|
|
# Public: Serializes outcome links in a hash that can be added to the linked hash.
|
|
#
|
|
# Returns a Hash containing serialized outcome links.
|
|
def outcome_results_include_outcome_links_json(outcome_links)
|
|
ols_json = outcome_links_json(outcome_links, @current_user, session)
|
|
end
|
|
|
|
# Public: Returns an Array of serialized Course objects for linked hash.
|
|
def outcome_results_linked_courses_json(courses)
|
|
courses.map { |course| {id: course.id.to_s, name: course.name} }
|
|
end
|
|
|
|
# Public: Returns an Array of serialized User objects for the linked hash.
|
|
def outcome_results_linked_users_json(users)
|
|
users.map do |u|
|
|
hash = {
|
|
id: u.id.to_s,
|
|
name: u.name,
|
|
display_name: u.short_name,
|
|
sortable_name: u.sortable_name
|
|
}
|
|
hash[:avatar_url] = avatar_url_for_user(u, blank_fallback) if service_enabled?(:avatars)
|
|
hash
|
|
end
|
|
end
|
|
|
|
# Public: Returns an Array of serialized Alignment objects for the linked hash.
|
|
def outcome_results_include_alignments_json(alignments)
|
|
alignments.map do |alignment|
|
|
hash = {id: alignment.asset_string, name: alignment.title}
|
|
html_url = polymorphic_url([alignment.context, alignment]) rescue nil
|
|
hash[:html_url] = html_url if html_url
|
|
hash
|
|
end
|
|
end
|
|
|
|
# Public: Serializes the aggregate rollup. Uses the specified context for the
|
|
# id and name fields.
|
|
def aggregate_outcome_results_rollups_json(rollups)
|
|
{
|
|
rollups: serialize_rollups(rollups, :course)
|
|
}
|
|
end
|
|
|
|
# Internal: Returns an Array of serialized rollups.
|
|
def serialize_rollups(rollups, context_key)
|
|
rollups.map { |rollup| serialize_rollup(rollup, context_key) }
|
|
end
|
|
|
|
# Internal: Returns a suitable output hash for the rollup.
|
|
def serialize_rollup(rollup, context_key)
|
|
# both Course and User have a name method, so this works for both.
|
|
{
|
|
scores: serialize_rollup_scores(rollup.scores),
|
|
links: {context_key => rollup.context.id.to_s}
|
|
}
|
|
end
|
|
|
|
# Internal: Returns a suitable output hash for the user rollups, including
|
|
# section information.
|
|
def serialize_user_rollups(rollups)
|
|
serialized_rollup_pairs = rollups.map do |rollup|
|
|
[rollup, serialize_rollup(rollup, :user)]
|
|
end
|
|
|
|
duplicate_rollup_rows_for_sections(serialized_rollup_pairs)
|
|
end
|
|
|
|
# Internal: generates an array of duplicate serialized_rollups with distinct
|
|
# section links for each section of the user's course. If @section is set (as
|
|
# it is if section_id is sent as a parameter to the rollup api endpoint), only
|
|
# that section is included
|
|
def duplicate_rollup_rows_for_sections(serialized_rollup_pairs)
|
|
# 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 if 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
|
|
section_ids_func = if @section
|
|
->(user) { [@section.id] }
|
|
else
|
|
enrollments = @context.student_enrollments.active.where(:user_id => serialized_rollup_pairs.map{|pair| pair[0].context.id}).to_a
|
|
->(user) { enrollments.select{|e| e.user_id == user.id}.map(&:course_section_id) }
|
|
end
|
|
|
|
serialized_rollup_pairs.flat_map do |rollup, serialized_rollup|
|
|
section_ids_func.call(rollup.context).map do |section_id|
|
|
serialized_rollup.deep_merge(links: {section: section_id.to_s})
|
|
end
|
|
end
|
|
end
|
|
|
|
# Internal: Returns an Array of serialized rollup scores
|
|
def serialize_rollup_scores(scores)
|
|
scores.map { |score| serialize_rollup_score(score) }
|
|
end
|
|
|
|
# Internal: Returns a suitable output hash for the rollup score
|
|
def serialize_rollup_score(score)
|
|
{
|
|
score: score.score,
|
|
title: score.title,
|
|
submitted_at: score.submitted_at,
|
|
count: score.count,
|
|
links: {outcome: score.outcome.id.to_s},
|
|
}
|
|
end
|
|
|
|
def outcome_results_rollups_csv(rollups, outcomes, outcome_paths)
|
|
CSV.generate do |csv|
|
|
row = []
|
|
row << I18n.t(:student_name, 'Student name')
|
|
row << I18n.t(:student_id, 'Student ID')
|
|
outcomes.each do |outcome|
|
|
pathParts = outcome_paths.find{|x| x[:id] == outcome.id}[:parts]
|
|
path = pathParts.map{|x| x[:name]}.join(' > ')
|
|
row << I18n.t(:outcome_path_result, "%{path} result", :path => path)
|
|
row << I18n.t(:outcome_path_mastery_points, "%{path} mastery points", :path => path)
|
|
end
|
|
csv << row
|
|
rollups.each do |rollup|
|
|
row = [rollup.context.name, rollup.context.id]
|
|
outcomes.each do |outcome|
|
|
score = rollup.scores.find{|x| x.outcome == outcome}
|
|
criterion = outcome.data && outcome.data[:rubric_criterion]
|
|
row << (score ? score.score : nil)
|
|
row << (criterion ? criterion[:mastery_points] : nil)
|
|
end
|
|
csv << row
|
|
end
|
|
end
|
|
end
|
|
end
|