Remove Academic Benchmark v1 code

closes OUT-1006

flag = none

test plan:
  - follow steps on the Confluence page "Academic Benchmark Importing"
    and confirm that importing using Academic Benchmarks still works

Change-Id: I3c0db27221ea3424a78b5f4e503db919a18d690b
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/222016
Product-Review: Augusto Callejas <acallejas@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Michael Brewer-Davis <mbd@instructure.com>
QA-Review: Michael Brewer-Davis <mbd@instructure.com>
This commit is contained in:
Augusto Callejas 2020-01-03 19:19:56 -10:00
parent 7ee06de561
commit 69151329da
18 changed files with 205 additions and 1968 deletions

View File

@ -7,7 +7,7 @@
</tr>
<tr>
<td colspan="2">
<%= t "These credentials are for Academic Benchmark API v3. Leaving the Partner Key empty will default the importer to use Academic Benchmark API v1." %>
<%= t "These credentials are for Academic Benchmark API v3." %>
</td>
</tr>
<tr>
@ -22,27 +22,10 @@
<%= f.text_field :partner_key %>
</td>
</tr>
<tr>
<td colspan="2">
<%= t "These credentials are for Academic Benchmark API v1, which will be deprecated in a future release." %>
</td>
</tr>
<tr>
<td><%= f.blabel :api_url, :en => "Api Url" %></td>
<td>
<%= f.text_field :api_url,:size => "90" %> <%= t :api_url_description, "(e.g. %{url})", :url => AcademicBenchmark::Api::API_BASE_URL %>
</td>
</tr>
<tr>
<td><%= f.blabel :api_key, :en => "API Key" %></td>
<td>
<%= f.text_field :api_key %>
</td>
</tr>
<tr>
<td><%= f.blabel :common_core_guid, :en => "Common Core GUID (optional)" %></td>
<td>
<%= f.text_field :common_core_guid %> <%= t :common_core_guid, "(e.g. %{guid})", :guid => AcademicBenchmark::ConverterV1::COMMON_CORE_GUID %>
<%= f.text_field :common_core_guid %> <%= t :common_core_guid, "(e.g. %{guid})", :guid => AcademicBenchmark::Converter::COMMON_CORE_GUID %>
</td>
</tr>
</table>

View File

@ -41,44 +41,18 @@ module AcademicBenchmark
end
def self.check_config
if self.v3?
self.check_v3_config
else
self.check_v1_config
end
end
def self.check_v3_config
if !self.config
"(needs partner_key and partner_id)"
elsif !self.config[:partner_key].present?
elsif self.config[:partner_key].blank?
"(needs partner_key)"
elsif !self.config[:partner_id].present?
elsif self.config[:partner_id].blank?
"(needs partner_id)"
end
end
def self.check_v1_config
if !self.config
"(needs api_key and api_url)"
elsif !self.config["api_key"] || self.config["api_key"].empty?
"(needs api_key)"
elsif !self.config["api_url"] || self.config["api_url"].empty?
"(needs api_url)"
end
end
def self.v3?
self.config.present? && self.config[:partner_key].present?
end
def self.extract_nat_stds(api, nat_stds_guid)
return [] if nat_stds_guid.nil?
if AcademicBenchmark.v3?
api.standards.authority_documents(nat_stds_guid)
else
api.browse_guid(nat_stds_guid).first["itm"].first["itm"]
end
api.standards.authority_documents(nat_stds_guid)
end
##
@ -86,12 +60,8 @@ module AcademicBenchmark
# National Standards are also known as Common Core and NGSS
##
def self.nat_stds_guid_from_auths(authorities)
if AcademicBenchmark.v3?
stds = authorities.find{|a| a.description == NATIONAL_STANDARDS_TITLE}
stds.try(:guid)
else
authorities.find{|a| a["title"] == NATIONAL_STANDARDS_TITLE}["guid"]
end
stds = authorities.find{|a| a.description == NATIONAL_STANDARDS_TITLE}
stds.try(:guid)
end
##
@ -103,12 +73,7 @@ module AcademicBenchmark
# browsed in order to retrieve specifics like NGSS and Common Core
##
def self.retrieve_authorities(api)
if AcademicBenchmark.v3?
self.sort_authorities(api.standards.authorities)
else
authorities = api.list_available_authorities.select { |a| a.key?("title") }
authorities.sort_by {|b| b["title"]}
end
self.sort_authorities(api.standards.authorities)
end
# sort national standards at the top, followed by country standards,
@ -138,17 +103,12 @@ module AcademicBenchmark
# prepend the common core, next gen science standards,
# and the ISTE (NETS) standards to the list
auth_list.unshift(self.extract_nat_stds(api, self.nat_stds_guid_from_auths(auth_list)))
if self.v3?
auth_list.unshift(api.standards.authority_documents(NGSS_AUTHORITY))
auth_list.unshift(api.standards.authority_documents(COMMON_CORE_AUTHORITY))
auth_list.unshift(api.standards.authority_documents(NGSS_AUTHORITY))
auth_list.unshift(api.standards.authority_documents(COMMON_CORE_AUTHORITY))
# flatten down the list of authorities and hashify it
auth_list.flatten!
auth_list.map(&:to_h)
else
# append the UK standards to the end of the list and flatten it down
auth_list.push(self.uk_guid(api)).flatten
end
# flatten down the list of authorities and hashify it
auth_list.flatten!
auth_list.map(&:to_h)
end
# The UK standards are now available to us as well,
@ -159,22 +119,18 @@ module AcademicBenchmark
class APIError < StandardError; end
def self.import(guid, options = {})
if self.v3?
is_auth = self.auth?(guid)
authority = is_auth ? guid : nil
document = is_auth ? nil : guid
check_args(authority, document)
self.ensure_ab_credentials
is_auth = self.auth?(guid)
authority = is_auth ? guid : nil
document = is_auth ? nil : guid
check_args(authority, document)
self.ensure_ab_credentials
AcademicBenchmark.queue_migration_for(
authority: authority,
document: document,
user: self.authorized?,
options: options
).first
else
AcademicBenchmarkV1.import(Array(guid), options).first
end
AcademicBenchmark.queue_migration_for(
authority: authority,
document: document,
user: self.authorized?,
options: options
).first
end
def self.queue_migration_for(authority:, document:, user:, options: {})
@ -195,29 +151,23 @@ module AcademicBenchmark
end
def self.set_common_core_setting!
unless self.v3?
AcademicBenchmarkV1.set_common_core_setting!
if (guid = AcademicBenchmark.config[:common_core_guid])
if (group = LearningOutcomeGroup.where(migration_id: guid).first)
Setting.set(common_core_setting_key, group.id)
end
end
end
def self.common_core_setting_key
unless self.v3?
AcademicBenchmarkV1.common_core_setting_key
end
"common_core_outcome_group_id:#{Shard.current.id}"
end
def self.api_handle
# create a new api connection. Note that this does not actually
# make a request to the API
if AcademicBenchmark.v3?
AcademicBenchmarks::Api::Handle.new(partner_id: config[:partner_id], partner_key: config[:partner_key])
else
AcademicBenchmark::Api.new(self.config["api_key"], base_url: self.config["api_url"])
end
AcademicBenchmarks::Api::Handle.new(partner_id: config[:partner_id], partner_key: config[:partner_key])
end
private
def self.auth?(guid)
self.api_handle.standards.authorities.map(&:guid).include?(guid)
end

View File

@ -1,129 +0,0 @@
#
# Copyright (C) 2012 - 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/>.
module AcademicBenchmark
class Api
API_BASE_URL = "http://api.statestandards.com/services/rest/"
BROWSE = "browse"
SEARCH = "search"
MAINTAIN_ACCESS = "maintainAccess"
MA_LIST = 'list'
MA_REMOVE = 'remove'
MA_ADD = 'add'
READ_TIMEOUT = 5.minutes.to_i
def initialize(api_key, opts={})
@api_key = api_key
@base_url = opts[:base_url] || API_BASE_URL
end
def browse(opts={})
set_defaults!(opts)
opts[:levels] ||= 1
get_ab_results(@base_url + BROWSE, opts)
end
def search(opts={})
set_defaults!(opts)
get_ab_results(@base_url + SEARCH, opts)
end
# returns a single list of all authorities across all available countries
def list_available_authorities(format = 'json')
res = browse({:levels => 2, :format => format})
auths = []
res.each do |country|
next unless country["itm"]
auths += country["itm"]
end
auths
end
def browse_authority(auth_code, opts={})
opts[:authority] = auth_code
browse(opts)
end
def browse_guid(guid, opts={})
opts[:guid] = guid
browse(opts)
end
def maintain_access(operation, params={})
set_defaults!(params)
params[:op] ||= operation
get_ab_results(@base_url + MAINTAIN_ACCESS, params)
end
def list_ips
maintain_access(MA_LIST)
end
def add_ip(ip, note=nil)
maintain_access(MA_ADD, :addr => ip, :note => note)
end
def remove_ip(ip)
maintain_access(MA_REMOVE, :addr => ip)
end
def get_ab_results(url, params={})
res = Api.get_url(url + query_string_from_hash(params))
if res.code.to_i == 200
parse_ab_data(res.body)
else
raise APIError.new("HTTP Error: #{res.code} - #{res.body}")
end
end
def parse_ab_data(json_string)
data = JSON.parse(json_string, :max_nesting => 50)
if data["status"] == "ok"
return data["itm"] || data["access"] || []
else
if data["ab_err"]
raise APIError.new("responseCode: #{data["ab_err"]["code"]} - #{data["ab_err"]["msg"]}")
else
raise APIError.new("response: #{data.to_json}")
end
end
end
def self.get_url(url)
uri = URI(url)
Net::HTTP.new(uri.host, uri.port).start { |http|
http.read_timeout = READ_TIMEOUT
http.request_get(uri.request_uri)
}
end
private
def query_string_from_hash(params)
return "" if params.empty?
"?".concat(params.map{|k, v| v ? "#{k}=#{CGI::escape(v.to_s)}" : nil}.compact.sort.join('&'))
end
def set_defaults!(opts)
opts[:api_key] ||= @api_key
opts[:format] ||= 'json'
end
end
end

View File

@ -1,152 +0,0 @@
#
# Copyright (C) 2015 - 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 'httparty'
module AcademicBenchmark
class CliTools
def self.whitelisted_ips
HTTParty.get("#{api_url}maintainAccess?api_key=#{api_key}")
end
def self.whitelist_ip(ip, note)
HTTParty.get(
"#{api_url}maintainAccess?api_key=#{api_key}&op=add&addr=#{ip}&note=#{note}"
)
end
def self.remove_from_whitelist(ip)
if self.is_ip_address(ip)
self.remove_ip_from_whitelist(ip)
else
self.remove_note_from_whitelist(ip)
end
end
def self.remove_ip_from_whitelist(ip)
HTTParty.get(
"#{api_url}maintainAccess?api_key=#{api_key}&op=remove&addr=#{ip}"
)
end
def self.remove_note_from_whitelist(note)
ips = self.whitelisted_ips
if ips["ab_rsp"] && ips["ab_rsp"]["access"]
ips["ab_rsp"]["access"].each do |entry|
return remove_ip_from_whitelist(entry["addr"]) if entry["note"] == note
end
puts "There were no whitelisted IP addresses with a note matching '#{note}'"
else
puts "Error retrieving list of whitelisted IP addresses: #{ips.to_json}"
end
end
def self.whitelisted?(ip)
ips = whitelisted_ips
ips["ab_rsp"] && ips["ab_rsp"]["access"].any?{ |i| i["addr"] == ip }
end
def self.delete_imported_outcomes(parent_group_title, no_prompt: false, override_shard_restriction: false)
unless no_prompt
return unless warn_shard
return unless warn_deleting(parent_group_title)
end
Rails.logger.warn("AcademicBenchmark::CliTools - deleting outcomes under #{parent_group_title}")
delete_with_children(LearningOutcomeGroup.where(title: parent_group_title).first)
Rails.logger.warn("AcademicBenchmark::CliTools - finished deleting outcomes under #{parent_group_title}")
end
# Make sure this account is on its own shard
# If it is not, then we could affect other schools
def self.own_shard
Account.root_accounts.count <= 1
end
private
def self.warn_deleting(title)
print "WARNING: You are about to delete all imported outcomes under #{title} for this shard. Proceed? (Y/N): "
return false unless STDIN.gets.chomp.downcase == "y"
return true
end
private
def self.warn_shard
unless own_shard
print "WARNING: This shard has more than one account on it! This means you will affect multiple customers with your actions. Proceed? (Y/N): "
return false unless STDIN.gets.chomp.downcase == "y"
end
true
end
private
def self.delete_with_children(item, no_prompt: false, override_shard_restriction: false)
expected_types = [LearningOutcomeGroup, ContentTag]
if !no_prompt && !expected_types.include?(item.class)
puts "Expected #{expected_types.map{|t| t.to_s}.join(" or ") } but received a '#{item.class.to_s}'"
return
end
if item.is_a?(LearningOutcomeGroup)
# These two queries can be combined when we hit rails 4
# and have multi-column pluck
child_outcome_links = ContentTag.where(
tag_type: 'learning_outcome_association',
content_type: 'LearningOutcome',
context_id: item.id
).pluck(:id)
child_outcome_ids = ContentTag.where(
tag_type: 'learning_outcome_association',
content_type: 'LearningOutcome',
context_id: item.id
).pluck(:content_id)
# delete all links to our children
ContentTag.destroy(child_outcome_links)
# delete all of our children
LearningOutcome.destroy(child_outcome_ids)
item.child_outcome_groups.each do |child|
delete_with_children(child)
end
item.destroy_permanently!
else
item.destroy_permanently!
end
end
private
def self.api_key
AcademicBenchmark.config["api_key"]
end
private
def self.api_url
AcademicBenchmark.config["api_url"]
end
private
def self.is_ip_address(ip)
# this simple and brief regex matches IP addresses strictly
ip =~ %r{\b(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\b}
end
end # class CliTools
end # module AcademicBenchmark

View File

@ -19,52 +19,47 @@ require 'academic_benchmarks'
module AcademicBenchmark
class Converter < Canvas::Migration::Migrator
COMMON_CORE_GUID = 'A83297F2-901A-11DF-A622-0C319DFF4B22'.freeze
def initialize(settings={})
super(settings, "academic_benchmark")
@ratings_overrides = settings[:migration_options] || {}
@course[:learning_outcomes] = []
@converter_v1 = ConverterV1.new(settings)
@partner_id = settings[:partner_id]
@partner_key = settings[:partner_key]
end
def export
if AcademicBenchmark.v3?
unless content_migration
raise Canvas::Migration::Error,
"Missing required content_migration settings"
end
unless Account.site_admin.grants_right?(content_migration.user, :manage_global_outcomes)
raise Canvas::Migration::Error,
"User isn't allowed to edit global outcomes"
end
unless content_migration
raise Canvas::Migration::Error,
"Missing required content_migration settings"
end
unless Account.site_admin.grants_right?(content_migration.user, :manage_global_outcomes)
raise Canvas::Migration::Error,
"User isn't allowed to edit global outcomes"
end
unless @archive_file
unless @partner_id.present? || AcademicBenchmark.ensure_partner_id.nil?
raise Canvas::Migration::Error, I18n.t("A partner ID is required to use Academic Benchmarks")
end
unless @partner_key.present? || AcademicBenchmark.ensure_partner_key.nil?
raise Canvas::Migration::Error, I18n.t("A partner key is required to use Academic Benchmarks")
end
if outcome_data.present?
if outcome_data.instance_of? AcademicBenchmarks::Standards::StandardsForest
outcome_data.trees.each do |t|
@course[:learning_outcomes] << t.root.build_outcomes(@ratings_overrides)
end
else
@course[:learning_outcomes] << outcome_data.root.build_outcomes(@ratings_overrides)
end
end
save_to_file
@course
else
@converter_v1.export
end
if outcome_data.present?
if outcome_data.instance_of? AcademicBenchmarks::Standards::StandardsForest
outcome_data.trees.each do |t|
@course[:learning_outcomes] << t.root.build_outcomes(@ratings_overrides)
end
else
@course[:learning_outcomes] << outcome_data.root.build_outcomes(@ratings_overrides)
end
end
save_to_file
@course
end
def post_process
unless AcademicBenchmark.v3?
@converter_v1.post_process
end
end
def post_process; end
private
def outcome_data

View File

@ -1,179 +0,0 @@
#
# Copyright (C) 2012 - 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/>.
module AcademicBenchmark
class ConverterV1 < Canvas::Migration::Migrator
COMMON_CORE_GUID = 'A83297F2-901A-11DF-A622-0C319DFF4B22'.freeze
DEFAULT_DEPTH = 3
def initialize(settings={})
super(settings, "academic_benchmark")
ab_settings = AcademicBenchmark.config
@ratings_overrides = settings[:migration_options] || {}
@api_key = settings[:api_key] || ab_settings[:api_key]
@api = AcademicBenchmark::Api.new(@api_key, :base_url => settings[:base_url] || ab_settings[:api_url])
@common_core_guid = settings[:common_core_guid] || ab_settings[:common_core_guid].presence
@course[:learning_outcomes] = []
end
def export
if content_migration && !Account.site_admin.grants_right?(content_migration.user, :manage_global_outcomes)
raise Canvas::Migration::Error,
I18n.t("User isn't allowed to edit global outcomes")
end
if @archive_file
convert_file
elsif @settings[:authorities] || @settings[:guids]
if @api_key && !@api_key.empty?
convert_authorities(@settings[:authorities]) if @settings[:authorities]
convert_guids(@settings[:guids]) if @settings[:guids]
else
raise Canvas::Migration::Error,
I18n.t("An API key is required to use Academic Benchmarks")
end
else
raise Canvas::Migration::Error, I18n.t("No outcome file or authority given")
end
save_to_file
@course
end
def post_process
if importing_common_core?
AcademicBenchmarkV1.set_common_core_setting!
end
end
def importing_common_core?
(@settings[:guids] && @settings[:guids].member?(AcademicBenchmark.config[:common_core_guid])) ||
(@settings[:authorities] && @settings[:authorities].member?("CC"))
end
def convert_file
data = @api.parse_ab_data(@archive_file.read)
process_json_data(data)
rescue APIError => e
add_error(
I18n.t("The provided Academic Benchmark file has an error"),
{
exception: e,
error_message: e.message
}
)
end
def convert_authorities(authorities=[])
authorities.each do |auth|
refresh_outcomes(:authority => auth)
end
end
def convert_guids(guids=[])
guids.each do |guid|
refresh_outcomes(:guid => guid)
end
end
def refresh_outcomes(opts)
res = build_full_auth_hash(opts)
process_json_data(res)
rescue EOFError, APIError => e
add_error(
I18n.t(
"Couldn't update standards for authority %{auth}",
:auth => opts[:authority] || opts[:guid]
),
{
exception: e,
error_message: e.message
}
)
end
# get a shallow tree for the authority then process the leaves
def build_full_auth_hash(opts)
data = @api.browse({:levels => DEFAULT_DEPTH}.merge(opts))
process_leaves!(find_by_prop(data, "type", "authority"))
end
# recursively find leaf nodes with children available to fetch
# fetch the children and then process them
def process_leaves!(data)
if data.is_a? Array
data.each do |itm|
process_leaves!(itm)
end
elsif data.is_a? Hash
if data["itm"]
data["itm"].each do |itm|
process_leaves!(itm)
end
elsif data["chld"]
count = data.delete("chld").to_i
if count > 0
data.delete("chld")
children_tree = @api.browse({:levels => DEFAULT_DEPTH, :guid => data["guid"]})
dup_with_children = find_by_prop(children_tree, "guid", data["guid"])
if data["guid"] == dup_with_children["guid"]
data["itm"] = dup_with_children["itm"]
process_leaves!(data)
end
end
end
end
data
end
def process_json_data(data)
if data = find_by_prop(data, "type", "authority")
outcomes = Standard.new(data).build_outcomes(@ratings_overrides)
@course[:learning_outcomes] << outcomes
else
err_msg = I18n.t("Couldn't find an authority to update")
add_error(
err_msg, { exception: nil, error_message: err_msg }
)
end
end
def find_by_prop(data, prop, value)
return nil unless data
if data.is_a? Array
data.each do |itm|
if found = find_by_prop(itm, prop, value)
return found
end
end
elsif data.is_a? Hash
if data[prop] && data[prop] == value
return data
elsif data["itm"]
return find_by_prop(data["itm"], prop, value)
end
end
nil
end
end
end

View File

@ -28,9 +28,7 @@ module AcademicBenchmark
:settings_partial => 'academic_benchmark/plugin_settings',
:hide_from_users => true,
:settings => {
:api_key => nil,
:api_url => AcademicBenchmark::Api::API_BASE_URL,
:common_core_guid => AcademicBenchmark::ConverterV1::COMMON_CORE_GUID,
:common_core_guid => AcademicBenchmark::Converter::COMMON_CORE_GUID,
:partner_id => nil,
:partner_key => nil,
:worker => 'CCWorker',

View File

@ -1,165 +0,0 @@
#
# Copyright (C) 2012 - 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/>.
module AcademicBenchmark
class Standard
def initialize(data, parent=nil)
@data = data
@parent = parent
@children = []
if has_items?
items.each do |itm|
Standard.new(itm, self)
end
end
# ignore course types and leaves that don't have a num
if num || @children.any?
@parent.add_child(self) if parent
end
end
def build_outcomes(ratings={})
hash = {:migration_id => guid, :vendor_guid => guid, :low_grade => low_grade, :high_grade => high_grade, :is_global_standard => true}
hash[:description] = description
if is_leaf?
# create outcome
hash[:type] = 'learning_outcome'
hash[:title] = build_num_title
set_default_ratings(hash, ratings)
else
#create outcome group
hash[:type] = 'learning_outcome_group'
hash[:title] = build_title
hash[:outcomes] = @children.map {|chld| chld.build_outcomes(ratings)}
end
hash
end
def add_child(itm)
@children << itm
end
def has_items?
!!(items && items.any?)
end
def items
@data["itm"]
end
def guid
@data["guid"]
end
def type
@data["type"]
end
def title
@data["title"]
end
# standards don't have titles so they are built from parent standards/groups
# it is generated like this:
# if I have a num, use it and all parent nums on standards
# if I don't have a num, use my description (potentially truncated at 50)
def build_num_title
# when traversing AB data, "standards" will always be deeper in the data
# hierarchy, so this code will always hit the else before a @parent is nil
if @parent.is_standard?
base = @parent.build_num_title
if base && num
num.include?(base) ? num : base + '.' + num
elsif base
base
else
num
end
else
num
end
end
def build_title
if num
build_num_title + " - " + (title || cropped_description)
else
title || cropped_description
end
end
def num
get_meta_field("num")
end
def description
get_meta_field("descr")
end
def cropped_description
# get the first 50 chars of description in a utf-8 friendly way
description && description[/.{0,50}/u]
end
def name
get_meta_field("name")
end
def high_grade
if @data["meta"] && @data["meta"]["name"]
@data["meta"]["hi"]
else
@parent && @parent.high_grade
end
end
def low_grade
if @data["meta"] && @data["meta"]["name"]
@data["meta"]["lo"]
else
@parent && @parent.low_grade
end
end
def get_meta_field(field)
@data["meta"] && @data["meta"][field] && @data["meta"][field]["content"]
end
def is_standard?
type == 'standard'
end
# it's only a leaf if it's a standard and has no children, or no children with a 'num'
# having a num is to ignore extra description nodes that we want to ignore
def is_leaf?
num && @children.empty?
end
def set_default_ratings(hash, overrides={})
hash[:ratings] = [{:description => "Exceeds Expectations", :points => 5},
{:description => "Meets Expectations", :points => 3},
{:description => "Does Not Meet Expectations", :points => 0}]
hash[:mastery_points] = 3
hash[:points_possible] = 5
hash.merge!(overrides)
end
end
end

View File

@ -1,74 +0,0 @@
#
# Copyright (C) 2012 - 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 'net/http'
require 'cgi'
module AcademicBenchmarkV1
def self.import(guid_or_guids, options={})
if !AcademicBenchmark.config[:api_key] || AcademicBenchmark.config[:api_key].empty?
raise Canvas::Migration::Error, "Not importing academic benchmark data because no API key is set"
end
# need a user with global outcome management rights
user_id = Setting.get("academic_benchmark_migration_user_id", nil)
unless user_id.present?
raise Canvas::Migration::Error, "Not importing academic benchmark data because no user id set"
end
unless (permissionful_user = User.where(id: user_id).first)
raise Canvas::Migration::Error, "Not importing academic benchmark data because no user found"
end
Array(guid_or_guids).map do |guid|
AcademicBenchmarkV1.queue_migration_for_guid(guid, permissionful_user, options).first
end
end
def self.queue_migration_for_guid(guid, user, options={})
unless Account.site_admin.grants_right?(user, :manage_global_outcomes)
raise Canvas::Migration::Error,
I18n.t("User isn't allowed to edit global outcomes")
end
cm = ContentMigration.new(:context => Account.site_admin)
cm.converter_class = AcademicBenchmark.config['converter_class']
cm.migration_settings[:migration_type] = 'academic_benchmark_importer'
cm.migration_settings[:import_immediately] = true
cm.migration_settings[:guids] = [guid]
cm.migration_settings[:no_archive_file] = true
cm.migration_settings[:skip_import_notification] = true
cm.migration_settings[:skip_job_progress] = true
cm.migration_settings[:migration_options] = options
cm.strand = "academic_benchmark"
cm.user = user
cm.save!
[cm, cm.export_content]
end
def self.set_common_core_setting!
if (guid = AcademicBenchmark.config[:common_core_guid])
if (group = LearningOutcomeGroup.where(migration_id: guid).first)
Setting.set(common_core_setting_key, group.id)
end
end
end
def self.common_core_setting_key
"common_core_outcome_group_id:#{Shard.current.id}"
end
end

View File

@ -1,6 +1,5 @@
# encoding: utf-8
#
# Copyright (C) 2012 - present Instructure, Inc.
# Copyright (C) 2015 - present Instructure, Inc.
#
# This file is part of Canvas.
#
@ -28,18 +27,17 @@ describe AcademicBenchmark::Converter do
@cm.converter_class = @plugin.settings['converter_class']
@cm.migration_settings[:migration_type] = 'academic_benchmark_importer'
@cm.migration_settings[:import_immediately] = true
@cm.migration_settings[:base_url] = "http://example.com/"
@cm.migration_settings[:migration_options] = {points_possible: 10, mastery_points: 6,
ratings: [{description: 'Bad', points: 0}, {description: 'Awesome', points: 10}]}
@cm.user = @user
@cm.save!
@level_0_browse = File.join(File.dirname(__FILE__) + '/fixtures', 'example.json')
@a_levels_3 = File.join(File.dirname(__FILE__) + '/fixtures', 'a_levels_3.json')
@d_levels_3 = File.join(File.dirname(__FILE__) + '/fixtures', 'd_levels_3.json')
@j_levels_3 = File.join(File.dirname(__FILE__) + '/fixtures', 'j_levels_3.json')
@authority_list = File.join(File.dirname(__FILE__) + '/fixtures', 'auth_list.json')
@florida_auth_list = File.join(File.dirname(__FILE__) + '/fixtures', 'florida_auth_list.json')
current_settings = @plugin.settings
new_settings = current_settings.merge(:partner_id => "instructure", :partner_key => "secret")
allow(@plugin).to receive(:settings).and_return(new_settings)
@level_0_browse = File.join(File.dirname(__FILE__) + '/fixtures', 'api_all_standards_response.json')
@florida_auth_list = File.join(File.dirname(__FILE__) + '/fixtures', 'florida_auth_list_v3.json')
File.open(@level_0_browse, 'r') do |file|
@att = Attachment.create!(
:filename => 'standards.json',
@ -61,78 +59,85 @@ describe AcademicBenchmark::Converter do
def verify_full_import
@root_group = LearningOutcomeGroup.global_root_outcome_group
expect(@root_group.child_outcome_groups.count).to eq 1
expect(@root_group.child_outcome_groups.count).to eq 2
a = @root_group.child_outcome_groups.first
expect(a.migration_id).to eq "aaaaaaaaaa"
expect(a.title).to eq "NGA Center/CCSSO"
expect(a.migration_id).to eq "CEC2CF6C-67AD-11DF-AB5F-995D9DFF4B22"
expect(a.title).to eq "CCSS.ELA-Literacy.CCRA.R - Reading"
b = a.child_outcome_groups.first
expect(b.migration_id).to eq "bbbbbbbbbbbb"
expect(b.title).to eq "Common Core State Standards"
c = b.child_outcome_groups.first
expect(c.migration_id).to eq "cccccccccc"
expect(c.title).to eq "College- and Career-Readiness Standards and K-12 Mathematics"
d = c.child_outcome_groups.where(migration_id: "ddddddddd").first
expect(d.migration_id).to eq "ddddddddd"
expect(d.title).to eq "Kindergarten"
expect(d.low_grade).to eq "K"
expect(d.high_grade).to eq "K"
e = d.child_outcome_groups.first
expect(e.migration_id).to eq "eeeeeeeeeeee"
expect(e.title).to eq "K.CC - Counting and Cardinality"
expect(e.description).to eq "Counting and Cardinality"
expect(e.low_grade).to eq "K"
expect(e.high_grade).to eq "K"
f = e.child_outcome_groups.first
expect(f.migration_id).to eq "ffffffffffffff"
expect(f.title).to eq "Know number names and the count sequence."
expect(f.description).to eq "Know number names and the count sequence."
expect(f.low_grade).to eq "K"
expect(f.high_grade).to eq "K"
expect(f.child_outcome_links.count).to eq 3
g = LearningOutcome.global.where(migration_id: "ggggggggggggggggg").first
expect(b.migration_id).to eq "CEB79A48-67AD-11DF-AB5F-995D9DFF4B22"
expect(b.title).to eq "Key Ideas and Details"
g = LearningOutcome.global.where(migration_id: "CEB87C92-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "K.CC.1"
expect(g.description).to eq "Count to 100 by ones and by tens."
g = LearningOutcome.global.where(migration_id: "hhhhhhhhhhhhhhhh").first
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.1"
expect(g.description).to eq "Read closely to determine what the text says explicitly and to make logical" \
" inferences from it; cite specific textual evidence when writing or speaking to support conclusions drawn" \
" from the text."
g = LearningOutcome.global.where(migration_id: "CEB8EE66-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "K.CC.2"
expect(g.description).to eq "Count forward beginning from a given number within the known sequence (instead of having to begin at 1)."
g = LearningOutcome.global.where(migration_id: "iiiiiiiiiiiiiiiii").first
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.2"
expect(g.description).to eq "Determine central ideas or themes of a text and analyze their development;" \
" summarize the key supporting details and ideas."
g = LearningOutcome.global.where(migration_id: "CEB96684-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "K.CC.3"
expect(g.description).to eq "Write numbers from 0 to 20. Represent a number of objects with a written numeral 0-20 (with 0 representing a count of no objects)."
j = c.child_outcome_groups.where(migration_id: "jjjjjjjjjjj").first
expect(j.migration_id).to eq "jjjjjjjjjjj"
expect(j.title).to eq "First Grade"
expect(j.low_grade).to eq "1"
expect(j.high_grade).to eq "1"
k = j.child_outcome_groups.last
expect(k.migration_id).to eq "kkkkkkkkkkk"
expect(k.title).to eq "1.DD - zééééééééééééééééééééééééééééééééééééééééééééééééé"
expect(k.description).to eq "zéééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
expect(k.low_grade).to eq "1"
expect(k.high_grade).to eq "1"
l = k.child_outcome_groups.first
expect(l.migration_id).to eq "lllllllll"
expect(l.title).to eq "Something else"
expect(l.description).to eq "Something else"
expect(l.low_grade).to eq "1"
expect(l.high_grade).to eq "1"
expect(l.child_outcome_links.count).to eq 1
m = LearningOutcome.global.where(migration_id: "mmmmmmmmmmm").first
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.3"
expect(g.description).to eq "Analyze how and why individuals, events, and ideas develop and interact over" \
" the course of a text."
g = LearningOutcome.global.where(migration_id: "CEBAB958-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(m.short_description).to eq "1.DD.1"
expect(m.description).to eq "And something else"
expect(m.title).to eq "1.DD.1"
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.4"
expect(g.description).to eq "Interpret words and phrases as they are used in a text, including determining" \
" technical, connotative, and figurative meanings, and analyze how specific word choices shape meaning or tone."
g = LearningOutcome.global.where(migration_id: "CEBB9AA8-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.5"
expect(g.description).to eq "Analyze the structure of texts, including how specific sentences, paragraphs," \
" and larger portions of the text (e.g., a section, chapter, scene, or stanza) relate to each other and" \
" the whole."
g = LearningOutcome.global.where(migration_id: "CEBC89F4-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.6"
expect(g.description).to eq "Assess how point of view or purpose shapes the content and style of a text."
g = LearningOutcome.global.where(migration_id: "CEBDDCA0-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.7"
expect(g.description).to eq "Integrate and evaluate content presented in diverse media and formats," \
" including visually and quantitatively, as well as in words."
g = LearningOutcome.global.where(migration_id: "CEBE4D52-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.8"
expect(g.description).to eq "Delineate and evaluate the argument and specific claims in a text," \
" including the validity of the reasoning as well as the relevance and sufficiency of the evidence."
g = LearningOutcome.global.where(migration_id: "CEBF37B2-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.9"
expect(g.description).to eq "Analyze how two or more texts address similar themes or topics in order" \
" to build knowledge or to compare the approaches the authors take."
g = LearningOutcome.global.where(migration_id: "CEC08B44-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.10"
expect(g.description).to eq "Read and comprehend complex literary and informational texts independently" \
" and proficiently."
g = LearningOutcome.global.where(migration_id: "CEC49A36-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.1"
expect(g.description).to eq "Write arguments to support claims in an analysis of substantive topics or" \
" texts, using valid reasoning and relevant and sufficient evidence."
g = LearningOutcome.global.where(migration_id: "CEC57CD0-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.2"
expect(g.description).to eq "Write informative/explanatory texts to examine and convey complex ideas and" \
" information clearly and accurately through the effective selection, organization, and analysis of content."
g = LearningOutcome.global.where(migration_id: "CEC665B4-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.3"
expect(g.description).to eq "Write narratives to develop real or imagined experiences or events" \
" using effective technique, well-chosen details, and well-structured event sequences."
end
def check_for_parent_num_duplication(outcome)
parent = outcome.instance_variable_get('@parent')
if outcome.num && parent && parent.is_standard? && parent.title && outcome.num.include?(parent.title)
outcome.title == "#{parent.title}.#{outcome.num}"
if outcome.number && parent && parent.build_title && outcome.number.include?(parent.build_title)
outcome.title == "#{parent.title}.#{outcome.number}"
else
false
end
@ -165,108 +170,38 @@ describe AcademicBenchmark::Converter do
expect(@cm.workflow_state).to eq 'failed'
end
it "should fail if no file or authority set" do
@cm.attachment = nil
@cm.migration_settings[:no_archive_file] = true
@cm.save!
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "No outcome file or authority given"
expect(@cm.workflow_state).to eq 'failed'
end
context "using the API" do
append_before do
@plugin.settings['api_key'] = "oioioi"
@cm.attachment = nil
@cm.migration_settings[:no_archive_file] = true
@cm.migration_settings[:authorities] = ["CC"]
@cm.save!
end
def run_and_check
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 0
expect(@cm.workflow_state).to eq 'imported'
@root_group = LearningOutcomeGroup.global_root_outcome_group
expect(@root_group.child_outcome_groups.count).to eq 1
a = @root_group.child_outcome_groups.first
expect(a.migration_id).to eq "aaaaaaaaaa"
end
it "should fail with no API key" do
@plugin.settings['api_key'] = nil
it "should fail with no partner ID" do
@plugin.settings[:partner_id] = nil
@plugin.settings[:partner_key] = "a"
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "An API key is required to use Academic Benchmarks"
expect(@cm.migration_issues.first.description).to eq "A partner ID is required to use Academic Benchmarks"
expect(@cm.workflow_state).to eq 'failed'
end
it "should fail with an empty string API key" do
@plugin.settings['api_key'] = ""
it "should fail with an empty string partner ID" do
current_settings = @plugin.settings
new_settings = current_settings.merge(:partner_id => "", :partner_key => "a")
allow(@plugin).to receive(:settings).and_return(new_settings)
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "An API key is required to use Academic Benchmarks"
expect(@cm.migration_issues.first.description).to eq "A partner ID is required to use Academic Benchmarks"
expect(@cm.workflow_state).to eq 'failed'
end
it "should use the API to get the set data with an authority short code" do
response = double()
allow(response).to receive(:body).and_return(File.read(@level_0_browse))
allow(response).to receive(:code).and_return("200")
expect(AcademicBenchmark::Api).to receive(:get_url).with("http://example.com/browse?api_key=oioioi&authority=CC&format=json&levels=3").and_return(response)
run_and_check
verify_full_import
end
it "should use the API to get the set data with a guid" do
@cm.migration_settings[:authorities] = nil
@cm.migration_settings[:guids] = ["aaaaaaaaaa"]
response = double('a_levels_3')
allow(response).to receive(:body).and_return(File.read(@a_levels_3))
allow(response).to receive(:code).and_return("200")
expect(AcademicBenchmark::Api).to receive(:get_url).with("http://example.com/browse?api_key=oioioi&format=json&guid=aaaaaaaaaa&levels=3").and_return(response)
responsed = double('d_levels_3')
allow(responsed).to receive(:body).and_return(File.read(@d_levels_3))
allow(responsed).to receive(:code).and_return("200")
expect(AcademicBenchmark::Api).to receive(:get_url).with("http://example.com/browse?api_key=oioioi&format=json&guid=ddddddddd&levels=3").and_return(responsed)
responsej = double('j_levels_3')
allow(responsej).to receive(:body).and_return(File.read(@j_levels_3))
allow(responsej).to receive(:code).and_return("200")
expect(AcademicBenchmark::Api).to receive(:get_url).with("http://example.com/browse?api_key=oioioi&format=json&guid=jjjjjjjjjjj&levels=3").and_return(responsej)
run_and_check
verify_full_import
end
it "should warn when api returns non-success" do
response = double()
allow(response).to receive(:body).and_return(%{{"status":"fail","ab_err":{"msg":"API key access violation.","code":"401"}}})
allow(response).to receive(:code).and_return("200")
expect(AcademicBenchmark::Api).to receive(:get_url).with("http://example.com/browse?api_key=oioioi&authority=CC&format=json&levels=3").and_return(response)
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_settings[:last_error]).to be_nil
expect(@cm.workflow_state).to eq 'imported'
end
end
# This test came about because the titles being generated for
@ -277,10 +212,28 @@ describe AcademicBenchmark::Converter do
#
# instead of this:
#
# LAFS.1.L.1.1.a instead of LAFS.1.L.LAFS.1.L.1.LAFS.1.L.1.1.a
# LAFS.1.L.1.1.a
#
it "doesn't duplicate the base numbers when building a title" do
json_data = JSON.parse(File.read(@florida_auth_list))
check_built_outcome(AcademicBenchmark::Standard.new(json_data))
AcademicBenchmarks::Standards::StandardsForest.new(json_data).trees.each do |tree|
tree.children.each do |outcome|
check_built_outcome(outcome)
end
end
end
it "raises error with invalid user id" do
expect { AcademicBenchmark.ensure_real_user(user_id: 0) }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because no user found matching id '0'")
end
it "raises error when crendentials are not set" do
allow(AcademicBenchmark).to receive(:config).and_return({})
expect{ AcademicBenchmark.ensure_ab_credentials }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because the Academic Benchmarks Partner ID is not set")
allow(AcademicBenchmark).to receive(:config).and_return({partner_id: "user"})
expect{ AcademicBenchmark.ensure_ab_credentials }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because the Academic Benchmarks Partner key is not set")
end
end

View File

@ -1,240 +0,0 @@
#
# Copyright (C) 2015 - 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 File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe AcademicBenchmark::Converter do
before(:each) do
@root_account = Account.site_admin
account_admin_user(:account => @root_account, :active_all => true)
@cm = ContentMigration.new(:context => @root_account)
@plugin = Canvas::Plugin.find('academic_benchmark_importer')
@cm.converter_class = @plugin.settings['converter_class']
@cm.migration_settings[:migration_type] = 'academic_benchmark_importer'
@cm.migration_settings[:import_immediately] = true
@cm.migration_settings[:migration_options] = {points_possible: 10, mastery_points: 6,
ratings: [{description: 'Bad', points: 0}, {description: 'Awesome', points: 10}]}
@cm.user = @user
@cm.save!
current_settings = @plugin.settings
new_settings = current_settings.merge(:partner_id => "instructure", :partner_key => "secret")
allow(@plugin).to receive(:settings).and_return(new_settings)
@level_0_browse = File.join(File.dirname(__FILE__) + '/fixtures', 'api_all_standards_response.json')
@florida_auth_list = File.join(File.dirname(__FILE__) + '/fixtures', 'florida_auth_list_v3.json')
File.open(@level_0_browse, 'r') do |file|
@att = Attachment.create!(
:filename => 'standards.json',
:display_name => 'standards.json',
:uploaded_data => file,
:context => @cm
)
end
@cm.attachment = @att
@cm.save!
end
def verify_rubric_criterion(outcome)
expect(outcome.data[:rubric_criterion][:mastery_points]).to eq 6
expect(outcome.data[:rubric_criterion][:points_possible]).to eq 10
expect(outcome.data[:rubric_criterion][:ratings]).to eq [{description: 'Bad', points: 0},
{description: 'Awesome', points: 10}]
end
def verify_full_import
@root_group = LearningOutcomeGroup.global_root_outcome_group
expect(@root_group.child_outcome_groups.count).to eq 2
a = @root_group.child_outcome_groups.first
expect(a.migration_id).to eq "CEC2CF6C-67AD-11DF-AB5F-995D9DFF4B22"
expect(a.title).to eq "CCSS.ELA-Literacy.CCRA.R - Reading"
b = a.child_outcome_groups.first
expect(b.migration_id).to eq "CEB79A48-67AD-11DF-AB5F-995D9DFF4B22"
expect(b.title).to eq "Key Ideas and Details"
g = LearningOutcome.global.where(migration_id: "CEB87C92-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.1"
expect(g.description).to eq "Read closely to determine what the text says explicitly and to make logical" \
" inferences from it; cite specific textual evidence when writing or speaking to support conclusions drawn" \
" from the text."
g = LearningOutcome.global.where(migration_id: "CEB8EE66-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.2"
expect(g.description).to eq "Determine central ideas or themes of a text and analyze their development;" \
" summarize the key supporting details and ideas."
g = LearningOutcome.global.where(migration_id: "CEB96684-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.3"
expect(g.description).to eq "Analyze how and why individuals, events, and ideas develop and interact over" \
" the course of a text."
g = LearningOutcome.global.where(migration_id: "CEBAB958-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.4"
expect(g.description).to eq "Interpret words and phrases as they are used in a text, including determining" \
" technical, connotative, and figurative meanings, and analyze how specific word choices shape meaning or tone."
g = LearningOutcome.global.where(migration_id: "CEBB9AA8-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.5"
expect(g.description).to eq "Analyze the structure of texts, including how specific sentences, paragraphs," \
" and larger portions of the text (e.g., a section, chapter, scene, or stanza) relate to each other and" \
" the whole."
g = LearningOutcome.global.where(migration_id: "CEBC89F4-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.6"
expect(g.description).to eq "Assess how point of view or purpose shapes the content and style of a text."
g = LearningOutcome.global.where(migration_id: "CEBDDCA0-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.7"
expect(g.description).to eq "Integrate and evaluate content presented in diverse media and formats," \
" including visually and quantitatively, as well as in words."
g = LearningOutcome.global.where(migration_id: "CEBE4D52-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.8"
expect(g.description).to eq "Delineate and evaluate the argument and specific claims in a text," \
" including the validity of the reasoning as well as the relevance and sufficiency of the evidence."
g = LearningOutcome.global.where(migration_id: "CEBF37B2-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.9"
expect(g.description).to eq "Analyze how two or more texts address similar themes or topics in order" \
" to build knowledge or to compare the approaches the authors take."
g = LearningOutcome.global.where(migration_id: "CEC08B44-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.R.10"
expect(g.description).to eq "Read and comprehend complex literary and informational texts independently" \
" and proficiently."
g = LearningOutcome.global.where(migration_id: "CEC49A36-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.1"
expect(g.description).to eq "Write arguments to support claims in an analysis of substantive topics or" \
" texts, using valid reasoning and relevant and sufficient evidence."
g = LearningOutcome.global.where(migration_id: "CEC57CD0-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.2"
expect(g.description).to eq "Write informative/explanatory texts to examine and convey complex ideas and" \
" information clearly and accurately through the effective selection, organization, and analysis of content."
g = LearningOutcome.global.where(migration_id: "CEC665B4-67AD-11DF-AB5F-995D9DFF4B22").first
verify_rubric_criterion(g)
expect(g.short_description).to eq "CCSS.ELA-Literacy.CCRA.W.3"
expect(g.description).to eq "Write narratives to develop real or imagined experiences or events" \
" using effective technique, well-chosen details, and well-structured event sequences."
end
def check_for_parent_num_duplication(outcome)
parent = outcome.instance_variable_get('@parent')
if outcome.number && parent && parent.build_title && outcome.number.include?(parent.build_title)
outcome.title == "#{parent.title}.#{outcome.number}"
else
false
end
end
def check_built_outcome(outcome)
expect(check_for_parent_num_duplication(outcome)).to be_falsey
outcome.instance_variable_get('@children').each { |o| check_built_outcome(o) }
end
it "should successfully import the standards" do
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 0
expect(@cm.workflow_state).to eq 'imported'
verify_full_import()
end
it "should reject creating global outcomes if no permissions" do
@cm.user = nil
@cm.save!
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "User isn't allowed to edit global outcomes"
expect(@cm.workflow_state).to eq 'failed'
end
context "using the API" do
append_before do
@cm.attachment = nil
@cm.migration_settings[:no_archive_file] = true
@cm.migration_settings[:authorities] = ["CC"]
@cm.save!
end
it "should fail with no partner ID" do
@plugin.settings['api_key'] = nil
@plugin.settings[:partner_id] = nil
@plugin.settings[:partner_key] = "a"
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "A partner ID is required to use Academic Benchmarks"
expect(@cm.workflow_state).to eq 'failed'
end
it "should fail with an empty string partner ID" do
current_settings = @plugin.settings
new_settings = current_settings.merge('api_key' => nil, :partner_id => "", :partner_key => "a")
allow(@plugin).to receive(:settings).and_return(new_settings)
@cm.export_content
run_jobs
@cm.reload
expect(@cm.migration_issues.count).to eq 1
expect(@cm.migration_issues.first.description).to eq "A partner ID is required to use Academic Benchmarks"
expect(@cm.workflow_state).to eq 'failed'
end
end
# This test came about because the titles being generated for
# Florida outcomes were long and out of control. They were looking
# like this:
#
# LAFS.1.L.LAFS.1.L.1.LAFS.1.L.1.1.a
#
# instead of this:
#
# LAFS.1.L.1.1.a
#
it "doesn't duplicate the base numbers when building a title" do
json_data = JSON.parse(File.read(@florida_auth_list))
AcademicBenchmarks::Standards::StandardsForest.new(json_data).trees.each do |tree|
tree.children.each do |outcome|
check_built_outcome(outcome)
end
end
end
it "raises error with invalid user id" do
expect { AcademicBenchmark.ensure_real_user(user_id: 0) }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because no user found matching id '0'")
end
it "raises error when crendentials are not set" do
allow(AcademicBenchmark).to receive(:config).and_return({})
expect{ AcademicBenchmark.ensure_ab_credentials }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because the Academic Benchmarks Partner ID is not set")
allow(AcademicBenchmark).to receive(:config).and_return({partner_id: "user"})
expect{ AcademicBenchmark.ensure_ab_credentials }.to raise_error(Canvas::Migration::Error,
"Not importing academic benchmark data because the Academic Benchmarks Partner key is not set")
end
end

View File

@ -1,81 +0,0 @@
#
# Copyright (C) 2012 - 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 File.expand_path(File.dirname(__FILE__) + '/spec_helper')
describe AcademicBenchmark::Api do
before do
@api = AcademicBenchmark::Api.new("oioioi", :base_url => "http://example.com/")
@level_0_browse = File.join(File.dirname(__FILE__) + '/fixtures', 'example.json')
@authority_list = File.join(File.dirname(__FILE__) + '/fixtures', 'auth_list.json')
end
def mock_api_call(code, body, url)
response = double()
allow(response).to receive(:body).and_return(body)
allow(response).to receive(:code).and_return(code.to_s)
expect(AcademicBenchmark::Api).to receive(:get_url).with(url).and_return(response)
end
it "should fail with bad AB response" do
mock_api_call(200,
%{{"status":"fail","ab_err":{"msg":"API key access violation.","code":"401"}}},
"http://example.com/browse?api_key=oioioi&format=json&levels=2")
expect {
@api.list_available_authorities
}.to raise_error(AcademicBenchmark::APIError)
end
it "should fail with http error code" do
mock_api_call(500,
'',
"http://example.com/browse?api_key=oioioi&format=json&levels=2")
expect {
@api.list_available_authorities
}.to raise_error(AcademicBenchmark::APIError)
end
it "should get authority" do
mock_api_call(200,
'{"status":"ok", "itm":[{"test":"yep"}]}',
"http://example.com/browse?api_key=oioioi&authority=CC&format=json&levels=0")
expect(@api.browse_authority("CC", :levels => 0)).to eq [{"test" => "yep"}]
end
it "should get guid" do
mock_api_call(200,
'{"status":"ok", "itm":[{"test":"yep"}]}',
"http://example.com/browse?api_key=oioioi&format=json&guid=gggggg&levels=0")
expect(@api.browse_guid("gggggg", :levels => 0)).to eq [{"test" => "yep"}]
end
it "should get available authorities" do
mock_api_call(200,
File.read(@authority_list),
"http://example.com/browse?api_key=oioioi&format=json&levels=2")
expect(@api.list_available_authorities).to eq [{"chld" => "1", "guid" => "AAA", "title" => "NGA Center/CCSSO", "type" => "authority"},
{"chld" => "2", "guid" => "CCC", "title" => "South Carolina", "type" => "authority"},
{"chld" => "3", "guid" => "BBB", "title" => "Louisiana", "type" => "authority"},
{"chld" => "2", "guid" => "111", "title" => "Good Standards", "type" => "authority"},
{"chld" => "3", "guid" => "222", "title" => "Bad Standards", "type" => "authority"}]
end
end

View File

@ -1,46 +0,0 @@
{"status":"ok", "itm":[
{
"title":"United States",
"guid":"US",
"type":"country",
"itm":[
{
"title":"NGA Center/CCSSO",
"guid":"AAA",
"type":"authority",
"chld":"1"
},
{
"title":"South Carolina",
"guid":"CCC",
"type":"authority",
"chld":"2"
},
{
"title":"Louisiana",
"guid":"BBB",
"type":"authority",
"chld":"3"
}
]
},
{
"title":"Canvas Land",
"guid":"CL",
"type":"country",
"itm":[
{
"title":"Good Standards",
"guid":"111",
"type":"authority",
"chld":"2"
},
{
"title":"Bad Standards",
"guid":"222",
"type":"authority",
"chld":"3"
}
]
}
]}

View File

@ -1,158 +0,0 @@
{"status":"ok", "itm":[
{
"title":"United States",
"guid":"US",
"type":"country",
"itm":[
{
"title":"NGA Center/CCSSO",
"guid":"aaaaaaaaaa",
"type":"authority",
"itm":[
{
"title":"Common Core State Standards",
"guid":"bbbbbbbbbbbb",
"type":"document",
"itm":[
{
"title":"College- and Career-Readiness Standards and K-12 Mathematics",
"guid":"cccccccccc",
"type":"subject",
"meta":{
"content":"2010",
"name":"year"
},
"itm":[
{
"title":"Kindergarten",
"guid":"ddddddddd",
"type":"grade",
"meta":{
"hi":"K",
"lo":"K",
"name":"grade"
},
"itm":[
{
"guid":"eeeeeeeeeeee",
"type":"standard",
"meta":{
"num":{
"content":"K.CC"
},
"descr":{
"content":"Counting and Cardinality"
}
},
"itm":[
{
"guid":"ffffffffffffff",
"type":"standard",
"meta":{
"num":{},
"descr":{
"content":"Know number names and the count sequence."
}
},
"itm":[
{
"guid":"ggggggggggggggggg",
"type":"standard",
"meta":{
"num":{
"content":"1"
},
"descr":{
"content":"Count to 100 by ones and by tens."
}
}
},
{
"guid":"hhhhhhhhhhhhhhhh",
"type":"standard",
"meta":{
"num":{
"content":"2"
},
"descr":{
"content":"Count forward beginning from a given number within the known sequence (instead of having to begin at 1)."
}
}
},
{
"guid":"iiiiiiiiiiiiiiiii",
"type":"standard",
"meta":{
"num":{
"content":"3"
},
"descr":{
"content":"Write numbers from 0 to 20. Represent a number of objects with a written numeral 0-20 (with 0 representing a count of no objects)."
}
}
}
]
}
]
}
]
},
{
"title":"First Grade",
"guid":"jjjjjjjjjjj",
"type":"grade",
"meta":{
"hi":"1",
"lo":"1",
"name":"grade"
},
"itm":[
{
"guid":"kkkkkkkkkkk",
"type":"standard",
"meta":{
"num":{
"content":"1.DD"
},
"descr":{
"content":"zéééééééééééééééééééééééééééééééééééééééééééééééééééééééééé"
}
},
"itm":[
{
"guid":"lllllllll",
"type":"standard",
"meta":{
"num":{},
"descr":{
"content":"Something else"
}
},
"itm":[
{
"guid":"mmmmmmmmmmm",
"type":"standard",
"meta":{
"num":{
"content":"1"
},
"descr":{
"content":"And something else"
}
}
}
]
}
]
}
]
}
]
}
]
}
]
}
]
}
]}

View File

@ -1,368 +0,0 @@
[
{
"title": "Common Core State Standards",
"guid": "89c8ea94",
"type": "document",
"chld": "2"
},
{
"title": "NGSS Arranged by Topic",
"guid": "138c9cb7",
"type": "document",
"chld": "1"
},
{
"title": "Next Generation Science Standards NGSS Arranged by Disciplinary Core Idea (DCI)",
"guid": "923ce7bf",
"type": "document",
"chld": "1"
},
{
"title": "NETS for Administrators",
"guid": "923ce7ba",
"type": "document",
"chld": "1"
},
{
"title": "NETS for Teachers",
"guid": "923ce7bb",
"type": "document",
"chld": "1"
},
{
"title": "NETS for Students",
"guid": "923ce7bc",
"type": "document",
"chld": "1"
},
{
"title": "Alabama",
"guid": "46bb3879",
"type": "authority",
"chld": "3"
},
{
"title": "Alaska",
"guid": "21f67573",
"type": "authority",
"chld": "4"
},
{
"title": "Arizona",
"guid": "55fb05de",
"type": "authority",
"chld": "4"
},
{
"title": "Arkansas",
"guid": "9fadba9f",
"type": "authority",
"chld": "2"
},
{
"title": "Australia",
"guid": "24d23683",
"type": "authority",
"chld": "1"
},
{
"title": "California",
"guid": "51a90b1c",
"type": "authority",
"chld": "7"
},
{
"title": "Colorado",
"guid": "87740e39",
"type": "authority",
"chld": "3"
},
{
"title": "Connecticut",
"guid": "ebf5916a",
"type": "authority",
"chld": "5"
},
{
"title": "Delaware",
"guid": "6de6b316",
"type": "authority",
"chld": "5"
},
{
"title": "District of Columbia",
"guid": "7063136c",
"type": "authority",
"chld": "5"
},
{
"title": "Florida",
"guid": "33ed68e9",
"type": "authority",
"chld": "5"
},
{
"title": "Georgia",
"guid": "6eca1d5b",
"type": "authority",
"chld": "6"
},
{
"title": "Hawaii",
"guid": "c4e475d5",
"type": "authority",
"chld": "4"
},
{
"title": "Idaho",
"guid": "c4d27bbf",
"type": "authority",
"chld": "2"
},
{
"title": "Illinois",
"guid": "c52909fe",
"type": "authority",
"chld": "7"
},
{
"title": "Indiana",
"guid": "5405f7cd",
"type": "authority",
"chld": "3"
},
{
"title": "Iowa",
"guid": "a1408ccf",
"type": "authority",
"chld": "5"
},
{
"title": "Kansas",
"guid": "d9e92a00",
"type": "authority",
"chld": "3"
},
{
"title": "Kentucky",
"guid": "e935b719",
"type": "authority",
"chld": "4"
},
{
"title": "Louisiana",
"guid": "6b6ab39f",
"type": "authority",
"chld": "3"
},
{
"title": "Maine",
"guid": "7aa35836",
"type": "authority",
"chld": "3"
},
{
"title": "Maryland",
"guid": "ab7c9a74",
"type": "authority",
"chld": "8"
},
{
"title": "Massachusetts",
"guid": "e47b1fd7",
"type": "authority",
"chld": "4"
},
{
"title": "Michigan",
"guid": "f674f704",
"type": "authority",
"chld": "4"
},
{
"title": "Minnesota",
"guid": "49412237",
"type": "authority",
"chld": "4"
},
{
"title": "Mississippi",
"guid": "16e8ef83",
"type": "authority",
"chld": "3"
},
{
"title": "Missouri",
"guid": "7eb6d9c1",
"type": "authority",
"chld": "4"
},
{
"title": "Montana",
"guid": "f8b810b0",
"type": "authority",
"chld": "4"
},
{
"title": "National Standards",
"guid": "be4ae687",
"type": "authority",
"chld": "3"
},
{
"title": "Nebraska",
"guid": "1d80aef1",
"type": "authority",
"chld": "3"
},
{
"title": "Nevada",
"guid": "4ac3bf40",
"type": "authority",
"chld": "2"
},
{
"title": "New Hampshire",
"guid": "b879cab0",
"type": "authority",
"chld": "5"
},
{
"title": "New Jersey",
"guid": "28ccaa7f",
"type": "authority",
"chld": "4"
},
{
"title": "New Mexico",
"guid": "62d48dfb",
"type": "authority",
"chld": "4"
},
{
"title": "New South Wales",
"guid": "dc11940b",
"type": "authority",
"chld": "1"
},
{
"title": "New York",
"guid": "5ba1414e",
"type": "authority",
"chld": "5"
},
{
"title": "North Carolina",
"guid": "60d9162b",
"type": "authority",
"chld": "5"
},
{
"title": "North Dakota",
"guid": "3a254e65",
"type": "authority",
"chld": "4"
},
{
"title": "Ohio",
"guid": "3cad1282",
"type": "authority",
"chld": "4"
},
{
"title": "Oklahoma",
"guid": "75e44c01",
"type": "authority",
"chld": "4"
},
{
"title": "Oregon",
"guid": "b73ef744",
"type": "authority",
"chld": "4"
},
{
"title": "Pennsylvania",
"guid": "f049b761",
"type": "authority",
"chld": "6"
},
{
"title": "Rhode Island",
"guid": "182583c5",
"type": "authority",
"chld": "5"
},
{
"title": "South Carolina",
"guid": "dbe6080d",
"type": "authority",
"chld": "4"
},
{
"title": "South Dakota",
"guid": "e4279125",
"type": "authority",
"chld": "3"
},
{
"title": "Tennessee",
"guid": "02becaac",
"type": "authority",
"chld": "5"
},
{
"title": "Texas",
"guid": "ce3b417f",
"type": "authority",
"chld": "4"
},
{
"title": "Utah",
"guid": "33eb23e4",
"type": "authority",
"chld": "3"
},
{
"title": "Vermont",
"guid": "56164380",
"type": "authority",
"chld": "6"
},
{
"title": "Virginia",
"guid": "7ff76448",
"type": "authority",
"chld": "4"
},
{
"title": "Washington",
"guid": "fd5eab70",
"type": "authority",
"chld": "7"
},
{
"title": "West Virginia",
"guid": "7f9edddc",
"type": "authority",
"chld": "3"
},
{
"title": "Wisconsin",
"guid": "8b18ea97",
"type": "authority",
"chld": "4"
},
{
"title": "Wyoming",
"guid": "7121df7d",
"type": "authority",
"chld": "2"
},
{
"title": "United Kingdom",
"guid": "e13189f3",
"type": "country",
"chld": "1"
}
]

View File

@ -132,33 +132,20 @@ describe "Outcomes Import API", type: :request do
it "requires the AcademicBenchmark config to be set" do
stub_ab_config_with(nil)
# Since :partner_key is missing above, we default to using AB API v1.
# Once AB API v3 becomes default, switch the regex below to /needs partner_key and partner_id/
expect(request.call(type: request_type)["error"]).to match(/needs api_key and api_url/i)
expect(request.call(type: request_type)["error"]).to match(/needs partner_key and partner_id/i)
end
context "requires the AcademicBenchmark config api_key or partner_key to be set" do
# Since :partner_key is missing below, we default to using AB API v1.
# Once AB API v3 becomes default, switch the regex below to /needs partner_key/
context "requires the AcademicBenchmark config partner_key to be set" do
it "rejects a missing/nil key" do
stub_ab_config_with({ "api_url" => "http://a.real.url.com" })
expect(request.call(type: request_type)["error"]).to match(/needs api_key/i)
stub_ab_config_with({})
expect(request.call(type: request_type)["error"]).to match(/needs partner_key/i)
end
# Since :partner_key is empty below, we default to using AB API v1.
# Once AB API v3 becomes default, switch the regex below to /needs partner_key/
it "rejects a partner key that is the empty string" do
stub_ab_config_with({
partner_id: "instructure",
partner_key: ""
})
expect(request.call(type: request_type)["error"]).to match(/needs api_key/i)
end
it "rejects an api key that is the empty string" do
stub_ab_config_with({
"api_url" => "http://a.real.url.com",
"api_key" => ""
})
expect(request.call(type: request_type)["error"]).to match(/needs api_key/i)
expect(request.call(type: request_type)["error"]).to match(/needs partner_key/i)
end
end
@ -391,71 +378,39 @@ describe "Outcomes Import API", type: :request do
end
end
describe "v1" do
def stub_ab_import
cm_mock = double("content_migration")
allow(cm_mock).to receive(:id).and_return(3)
allow(AcademicBenchmark).to receive(:import).and_return(cm_mock)
def stub_ab_import
cm_mock = double("content_migration")
allow(cm_mock).to receive(:id).and_return(3)
allow(AcademicBenchmark).to receive(:import).and_return(cm_mock)
end
include_examples "outcomes import" do
let(:description_key){ "description" }
let(:json_file) { "available_return_val.json" }
def stub_ab_api
standards_mock = double("standards")
allow(standards_mock).to receive(:authorities).
and_return(filename_to_hash("available_authorities.json").
map{ |a| AcademicBenchmarks::Standards::Authority.from_hash(a) })
allow(standards_mock).to receive(:authority_documents).
with(not_eq('CC').and(not_eq('NRC'))).
and_return(filename_to_hash("national_standards_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(standards_mock).to receive(:authority_documents).
with('NRC').
and_return(filename_to_hash("ngss_nrc_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(standards_mock).to receive(:authority_documents).
with('CC').
and_return(filename_to_hash("common_core_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(AcademicBenchmarks::Api::Standards).to receive(:new).and_return(standards_mock)
end
include_examples "outcomes import" do
let(:description_key){ "title" }
let(:json_file) { "available_return_val_v1.json" }
def stub_ab_api
api_mock = double("api")
allow(api_mock).to receive(:list_available_authorities).
and_return(filename_to_hash("api_list_authorities.json"))
allow(api_mock).to receive(:browse_guid).
and_return(filename_to_hash("api_browse_guid.json"))
allow(api_mock).to receive(:browse).
and_return(filename_to_hash("api_browse.json"))
allow(AcademicBenchmark::Api).to receive(:new).and_return(api_mock)
end
def stub_ab_config
stub_ab_config_with({
"api_key" => "<secret-key>",
"api_url" => "http://api.statestandards.com/services/rest/"
})
end
def stub_ab_config
stub_ab_config_with({
partner_key: "<secret-key>",
partner_id: "instructure"
})
end
end
describe "v3" do
def stub_ab_import
cm_mock = double("content_migration")
allow(cm_mock).to receive(:id).and_return(3)
allow(AcademicBenchmark).to receive(:import).and_return(cm_mock)
end
include_examples "outcomes import" do
let(:description_key){ "description" }
let(:json_file) { "available_return_val.json" }
def stub_ab_api
standards_mock = double("standards")
allow(standards_mock).to receive(:authorities).
and_return(filename_to_hash("available_authorities.json").
map{ |a| AcademicBenchmarks::Standards::Authority.from_hash(a) })
allow(standards_mock).to receive(:authority_documents).
with(not_eq('CC').and not_eq('NRC')).
and_return(filename_to_hash("national_standards_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(standards_mock).to receive(:authority_documents).
with('NRC').
and_return(filename_to_hash("ngss_nrc_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(standards_mock).to receive(:authority_documents).
with('CC').
and_return(filename_to_hash("common_core_authority_docs.json").
map{ |d| AcademicBenchmarks::Standards::Document.from_hash(d) })
allow(AcademicBenchmarks::Api::Standards).to receive(:new).and_return(standards_mock)
end
def stub_ab_config
stub_ab_config_with({
partner_key: "<secret-key>",
partner_id: "instructure"
})
end
end
end
end

View File

@ -98,12 +98,8 @@ module OutcomeCommon
def state_outcome
state_outcome = [
'NGA Center/CCSSO',
'Common Core State Standards',
'College- and Career-Readiness Standards and K-12 Mathematics',
'First Grade',
'1.DD - zééééééééééééééééééééééééééééééééééééééééééééééééé',
'Something else'
'CCSS.ELA-Literacy.CCRA.R - Reading',
'Craft and Structure'
]
state_outcome
end

View File

@ -40,8 +40,7 @@ describe "account admin outcomes" do
@cm.user = @user
@cm.save!
@level_0_browse = File.join(File.dirname(__FILE__) + "/../../../gems/plugins/academic_benchmark/spec_canvas/fixtures", 'example.json')
@authority_list = File.join(File.dirname(__FILE__) + "/../../../gems/plugins/academic_benchmark/spec_canvas/fixtures", 'auth_list.json')
@level_0_browse = File.join(File.dirname(__FILE__) + "/../../../gems/plugins/academic_benchmark/spec_canvas/fixtures", 'api_all_standards_response.json')
File.open(@level_0_browse, 'r') do |file|
@att = Attachment.create!(:filename => 'standards.json', :display_name => 'standards.json', :uploaded_data => file, :context => @cm)
end
@ -59,7 +58,7 @@ describe "account admin outcomes" do
it "should have state standards available for outcomes through find", priority: "2", test_id: 250008 do
state_outcome_setup
goto_state_outcomes
expect(ffj(".outcome-level:last .outcome-group .ellipsis")[0]).to have_attribute("title", 'NGA Center/CCSSO')
expect(ffj(".outcome-level:last .outcome-group .ellipsis")[0]).to have_attribute("title", 'CCSS.ELA-Literacy.CCRA.R - Reading')
end
it "should import state standards to course groups and all nested outcomes", priority: "2", test_id: 56584 do
@ -67,22 +66,22 @@ describe "account admin outcomes" do
import_state_standards_to_account(state_outcome)
el1 = fj(".outcome-level:first .outcome-group .ellipsis")
el2 = fj(".outcome-level:last .outcome-link .ellipsis")
expect(el1).to have_attribute("title", 'Something else')
expect(el2).to have_attribute("title", '1.DD.1')
expect(el1).to have_attribute("title", 'Craft and Structure')
expect(el2).to have_attribute("title", 'CCSS.ELA-Literacy.CCRA.R.4')
end
it "should import a state standard into account level", priority: "2", test_id: 56017 do
skip_if_safari(:alert)
outcome = ['NGA Center/CCSSO']
outcome = ['CCSS.ELA-Literacy.CCRA.R - Reading']
import_state_standards_to_account(outcome)
el = fj('.outcome-level:first .outcome-group .ellipsis')
expect(el).to have_attribute("title", 'NGA Center/CCSSO')
expect(el).to have_attribute("title", 'CCSS.ELA-Literacy.CCRA.R - Reading')
end
it "should import account outcomes into course", priority: "1", test_id: 56585 do
skip_if_safari(:alert)
import_state_standards_to_account(state_outcome)
outcome = ['Default Account', 'Something else']
outcome = ['Default Account', 'Craft and Structure']
goto_state_outcomes("/courses/#{@course.id}/outcomes")
traverse_nested_outcomes(outcome)
import_account_level_outcomes
@ -91,7 +90,7 @@ describe "account admin outcomes" do
it "should delete state standards outcome groups from course listing", priority: "2", test_id: 250009 do
skip_if_safari(:alert)
import_state_standards_to_account(state_outcome)
f(".ellipsis[title='Something else']").click
f(".ellipsis[title='Craft and Structure']").click
wait_for_ajaximations
f('.delete_button').click