Add reporting name for learning outcomes
fixes CNVS-13242 This adds a special field to learning outcomes to use for friendly reporting in case the outcome's actual name is quite complex or cryptic (like common core standard outcomes). TEST PLAN: - login as an instructor - create or edit an outcome and validate that you can add a "friendly" name and that it persists - use that outcome for an assignment and let a student complete it - navigate to the student mastery report for that student - verify that by hovering over that outcome name on the student mastery report you can see both the real title and the friendly name Change-Id: I89d1a5de590666ddf6cbc82617e4475d1f7a5226 Reviewed-on: https://gerrit.instructure.com/35919 Reviewed-by: Drew Bowman <dbowman@instructure.com> QA-Review: Steven Shepherd <sshepherd@instructure.com> Tested-by: Jenkins <jenkins@instructure.com> Product-Review: Drew Bowman <dbowman@instructure.com>
This commit is contained in:
parent
ee1db6929c
commit
a0c96d6a41
|
@ -5,7 +5,7 @@ define [
|
|||
], (_, {Model, Collection}, natcompare) ->
|
||||
class Group extends Model
|
||||
initialize: ->
|
||||
@set('outcomes', new Collection([], comparator: natcompare.byGet('title')))
|
||||
@set('outcomes', new Collection([], comparator: natcompare.byGet('friendly_name')))
|
||||
|
||||
count: -> @get('outcomes').length
|
||||
|
||||
|
|
|
@ -3,6 +3,11 @@ define [
|
|||
'Backbone'
|
||||
], (_, {Model, Collection}) ->
|
||||
class Outcome extends Model
|
||||
initialize: ->
|
||||
super
|
||||
@set 'friendly_name', @get('display_name') || @get('title')
|
||||
@set 'hover_name', (@get('title') if @get('display_name'))
|
||||
|
||||
status: ->
|
||||
if @scoreDefined()
|
||||
score = @get('score')
|
||||
|
|
|
@ -384,6 +384,10 @@ class OutcomeGroupsApiController < ApplicationController
|
|||
# @argument title [Optional, String]
|
||||
# The title of the new outcome. Required if outcome_id is absent.
|
||||
#
|
||||
# @argument display_name [Optional, String]
|
||||
# A friendly name shown in reports for outcomes with cryptic titles,
|
||||
# such as common core standards names.
|
||||
#
|
||||
# @argument description [Optional, String]
|
||||
# The description of the new outcome.
|
||||
#
|
||||
|
@ -412,6 +416,7 @@ class OutcomeGroupsApiController < ApplicationController
|
|||
# curl 'https://<canvas>/api/v1/accounts/1/outcome_groups/1/outcomes.json' \
|
||||
# -X POST \
|
||||
# -F 'title=Outcome Title' \
|
||||
# -F 'display_name=Title for reporting' \
|
||||
# -F 'description=Outcome description' \
|
||||
# -F 'vendor_guid=customid9000' \
|
||||
# -F 'mastery_points=3' \
|
||||
|
@ -429,6 +434,7 @@ class OutcomeGroupsApiController < ApplicationController
|
|||
# -X POST \
|
||||
# --data-binary '{
|
||||
# "title": "Outcome Title",
|
||||
# "display_name": "Title for reporting",
|
||||
# "description": "Outcome description",
|
||||
# "vendor_guid": "customid9000",
|
||||
# "mastery_points": 3,
|
||||
|
@ -451,7 +457,7 @@ class OutcomeGroupsApiController < ApplicationController
|
|||
return
|
||||
end
|
||||
else
|
||||
@outcome = context_create_outcome(params.slice(:title, :description, :ratings, :mastery_points, :vendor_guid))
|
||||
@outcome = context_create_outcome(params.slice(:title, :description, :ratings, :mastery_points, :vendor_guid, :display_name))
|
||||
unless @outcome.valid?
|
||||
render :json => @outcome.errors, :status => :bad_request
|
||||
return
|
||||
|
@ -666,7 +672,7 @@ class OutcomeGroupsApiController < ApplicationController
|
|||
|
||||
def context_create_outcome(data)
|
||||
scope = @context ? @context.created_learning_outcomes : LearningOutcome.global
|
||||
outcome = scope.build(data.slice(:title, :description, :vendor_guid))
|
||||
outcome = scope.build(data.slice(:title, :display_name, :description, :vendor_guid))
|
||||
if data[:ratings]
|
||||
outcome.rubric_criterion = data.slice(:ratings, :mastery_points)
|
||||
end
|
||||
|
|
|
@ -49,6 +49,11 @@
|
|||
# "example": "Outcome title",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "display_name": {
|
||||
# "description": "Optional friendly name for reporting",
|
||||
# "example": "My Favorite Outocome",
|
||||
# "type": "string"
|
||||
# },
|
||||
# "description": {
|
||||
# "description": "description of the outcome. omitted in the abbreviated form.",
|
||||
# "example": "Outcome description",
|
||||
|
@ -118,6 +123,10 @@ class OutcomesApiController < ApplicationController
|
|||
# @argument title [Optional, String]
|
||||
# The new outcome title.
|
||||
#
|
||||
# @argument display_name [Optional, String]
|
||||
# A friendly name shown in reports for outcomes with cryptic titles,
|
||||
# such as common core standards names.
|
||||
#
|
||||
# @argument description [Optional, String]
|
||||
# The new outcome description.
|
||||
#
|
||||
|
@ -141,6 +150,7 @@ class OutcomesApiController < ApplicationController
|
|||
# curl 'https://<canvas>/api/v1/outcomes/1.json' \
|
||||
# -X PUT \
|
||||
# -F 'title=Outcome Title' \
|
||||
# -F 'display_name=Title for reporting' \
|
||||
# -F 'description=Outcome description' \
|
||||
# -F 'vendor_guid=customid9001' \
|
||||
# -F 'mastery_points=3' \
|
||||
|
@ -158,6 +168,7 @@ class OutcomesApiController < ApplicationController
|
|||
# -X PUT \
|
||||
# --data-binary '{
|
||||
# "title": "Outcome Title",
|
||||
# "display_name": "Title for reporting",
|
||||
# "description": "Outcome description",
|
||||
# "vendor_guid": "customid9001",
|
||||
# "mastery_points": 3,
|
||||
|
@ -172,7 +183,7 @@ class OutcomesApiController < ApplicationController
|
|||
#
|
||||
def update
|
||||
if authorized_action(@outcome, @current_user, :update)
|
||||
@outcome.update_attributes(params.slice(:title, :description, :vendor_guid))
|
||||
@outcome.update_attributes(params.slice(:title, :display_name, :description, :vendor_guid))
|
||||
if params[:mastery_points] || params[:ratings]
|
||||
criterion = @outcome.data && @outcome.data[:rubric_criterion]
|
||||
criterion ||= {}
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
class LearningOutcome < ActiveRecord::Base
|
||||
include Workflow
|
||||
attr_accessible :context, :description, :short_description, :title, :rubric_criterion, :vendor_guid
|
||||
attr_accessible :context, :description, :short_description, :title, :display_name
|
||||
attr_accessible :rubric_criterion, :vendor_guid
|
||||
|
||||
belongs_to :context, :polymorphic => true
|
||||
validates_inclusion_of :context_type, :allow_nil => true, :in => ['Account', 'Course']
|
||||
has_many :learning_outcome_results
|
||||
|
|
|
@ -361,6 +361,9 @@ $outcome-border: 1px solid #BCC2CA
|
|||
color: #2a333b
|
||||
p
|
||||
margin: 0
|
||||
white-space: nowrap
|
||||
overflow: hidden
|
||||
text-overflow: ellipsis
|
||||
|
||||
.title
|
||||
font-weight: bold
|
||||
|
@ -445,3 +448,6 @@ $outcome-border: 1px solid #BCC2CA
|
|||
top: 13px
|
||||
.score
|
||||
font-size: 14px
|
||||
|
||||
.ui-widget.ui-tooltip
|
||||
max-width: 500px
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
height: 500px
|
||||
border-left: 1px solid $borderColor
|
||||
overflow: scroll
|
||||
.learning_outcome
|
||||
label.span3
|
||||
margin-left: 0px
|
||||
.wrapper
|
||||
padding: 15px
|
||||
width: 600px
|
||||
|
|
|
@ -14,7 +14,9 @@
|
|||
{{/if}}
|
||||
</div>
|
||||
<div class="outcome-properties">
|
||||
<div class="title" data-tooltip title="{{description}}">{{title}}</div>
|
||||
<div class="title" data-tooltip title="{{#if hover_name}}{{hover_name}}: {{/if}}{{description}}">
|
||||
{{friendly_name}}
|
||||
</div>
|
||||
<div class="description">{{{description}}}</div>
|
||||
</div>
|
||||
<div class="outcome-score outcome-bar-wrapper">
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
<div class="outcome-modal">
|
||||
<div class="title">{{title}}</div>
|
||||
<div class="title"{{#if hover_name}} data-tooltip title="{{hover_name}}"{{/if}}>
|
||||
{{friendly_name}}
|
||||
</div>
|
||||
<div class="outcome-bar-wrapper">
|
||||
<div class="score"><strong>{{#if scoreDefined}}{{score}}{{else}}-{{/if}}</strong>/{{mastery_points}}</div>
|
||||
{{view progress}}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
<form action="{{url}}" class="learning_outcome" method="post">
|
||||
<label for="title">{{#t "title"}}Name this outcome{{/t}}:</label>
|
||||
<input class="outcome_title" name="title" id=title size="50" type="text" value="{{title}}">
|
||||
|
||||
<label class="span3" for="title">{{#t "title"}}Name this outcome{{/t}}:</label>
|
||||
<input class="outcome_title span3" name="title" id=title size="50" type="text" value="{{title}}">
|
||||
|
||||
<label class="span3" for="display_name">{{#t "display_name"}}Friendly name (optional){{/t}}:</label>
|
||||
<input class="outcome_display_name span3" name="display_name" id=display_name size="50" type="text" value="{{display_name}}">
|
||||
|
||||
|
||||
<label for="description">{{#t "description"}}Describe this outcome{{/t}}:</label>
|
||||
<textarea cols="40" name="description" id=description rows="20" style="width: 100%; height: 150px;">{{description}}</textarea>
|
||||
<div id="outcome_criterion_dialog" style="display: none;">
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
class AddDisplayNameToLearningOutcomes < ActiveRecord::Migration
|
||||
tag :predeploy
|
||||
|
||||
def self.up
|
||||
add_column :learning_outcomes, :display_name, :string
|
||||
end
|
||||
|
||||
def self.down
|
||||
remove_column :learning_outcomes, :display_name
|
||||
end
|
||||
end
|
|
@ -32,7 +32,8 @@ module Api::V1::Outcome
|
|||
# can_edit. full expands on that by adding description and criterion values
|
||||
# (if any).
|
||||
def outcome_json(outcome, user, session, style=:full)
|
||||
api_json(outcome, user, session, :only => %w(id context_type context_id vendor_guid), :methods => [:title]).tap do |hash|
|
||||
json_attributes = %w(id context_type context_id vendor_guid display_name)
|
||||
api_json(outcome, user, session, :only => json_attributes, :methods => [:title]).tap do |hash|
|
||||
hash['url'] = api_v1_outcome_path :id => outcome.id
|
||||
hash['can_edit'] = outcome.context_id ?
|
||||
outcome.context.grants_right?(user, session, :manage_outcomes) :
|
||||
|
|
|
@ -623,6 +623,7 @@ describe "Outcome Groups API", type: :request do
|
|||
"context_type" => "Account",
|
||||
"context_id" => @account.id,
|
||||
"title" => outcome.title,
|
||||
"display_name" => nil,
|
||||
"url" => api_v1_outcome_path(:id => outcome.id),
|
||||
"can_edit" => true
|
||||
}
|
||||
|
@ -772,6 +773,7 @@ describe "Outcome Groups API", type: :request do
|
|||
"context_type" => nil,
|
||||
"context_id" => nil,
|
||||
"title" => @outcome.title,
|
||||
"display_name" => nil,
|
||||
"url" => api_v1_outcome_path(:id => @outcome.id),
|
||||
"can_edit" => false
|
||||
}
|
||||
|
@ -815,6 +817,7 @@ describe "Outcome Groups API", type: :request do
|
|||
:id => @group.id.to_s,
|
||||
:format => 'json' },
|
||||
{ :title => "My Outcome",
|
||||
:display_name => "Friendly Name",
|
||||
:description => "Description of my outcome",
|
||||
:mastery_points => 5,
|
||||
:ratings => [
|
||||
|
@ -826,6 +829,7 @@ describe "Outcome Groups API", type: :request do
|
|||
LearningOutcome.active.count.should == 1
|
||||
@outcome = LearningOutcome.active.first
|
||||
@outcome.title.should == "My Outcome"
|
||||
@outcome.display_name.should == "Friendly Name"
|
||||
@outcome.description.should == "Description of my outcome"
|
||||
@outcome.data[:rubric_criterion].should == {
|
||||
:description => 'My Outcome',
|
||||
|
@ -955,6 +959,7 @@ describe "Outcome Groups API", type: :request do
|
|||
"vendor_guid" => @outcome.vendor_guid,
|
||||
"context_type" => nil,
|
||||
"context_id" => nil,
|
||||
"display_name" => nil,
|
||||
"title" => @outcome.title,
|
||||
"url" => api_v1_outcome_path(:id => @outcome.id),
|
||||
"can_edit" => false
|
||||
|
|
|
@ -104,6 +104,7 @@ describe "Outcomes API", type: :request do
|
|||
"context_id" => @account.id,
|
||||
"context_type" => "Account",
|
||||
"title" => @outcome.title,
|
||||
"display_name" => nil,
|
||||
"url" => api_v1_outcome_path(:id => @outcome.id),
|
||||
"vendor_guid" => "vendorguid9000",
|
||||
"can_edit" => true,
|
||||
|
@ -134,6 +135,7 @@ describe "Outcomes API", type: :request do
|
|||
"context_id" => @account.id,
|
||||
"context_type" => "Account",
|
||||
"title" => @outcome.title,
|
||||
"display_name" => nil,
|
||||
"url" => api_v1_outcome_path(:id => @outcome.id),
|
||||
"vendor_guid" => "vendorguid9000",
|
||||
"can_edit" => true,
|
||||
|
@ -250,6 +252,7 @@ describe "Outcomes API", type: :request do
|
|||
"context_type" => "Account",
|
||||
"vendor_guid" => "vendorguid9000",
|
||||
"title" => "New Title",
|
||||
"display_name" => nil,
|
||||
"url" => api_v1_outcome_path(:id => @outcome.id),
|
||||
"can_edit" => true,
|
||||
"description" => "New Description"
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
#
|
||||
# Copyright (C) 2014 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 File.expand_path(File.dirname(__FILE__) + '/../../../spec_helper.rb')
|
||||
|
||||
class Subject
|
||||
include Api::V1::Outcome
|
||||
|
||||
def api_v1_outcome_path(opts)
|
||||
"/api/v1/outcome/#{opts.fetch(:id)}"
|
||||
end
|
||||
end
|
||||
|
||||
describe "Api::V1::Outcome" do
|
||||
describe "#outcome_json" do
|
||||
it "includes the display name from the outcome" do
|
||||
outcome = LearningOutcome.new(display_name: "MyFavoriteOutcome")
|
||||
subj = Subject.new
|
||||
result = subj.outcome_json(outcome, nil, nil)
|
||||
result['display_name'].should == "MyFavoriteOutcome"
|
||||
end
|
||||
end
|
||||
end
|
|
@ -153,10 +153,8 @@ def should_create_a_learning_outcome_nested
|
|||
replace_content(f('.outcomes-content input[name=title]'), outcome_name)
|
||||
|
||||
# submit
|
||||
driver.execute_script("$('.submit_button').click()")
|
||||
if !f('.submit_button').nil?
|
||||
driver.execute_script("$('.submit_button').click()")
|
||||
end
|
||||
f('.submit_button').click
|
||||
wait_for_ajaximations
|
||||
refresh_page
|
||||
|
||||
#select group
|
||||
|
|
Loading…
Reference in New Issue