add profile pics to mastery gradebook and outcome result api

fixes CNVS-10295

test plan
- regression on mastery gradebook
- ensure student profile pics appear next to the student name on
 the mastery gradebook when profile pics are enabled
- ensure that profile pics do not appear if profile pics are
 disabled

Change-Id: I24ccb21990402a76fe1c79ea39a752d1f73e8d5f
Reviewed-on: https://gerrit.instructure.com/28561
Reviewed-by: Braden Anderson <banderson@instructure.com>
QA-Review: Braden Anderson <banderson@instructure.com>
Tested-by: Jenkins <jenkins@instructure.com>
Product-Review: Joel Hough <joel@instructure.com>
This commit is contained in:
Joel Hough 2014-01-14 14:58:57 -07:00
parent c126c17f5b
commit e209ba649d
8 changed files with 128 additions and 64 deletions

View File

@ -115,7 +115,7 @@ define [
# Returns an object.
_toRow: (rollup, section) ->
return null unless Grid.Util.sectionFilter(section, rollup)
row = { student: rollup.name, section: rollup.links.section }
row = { student: Grid.Util.lookupStudent(rollup.links.user), section: rollup.links.section }
_.each rollup.scores, (score) ->
row["outcome_#{score.links.outcome}"] = score.score
row
@ -149,6 +149,25 @@ define [
lookupOutcome: (name) ->
Grid.outcomes[name]
# Public: Parse and store a list of students from the outcome rollups API.
#
# students - An array of student objects.
#
# Returns nothing.
saveStudents: (students) ->
Grid.students = _.reduce(students, (result, student) ->
result[student.id] = student
result
, {})
# Public: Look up a student in the current student list.
#
# name - The id for the student to look for.
#
# Returns an student or null.
lookupStudent: (id) ->
Grid.students[id]
Math:
mean: (values, round = false) ->
total = _.reduce(values, ((a, b) -> a + b), 0)
@ -197,7 +216,7 @@ define [
cellTemplate(score: value, className: className, masteryScore: outcome.mastery_points)
studentCell: (row, cell, value, columnDef, dataContext) ->
studentCellTemplate(name: value)
studentCellTemplate(value)
# Public: Create a string class name for the given score.
#

View File

@ -81,6 +81,7 @@ define [
# Returns nothing.
renderGrid: (response) =>
Grid.Util.saveOutcomes(response.linked.outcomes)
Grid.Util.saveStudents(response.linked.users)
[columns, rows] = Grid.Util.toGrid(response, column: { formatter: Grid.View.cell }, row: { section: @menu.currentSection })
@grid = new Slick.Grid(
'.outcome-gradebook-wrapper',

View File

@ -30,7 +30,7 @@
#
# "links": {
# // The id of the related outcome
# "outcome": 42
# "outcome": "42"
# }
# }
#
@ -39,15 +39,21 @@
# // an array of OutcomeRollupScore objects
# "scores": ["OutcomeRollupScore"],
#
# // The id of the resource for this rollup. For example, the user id.
# "id": 42,
#
# // The name of the resource for this rollup. For example, the user name.
# "name": "John Doe",
#
# "links": {
# // (Optional) The id of the section this resource is in
# "section": 57
# // If an aggregate result was requested, the course field will be present
# // Otherwise, the user and section field will be present
#
# // (Optional) The id of the course that this rollup applies to
# "course": "42",
#
# // (Optional) The id of the user that this rollup applies to
# "user": "42",
#
# // (Optional) The id of the section the user is in
# "section": "57"
# }
# }
#
@ -88,7 +94,13 @@ class OutcomeResultsController < ApplicationController
# {
# "rollups": [OutcomeRollup],
# "linked": {
# "outcomes": [Outcome]
# "outcomes": [Outcome],
#
# // (Optional) Included if aggregate is not set
# "users": [User],
#
# // (Optional) Included if aggregate is 'course'
# "courses": [Course]
# }
# }
def rollups
@ -106,7 +118,20 @@ class OutcomeResultsController < ApplicationController
@users = Api.paginate(@users, self, api_v1_course_outcome_rollups_url(@context))
@results = find_outcome_results(users: @users, context: @context, outcomes: @outcomes)
rollups = outcome_results_rollups(@results, @users)
json = outcome_results_rollups_json(rollups, @outcomes)
json = outcome_results_rollups_json(rollups)
json[:linked] = {
outcomes: Api.recursively_stringify_json_ids(outcomes_json(@outcomes, @current_user, session)),
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_image_url] = avatar_url_for_user(u, blank_fallback) if service_enabled?(:avatars)
hash
end
}
json[:meta] = Api.jsonapi_meta(@users, self, api_v1_course_outcome_rollups_url(@context))
render json: json
end
@ -119,7 +144,11 @@ class OutcomeResultsController < ApplicationController
# rollup, so don't paginate users in ths method.
@results = find_outcome_results(users: @users, context: @context, outcomes: @outcomes)
aggregate_rollups = [aggregate_outcome_results_rollup(@results, @context)]
json = aggregate_outcome_results_rollups_json(aggregate_rollups, @outcomes)
json = aggregate_outcome_results_rollups_json(aggregate_rollups)
json[:linked] = {
outcomes: Api.recursively_stringify_json_ids(outcomes_json(@outcomes, @current_user, session)),
courses: [{id: @context.id.to_s, name: @context.name}]
}
# no pagination, so no meta field
render json: json
end

View File

@ -241,16 +241,21 @@ $page-dark: #ebeff2;
width: 228px;
}
.avatar-placeholder {
.avatar-circle {
border: 1px solid $border-light;
border-radius: 25px;
box-sizing: border-box;
-moz-box-sizing: border-box;
float: left;
height: 25px;
margin: 5px 8px 0 4px;
margin: 5px 8px 0 -4px;
width: 25px;
}
.outcome-student-cell-content {
padding-left: 8px;
margin-top: 1px;
}
}
.outcome-cell-wrapper {

View File

@ -1,2 +1,6 @@
<div class="avatar-placeholder"></div>
{{name}}
<div class="outcome-student-cell-content">
{{#if avatar_image_url}}
<img class="avatar-circle" src="{{avatar_image_url}}" alt="{{display_name}}">
{{/if}}
{{display_name}}
</div>

View File

@ -19,6 +19,14 @@
module Api::V1::Outcome
include Api::V1::Json
# style can be :full or :abbrev; anything unrecognized defaults to :full.
# abbreviated includes only id, title, url, subgroups_url, outcomes_url, and can_edit. full expands on
# that by adding import_url, parent_outcome_group (if any),
# context id and type, and description.
def outcomes_json(outcomes, user, session, style=:full)
outcomes.map { |o| outcome_json(o, user, session, style) }
end
# style can be :full or :abbrev; anything unrecognized defaults to :full.
# abbreviated includes only id, title, context id and type, url, and
# can_edit. full expands on that by adding description and criterion values

View File

@ -24,41 +24,31 @@ module Api::V1::OutcomeResults
# rollups - The rollups from Outcomes::ResultAnalytics to seralize
#
# Returns a hash that can be converted into json.
def outcome_results_rollups_json(rollups, outcomes)
def outcome_results_rollups_json(rollups)
{
rollups: serialize_user_rollups(rollups),
linked: serialize_linked_outcomes(outcomes),
rollups: serialize_user_rollups(rollups)
}
end
# Public: Serializes the aggregate rollup. Uses the specified context for the
# id and name fields.
def aggregate_outcome_results_rollups_json(rollups, outcomes)
def aggregate_outcome_results_rollups_json(rollups)
{
rollups: serialize_rollups(rollups),
linked: serialize_linked_outcomes(outcomes),
}
end
# Internal: Returns a hash for linked of serialized outcomes
def serialize_linked_outcomes(outcomes)
{
outcomes: outcomes.map { |o| Api.recursively_stringify_json_ids(outcome_json(o, @current_user, session)) },
rollups: serialize_rollups(rollups, :course)
}
end
# Internal: Returns an Array of serialized rollups.
def serialize_rollups(rollups)
rollups.map { |rollup| serialize_rollup(rollup) }
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)
def serialize_rollup(rollup, context_key)
# both Course and User have a name method, so this works for both.
{
id: rollup.context.id.to_s,
name: rollup.context.name,
scores: serialize_rollup_scores(rollup.scores),
links: {context_key => rollup.context.id.to_s}
}
end
@ -66,7 +56,7 @@ module Api::V1::OutcomeResults
# section information.
def serialize_user_rollups(rollups)
serialized_rollup_pairs = rollups.map do |rollup|
[rollup, serialize_rollup(rollup)]
[rollup, serialize_rollup(rollup, :user)]
end
serialized_rollups_with_section_duplicates = serialized_rollup_pairs.map do |rollup, serialized_rollup|
@ -89,7 +79,7 @@ module Api::V1::OutcomeResults
# 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 ? [@section] : rollup.context.sections_for_course(@context)).map do |section|
serialized_rollup.merge(links: {section: section.id.to_s})
serialized_rollup.deep_merge(links: {section: section.id.to_s})
end
end

View File

@ -219,25 +219,28 @@ describe "Outcome Results API", :type => :integration do
json.keys.sort.should == %w(linked meta rollups)
json['rollups'].size.should == 1
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id links name scores)
rollup['links'].keys.should == %w(section)
rollup.keys.sort.should == %w(links scores)
rollup['links'].keys.sort.should == %w(section user)
rollup['links']['section'].should == @course.course_sections.first.id.to_s
rollup['links']['user'].should == outcome_student.id.to_s
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
score['score'].should == first_outcome_rating[:points]
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
score['links']['outcome'].should == outcome_object.id.to_s
end
end
json['linked'].keys.should == %w(outcomes)
json['linked'].keys.sort.should == %w(outcomes users)
json['linked']['outcomes'].size.should == 1
json['linked']['users'].size.should == 1
end
describe "user_ids parameter" do
it "restricts results to specified users" do
outcome_students
student_id_str = outcome_students[0..1].map(&:id).join(',')
student_ids = outcome_students[0..1].map(&:id).map(&:to_s)
student_id_str = student_ids.join(',')
course_with_teacher_logged_in(course: outcome_course, active_all: true)
api_call(:get, outcome_rollups_url(outcome_course, user_ids: student_id_str),
controller: 'outcome_results', action: 'rollups', format: 'json', course_id: outcome_course.id.to_s, user_ids: student_id_str)
@ -245,19 +248,21 @@ describe "Outcome Results API", :type => :integration do
json.keys.sort.should == %w(linked meta rollups)
json['rollups'].size.should == 2
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id links name scores)
rollup['links'].keys.should == %w(section)
rollup.keys.sort.should == %w(links scores)
rollup['links'].keys.sort.should == %w(section user)
rollup['links']['section'].should == @course.course_sections.first.id.to_s
student_ids.should be_include(rollup['links']['user'])
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
[0,1].should be_include(score['score'])
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
score['links']['outcome'].should == outcome_object.id.to_s
end
end
json['linked'].keys.should == %w(outcomes)
json['linked'].keys.sort.should == %w(outcomes users)
json['linked']['outcomes'].size.should == 1
json['linked']['users'].size.should == 2
end
end
@ -271,19 +276,21 @@ describe "Outcome Results API", :type => :integration do
json.keys.sort.should == %w(linked meta rollups)
json['rollups'].size.should == 2
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id links name scores)
rollup['links'].keys.should == %w(section)
rollup.keys.sort.should == %w(links scores)
rollup['links'].keys.sort.should == %w(section user)
rollup['links']['section'].should == outcome_course_sections[0].id.to_s
outcome_course_sections[0].student_ids.map(&:to_s).should be_include(rollup['links']['user'])
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
[0,2].should be_include(score['score'])
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
score['links']['outcome'].should == outcome_object.id.to_s
end
end
json['linked'].keys.should == %w(outcomes)
json['linked'].keys.sort.should == %w(outcomes users)
json['linked']['outcomes'].size.should == 1
json['linked']['users'].size.should == outcome_course_sections[0].students.count
end
end
end
@ -347,18 +354,18 @@ describe "Outcome Results API", :type => :integration do
json.keys.sort.should == %w(linked rollups)
json['rollups'].size.should == 1
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id name scores)
rollup['id'].should == @course.id.to_s
rollup['name'].should == @course.name
rollup.keys.sort.should == %w(links scores)
rollup['links']['course'] == @course.id.to_s
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
score['score'].should == first_outcome_rating[:points]
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
end
end
json['linked'].keys.should == %w(outcomes)
json['linked'].keys.sort.should == %w(courses outcomes)
json['linked']['outcomes'].size.should == 1
json['linked']['courses'].size.should == 1
end
describe "user_ids parameter" do
@ -372,16 +379,16 @@ describe "Outcome Results API", :type => :integration do
user_ids: student_id_str)
json = JSON.parse(response.body)
json.keys.sort.should == %w(linked rollups)
json['linked'].keys.sort.should == %w(courses outcomes)
json['rollups'].size.should == 1
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id name scores)
rollup['id'].should == @course.id.to_s
rollup['name'].should == @course.name
rollup.keys.sort.should == %w(links scores)
rollup['links']['course'].should == @course.id.to_s
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
score['score'].should == 0.5
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
end
end
end
@ -399,14 +406,13 @@ describe "Outcome Results API", :type => :integration do
json.keys.sort.should == %w(linked rollups)
json['rollups'].size.should == 1
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id name scores)
rollup['id'].should == outcome_course.id.to_s
rollup['name'].should == outcome_course.name
rollup.keys.sort.should == %w(links scores)
rollup['links']['course'].should == outcome_course.id.to_s
rollup['scores'].size.should == 1
rollup['scores'].each do |score|
score.keys.sort.should == %w(links score)
score['score'].should == 1
score['links'].keys.should == %w(outcome)
score['links'].keys.sort.should == %w(outcome)
end
end
end
@ -430,13 +436,15 @@ describe "Outcome Results API", :type => :integration do
json = JSON.parse(response.body)
json.keys.sort.should == %w(linked meta rollups)
json['rollups'].size.should == 2
json['rollups'].collect{|x| x['id']}.sort.should == [student.id.to_s, student2.id.to_s].sort
json['rollups'].collect{|x| x['links']['user']}.sort.should == [student.id.to_s, student2.id.to_s].sort
json['rollups'].each do |rollup|
rollup.keys.sort.should == %w(id links name scores)
rollup.keys.sort.should == %w(links scores)
rollup['scores'].size.should == 1
rollup['links'].keys.sort.should == %w(section user)
end
json['linked'].keys.should == %w(outcomes)
json['linked'].keys.sort.should == %w(outcomes users)
json['linked']['outcomes'].size.should == 1
json['linked']['users'].size.should == 2
end
end