academic_benchmarks 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/academic_benchmarks.rb +13 -0
- data/lib/academic_benchmarks/api/auth.rb +46 -0
- data/lib/academic_benchmarks/api/constants.rb +43 -0
- data/lib/academic_benchmarks/api/handle.rb +92 -0
- data/lib/academic_benchmarks/api/standards.rb +128 -0
- data/lib/academic_benchmarks/lib/inst_vars_to_hash.rb +14 -0
- data/lib/academic_benchmarks/standards/authority.rb +23 -0
- data/lib/academic_benchmarks/standards/course.rb +22 -0
- data/lib/academic_benchmarks/standards/document.rb +20 -0
- data/lib/academic_benchmarks/standards/grade.rb +20 -0
- data/lib/academic_benchmarks/standards/has_relations.rb +21 -0
- data/lib/academic_benchmarks/standards/parent.rb +41 -0
- data/lib/academic_benchmarks/standards/standard.rb +96 -0
- data/lib/academic_benchmarks/standards/standards_forest.rb +97 -0
- data/lib/academic_benchmarks/standards/standards_tree.rb +67 -0
- data/lib/academic_benchmarks/standards/subject.rb +20 -0
- data/lib/academic_benchmarks/standards/subject_doc.rb +22 -0
- metadata +132 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c13cf6be09fd07cb0109d047e350f67b5d4cbc26
|
4
|
+
data.tar.gz: 64e6b5f20059e2d4a3cb9fa80e2fb646368eb8bd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3dd28dee3d000f68bedf3b0f096ad3373946fd908bb38ed0eec10d6900d8fc0851243df23eed7dca4354a4b4a698f5542d4eff0ce7158e58b3fb70840becb1d3
|
7
|
+
data.tar.gz: b9064ef02deddcef55face091a4212ce4e83a6b4f83189428ad5b9eb75b22d184404a43a17195ee463db3b7353701b98b07fe191dc7f1541ff2037b0cc10be48
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'active_support/core_ext/numeric/time'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Api
|
5
|
+
module Auth
|
6
|
+
def self.auth_query_params(partner_id:, partner_key:, expires:, user_id: "")
|
7
|
+
{
|
8
|
+
"partner.id" => partner_id,
|
9
|
+
"auth.signature" => signature_for(
|
10
|
+
partner_key: partner_key,
|
11
|
+
message: self.message(expires: expires, user_id: user_id)),
|
12
|
+
"auth.expires" => expires,
|
13
|
+
"user.id" => user_id
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.signature_for(partner_key:, message:)
|
18
|
+
Base64.encode64(OpenSSL::HMAC.digest(
|
19
|
+
OpenSSL::Digest.new('sha256'),
|
20
|
+
partner_key,
|
21
|
+
message
|
22
|
+
)).chomp
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.message(expires:, user_id: '')
|
26
|
+
if user_id.empty?
|
27
|
+
"#{expires}"
|
28
|
+
else
|
29
|
+
"#{expires}\n#{user_id}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.expire_time_in_10_seconds
|
34
|
+
self.expire_time_in(10.seconds)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.expire_time_in_2_hours
|
38
|
+
self.expire_time_in(2.hours)
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.expire_time_in(offset)
|
42
|
+
Time.now.to_i + offset
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AcademicBenchmarks
|
2
|
+
module Api
|
3
|
+
module Constants
|
4
|
+
def self.base_url
|
5
|
+
'https://api.academicbenchmarks.com/rest/v3'
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.api_version
|
9
|
+
'3'
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.partner_id_env_var
|
13
|
+
'ACADEMIC_BENCHMARKS_PARTNER_ID'
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.partner_key_env_var
|
17
|
+
'ACADEMIC_BENCHMARKS_PARTNER_KEY'
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.user_id_env_var
|
21
|
+
'ACADEMIC_BENCHMARKS_USER_ID'
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.standards_search_params
|
25
|
+
%w[
|
26
|
+
query
|
27
|
+
authority
|
28
|
+
subject
|
29
|
+
grade
|
30
|
+
subject_doc
|
31
|
+
course
|
32
|
+
document
|
33
|
+
parent
|
34
|
+
deepest
|
35
|
+
limit
|
36
|
+
offset
|
37
|
+
list
|
38
|
+
fields
|
39
|
+
]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
require_relative 'constants'
|
2
|
+
require_relative 'standards'
|
3
|
+
|
4
|
+
module AcademicBenchmarks
|
5
|
+
module Api
|
6
|
+
class Handle
|
7
|
+
include HTTParty
|
8
|
+
|
9
|
+
attr_accessor :partner_id, :partner_key
|
10
|
+
|
11
|
+
attr_reader :user_id # user_id writer is defined below
|
12
|
+
|
13
|
+
base_uri AcademicBenchmarks::Api::Constants.base_url
|
14
|
+
|
15
|
+
# Allows the user to initialize from environment variables
|
16
|
+
def self.init_from_env
|
17
|
+
partner_id = partner_id_from_env
|
18
|
+
partner_key = partner_key_from_env
|
19
|
+
|
20
|
+
if !partner_id.present? || !partner_key.present?
|
21
|
+
pidstr = !partner_id.present? ?
|
22
|
+
AcademicBenchmarks::Api::Constants.partner_id_env_var : ""
|
23
|
+
pkystr = !partner_key.present? ?
|
24
|
+
AcademicBenchmarks::Api::Constants.partner_key_env_var : ""
|
25
|
+
raise StandardError.new(
|
26
|
+
"Missing environment variable(s): #{[pidstr, pkystr].join(', ')}"
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
new(
|
31
|
+
partner_id: partner_id,
|
32
|
+
partner_key: partner_key,
|
33
|
+
user_id: user_id_from_env
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(partner_id:, partner_key:, user_id: "")
|
38
|
+
@partner_id = partner_id
|
39
|
+
@partner_key = partner_key
|
40
|
+
@user_id = user_id.to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def user_id=(user_id)
|
44
|
+
@user_id = user_id.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def related(guid:, fields: [])
|
48
|
+
raise StandardError.new("Sorry, not implemented yet!")
|
49
|
+
end
|
50
|
+
|
51
|
+
def standards
|
52
|
+
Standards.new(self)
|
53
|
+
end
|
54
|
+
|
55
|
+
def assets
|
56
|
+
raise StandardError.new("Sorry, not implemented yet!")
|
57
|
+
end
|
58
|
+
|
59
|
+
def alignments
|
60
|
+
raise StandardError.new("Sorry, not implemented yet!")
|
61
|
+
end
|
62
|
+
|
63
|
+
def topics
|
64
|
+
raise StandardError.new("Sorry, not implemented yet!")
|
65
|
+
end
|
66
|
+
|
67
|
+
def special
|
68
|
+
raise StandardError.new("Sorry, not implemented yet!")
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def api_resp_to_array_of_standards(api_resp)
|
74
|
+
api_resp.parsed_response["resources"].inject([]) do |retval, resource|
|
75
|
+
retval.push(AcademicBenchmarks::Standards::Standard.new(resource["data"]))
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.partner_id_from_env
|
80
|
+
ENV[AcademicBenchmarks::Api::Constants.partner_id_env_var]
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.partner_key_from_env
|
84
|
+
ENV[AcademicBenchmarks::Api::Constants.partner_key_env_var]
|
85
|
+
end
|
86
|
+
|
87
|
+
def self.user_id_from_env
|
88
|
+
ENV[AcademicBenchmarks::Api::Constants.user_id_env_var]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'active_support/hash_with_indifferent_access'
|
2
|
+
|
3
|
+
require_relative 'auth'
|
4
|
+
require_relative 'constants'
|
5
|
+
|
6
|
+
module AcademicBenchmarks
|
7
|
+
module Api
|
8
|
+
class Standards
|
9
|
+
DEFAULT_PER_PAGE = 100
|
10
|
+
|
11
|
+
def initialize(handle)
|
12
|
+
@handle = handle
|
13
|
+
end
|
14
|
+
|
15
|
+
def search(opts = {})
|
16
|
+
# query: "", authority: "", subject: "", grade: "", subject_doc: "", course: "",
|
17
|
+
# document: "", parent: "", deepest: "", limit: -1, offset: -1, list: "", fields: []
|
18
|
+
invalid_params = invalid_search_params(opts)
|
19
|
+
if invalid_params.empty?
|
20
|
+
raw_search(opts).map do |standard|
|
21
|
+
AcademicBenchmarks::Standards::Standard.new(standard)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
raise ArgumentError.new(
|
25
|
+
"Invalid search params: #{invalid_params.join(', ')}"
|
26
|
+
)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
alias_method :where, :search
|
31
|
+
|
32
|
+
def guid(guid, fields: [])
|
33
|
+
query_params = if fields.empty?
|
34
|
+
auth_query_params
|
35
|
+
else
|
36
|
+
auth_query_params.merge({
|
37
|
+
fields: fields.join(",")
|
38
|
+
})
|
39
|
+
end
|
40
|
+
@handle.class.get(
|
41
|
+
"/standards/#{guid}",
|
42
|
+
query: query_params
|
43
|
+
).parsed_response["resources"].map do |r|
|
44
|
+
AcademicBenchmarks::Standards::Standard.new(r["data"])
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def all
|
49
|
+
request_search_pages_and_concat_resources(auth_query_params)
|
50
|
+
end
|
51
|
+
|
52
|
+
def authorities
|
53
|
+
raw_search(list: "authority").map{|a| a["data"]["authority"]}.map{|a| AcademicBenchmarks::Standards::Authority.from_hash(a)}
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def raw_search(opts = {})
|
59
|
+
request_search_pages_and_concat_resources(opts.merge(auth_query_params))
|
60
|
+
end
|
61
|
+
|
62
|
+
def invalid_search_params(opts)
|
63
|
+
opts.keys.map(&:to_s) - AcademicBenchmarks::Api::Constants.standards_search_params
|
64
|
+
end
|
65
|
+
|
66
|
+
def auth_query_params
|
67
|
+
AcademicBenchmarks::Api::Auth.auth_query_params(
|
68
|
+
partner_id: @handle.partner_id,
|
69
|
+
partner_key: @handle.partner_key,
|
70
|
+
expires: AcademicBenchmarks::Api::Auth.expire_time_in_2_hours,
|
71
|
+
user_id: @handle.user_id
|
72
|
+
)
|
73
|
+
end
|
74
|
+
|
75
|
+
def request_search_pages_and_concat_resources(query_params)
|
76
|
+
query_params.reverse_merge!({limit: DEFAULT_PER_PAGE})
|
77
|
+
|
78
|
+
if !query_params[:limit] || query_params[:limit] <= 0
|
79
|
+
raise ArgumentError.new(
|
80
|
+
"limit must be specified as a positive integer"
|
81
|
+
)
|
82
|
+
end
|
83
|
+
|
84
|
+
first_page = request_page(
|
85
|
+
query_params: query_params,
|
86
|
+
limit: query_params[:limit],
|
87
|
+
offset: 0
|
88
|
+
).parsed_response
|
89
|
+
|
90
|
+
resources = first_page["resources"]
|
91
|
+
count = first_page["count"]
|
92
|
+
offset = query_params[:limit]
|
93
|
+
|
94
|
+
while offset < count
|
95
|
+
page = request_page(
|
96
|
+
query_params: query_params,
|
97
|
+
limit: query_params[:limit],
|
98
|
+
offset: offset
|
99
|
+
)
|
100
|
+
offset += query_params[:limit]
|
101
|
+
resources.push(page.parsed_response["resources"])
|
102
|
+
end
|
103
|
+
|
104
|
+
resources.flatten
|
105
|
+
end
|
106
|
+
|
107
|
+
def request_page(query_params:, limit:, offset:)
|
108
|
+
query_params.merge!({
|
109
|
+
limit: limit,
|
110
|
+
offset: offset,
|
111
|
+
})
|
112
|
+
resp = @handle.class.get(
|
113
|
+
'/standards',
|
114
|
+
query: query_params.merge({
|
115
|
+
limit: limit,
|
116
|
+
offset: offset,
|
117
|
+
})
|
118
|
+
)
|
119
|
+
if resp.code != 200
|
120
|
+
raise RuntimeError.new(
|
121
|
+
"Received response '#{resp.code}: #{resp.message}' requesting standards from Academic Benchmarks:"
|
122
|
+
)
|
123
|
+
end
|
124
|
+
resp
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Authority
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :code, :guid, :description
|
9
|
+
|
10
|
+
alias_method :descr, :description
|
11
|
+
|
12
|
+
def self.from_hash(hash)
|
13
|
+
self.new(code: hash["code"], guid: hash["guid"], description: hash["descr"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(code:, guid:, description:)
|
17
|
+
@code = code
|
18
|
+
@guid = guid
|
19
|
+
@description = description
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Course
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :guid, :description
|
9
|
+
|
10
|
+
alias_method :descr, :description
|
11
|
+
|
12
|
+
def self.from_hash(hash)
|
13
|
+
self.new(description: hash["descr"], guid: hash["guid"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(guid:, description:)
|
17
|
+
@guid = guid
|
18
|
+
@description = description
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Document
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :title, :guid
|
9
|
+
|
10
|
+
def self.from_hash(hash)
|
11
|
+
self.new(title: hash["title"], guid: hash["guid"])
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(title:, guid:)
|
15
|
+
@title = title
|
16
|
+
@guid = guid
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Grade
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :high, :low
|
9
|
+
|
10
|
+
def self.from_hash(hash)
|
11
|
+
self.new(high: hash["high"], low: hash["low"])
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(high:, low:)
|
15
|
+
@high = high
|
16
|
+
@low = low
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class HasRelations
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :origin, :derivative, :related_derivative
|
9
|
+
|
10
|
+
def self.from_hash(hash)
|
11
|
+
self.new(derivative: hash["derivative"], origin: hash["origin"], related_derivative: hash["related_derivative"])
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(origin: 0, derivative: 0, related_derivative: 0)
|
15
|
+
@origin = origin
|
16
|
+
@derivative = derivative
|
17
|
+
@related_derivative = related_derivative
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Parent
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :guid, :description, :number, :stem, :label, :deepest,
|
9
|
+
:seq, :level, :status, :version
|
10
|
+
|
11
|
+
def self.from_hash(hash)
|
12
|
+
self.new(
|
13
|
+
guid: hash["guid"],
|
14
|
+
description: hash["description"],
|
15
|
+
number: hash["number"],
|
16
|
+
stem: hash["stem"],
|
17
|
+
label: hash["label"],
|
18
|
+
deepest: hash["deepest"],
|
19
|
+
seq: hash["seq"],
|
20
|
+
level: hash["level"],
|
21
|
+
status: hash["status"],
|
22
|
+
version: hash["version"]
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(guid:, description:, number:, stem:, label:, deepest:,
|
27
|
+
seq:, level:, status:, version:)
|
28
|
+
@guid = guid
|
29
|
+
@description = description
|
30
|
+
@number = number
|
31
|
+
@stem = stem
|
32
|
+
@label = label
|
33
|
+
@deepest = deepest
|
34
|
+
@seq = seq
|
35
|
+
@level = level
|
36
|
+
@status = status
|
37
|
+
@version = version
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Standard
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_reader :status, :deepest, :children
|
9
|
+
attr_accessor :guid, :description, :number, :stem, :label, :level,
|
10
|
+
:version, :seq, :adopt_year, :authority, :course,
|
11
|
+
:document, :grade, :has_relations, :subject,
|
12
|
+
:subject_doc, :parent, :parent_guid
|
13
|
+
|
14
|
+
alias_method :descr, :description
|
15
|
+
|
16
|
+
def initialize(data)
|
17
|
+
data = data["data"] if data["data"]
|
18
|
+
@guid = data["guid"]
|
19
|
+
@grade = attr_to_val_or_nil(Grade, data, "grade")
|
20
|
+
@label = data["label"]
|
21
|
+
@level = data["level"]
|
22
|
+
@course = attr_to_val_or_nil(Course, data, "course")
|
23
|
+
@number = data["number"]
|
24
|
+
@status = data["status"]
|
25
|
+
@parent = nil
|
26
|
+
@subject = attr_to_val_or_nil(Subject, data, "subject")
|
27
|
+
@deepest = data["deepest"]
|
28
|
+
@version = data["version"]
|
29
|
+
@children = []
|
30
|
+
@document = attr_to_val_or_nil(Document, data, "document")
|
31
|
+
@authority = attr_to_val_or_nil(Authority, data, "authority")
|
32
|
+
@adopt_year = data["adopt_year"]
|
33
|
+
@description = data["descr"]
|
34
|
+
@subject_doc = attr_to_val_or_nil(SubjectDoc, data, "subject_doc")
|
35
|
+
@has_relations = attr_to_val_or_nil(HasRelations, data, "has_relations")
|
36
|
+
|
37
|
+
# Parent guid extraction can be a little more complicated. Thanks AB!
|
38
|
+
if data["parent"] && data["parent"].is_a?(String)
|
39
|
+
@parent_guid = data["parent"]
|
40
|
+
elsif data["parent"] && data["parent"].is_a?(Hash)
|
41
|
+
@parent_guid = data["parent"]["guid"]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def active?
|
46
|
+
status == "Active"
|
47
|
+
end
|
48
|
+
|
49
|
+
def obsolete?
|
50
|
+
status == "Obsolete"
|
51
|
+
end
|
52
|
+
|
53
|
+
def deepest?
|
54
|
+
deepest == 'Y'
|
55
|
+
end
|
56
|
+
|
57
|
+
def status=(status)
|
58
|
+
unless %w[Active Obsolete].include?(status)
|
59
|
+
raise ArgumentError.new(
|
60
|
+
"Standard status must be either 'Active' or 'Obsolete'"
|
61
|
+
)
|
62
|
+
end
|
63
|
+
@status = status
|
64
|
+
end
|
65
|
+
|
66
|
+
def deepest=(deepest)
|
67
|
+
unless %w[Y N].include?(deepest)
|
68
|
+
raise ArgumentError.new("Standard deepest must be either 'Y' or 'N'")
|
69
|
+
end
|
70
|
+
@deepest = deepest
|
71
|
+
end
|
72
|
+
|
73
|
+
def add_child(child)
|
74
|
+
unless child.is_a?(Standard)
|
75
|
+
raise ArgumentError.new("Tried to set child that isn't a Standard")
|
76
|
+
end
|
77
|
+
@children.push(child)
|
78
|
+
end
|
79
|
+
|
80
|
+
def remove_child(child)
|
81
|
+
@children.delete(child)
|
82
|
+
end
|
83
|
+
|
84
|
+
def has_children?
|
85
|
+
@children.count > 0
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
def attr_to_val_or_nil(klass, hash, attr)
|
91
|
+
return nil unless hash.has_key?(attr)
|
92
|
+
klass.from_hash(hash)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module AcademicBenchmarks
|
2
|
+
module Standards
|
3
|
+
class StandardsForest
|
4
|
+
attr_reader :trees, :data_hash
|
5
|
+
|
6
|
+
# The guid to standard hash can optionally be saved to permit speedily
|
7
|
+
# adding standards to the tree (since the tree is unordered,
|
8
|
+
# this would otherwise be an expensive operation).
|
9
|
+
#
|
10
|
+
# The initial data hash can also be optionally saved to
|
11
|
+
# permit testing and internal consistency checks
|
12
|
+
|
13
|
+
def initialize(
|
14
|
+
data_hash,
|
15
|
+
save_guid_to_standard_hash: true,
|
16
|
+
save_initial_data_hash: false
|
17
|
+
)
|
18
|
+
@data_hash = data_hash.dup.freeze if save_initial_data_hash
|
19
|
+
@guid_to_standard = {} # a hash of guids to standards
|
20
|
+
@trees = []
|
21
|
+
process_items(data_hash)
|
22
|
+
|
23
|
+
# upgrade the hash data to a StandardsTree object
|
24
|
+
@trees.map! do |item|
|
25
|
+
StandardsTree.new(item, build_item_hash: save_guid_to_standard_hash)
|
26
|
+
end
|
27
|
+
|
28
|
+
unless save_guid_to_standard_hash
|
29
|
+
remove_instance_variable('@guid_to_standard')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def add_standard(standard)
|
34
|
+
if standard.is_a?(Standard)
|
35
|
+
raise StandardError.new(
|
36
|
+
"adding standards is not currently implemented"
|
37
|
+
)
|
38
|
+
elsif standard.is_a?(Hash)
|
39
|
+
add_standard(Standard.new(standard))
|
40
|
+
else
|
41
|
+
raise ArgumentError.new(
|
42
|
+
"standard must be an 'AcademicBenchmarks::Standards::Standard' " \
|
43
|
+
"or a 'Hash' but was a #{standard.class.to_s}"
|
44
|
+
)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def single_tree?
|
49
|
+
@trees.count == 1
|
50
|
+
end
|
51
|
+
|
52
|
+
def empty?
|
53
|
+
@trees.empty?
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def to_standard(item)
|
59
|
+
return item if item.is_a?(Standard)
|
60
|
+
Standard.new(item)
|
61
|
+
end
|
62
|
+
|
63
|
+
def process_items(data_hash)
|
64
|
+
build_guid_to_standard_hash(data_hash)
|
65
|
+
link_parent_and_children
|
66
|
+
end
|
67
|
+
|
68
|
+
def build_guid_to_standard_hash(data_hash)
|
69
|
+
data_hash.each do |item|
|
70
|
+
item = to_standard(item)
|
71
|
+
@guid_to_standard[item.guid] = item
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def link_parent_and_children
|
76
|
+
@guid_to_standard.values.each do |child|
|
77
|
+
if child.parent_guid
|
78
|
+
present_in_hash_or_raise(child.parent_guid)
|
79
|
+
parent = @guid_to_standard[child.parent_guid]
|
80
|
+
parent.add_child(child)
|
81
|
+
child.parent = parent
|
82
|
+
else
|
83
|
+
@trees.push(child)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def present_in_hash_or_raise(guid)
|
89
|
+
unless @guid_to_standard.has_key?(guid)
|
90
|
+
raise StandardError.new(
|
91
|
+
"item missing from guid_to_standard hash"
|
92
|
+
)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module AcademicBenchmarks
|
2
|
+
module Standards
|
3
|
+
class StandardsTree
|
4
|
+
attr_reader :root_standard
|
5
|
+
|
6
|
+
# The item hash can optionally be built to permit the speedy
|
7
|
+
# addition of standards to the tree. since the tree is unordered,
|
8
|
+
# adding to it can be expensive without this
|
9
|
+
|
10
|
+
def initialize(root_standard, build_item_hash: true)
|
11
|
+
@root_standard = root_standard
|
12
|
+
if build_item_hash
|
13
|
+
@item_hash = {}
|
14
|
+
go_ahead_and_build_item_hash
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_standard(standard)
|
19
|
+
if standard.is_a?(Standard)
|
20
|
+
parent = @item_hash ? @item_hash[standard.parent_guid] : find_parent(standard)
|
21
|
+
unless parent
|
22
|
+
raise StandardError.new(
|
23
|
+
"Parent of standard not found in tree. Parent guid is " \
|
24
|
+
"'#{standard.parent_guid}' and child guid is '#{standard.guid}'"
|
25
|
+
)
|
26
|
+
end
|
27
|
+
parent.add_child(standard)
|
28
|
+
standard.parent = parent
|
29
|
+
elsif standard.is_a?(Hash)
|
30
|
+
add_standard(Standard.new(standard))
|
31
|
+
else
|
32
|
+
raise ArgumentError.new(
|
33
|
+
"standard must be an 'AcademicBenchmarks::Standards::Standard' " \
|
34
|
+
"or a 'Hash' but was a #{standard.class.to_s}"
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
def go_ahead_and_build_item_hash
|
42
|
+
@item_hash[@root_standard.guid] = @root_standard
|
43
|
+
add_children_to_item_hash(@root_standard)
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_children_to_item_hash(parent)
|
47
|
+
parent.children.each do |child|
|
48
|
+
@item_hash[child.guid] = child
|
49
|
+
add_children_to_item_hash(child) if child.has_children?
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def find_parent(standard)
|
54
|
+
return @root_standard if @root_standard.guid == standard.parent_guid
|
55
|
+
check_children_for_parent(standard.parent_guid, @root_standard)
|
56
|
+
end
|
57
|
+
|
58
|
+
# does a depth-first search
|
59
|
+
def check_children_for_parent(parent_guid, standard)
|
60
|
+
standard.children.each do |child|
|
61
|
+
return child if child.guid == parent_guid
|
62
|
+
check_children_for_parent(parent_guid, child) if child.has_children?
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class Subject
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :code, :broad
|
9
|
+
|
10
|
+
def self.from_hash(hash)
|
11
|
+
self.new(code: hash["code"], broad: hash["broad"], )
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(code:, broad:)
|
15
|
+
@code = code
|
16
|
+
@broad = broad
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require_relative '../lib/inst_vars_to_hash'
|
2
|
+
|
3
|
+
module AcademicBenchmarks
|
4
|
+
module Standards
|
5
|
+
class SubjectDoc
|
6
|
+
include InstVarsToHash
|
7
|
+
|
8
|
+
attr_accessor :guid, :description
|
9
|
+
|
10
|
+
alias_method :descr, :description
|
11
|
+
|
12
|
+
def self.from_hash(hash)
|
13
|
+
self.new(guid: hash["guid"], description: hash["descr"])
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(guid:, description:)
|
17
|
+
@guid = guid
|
18
|
+
@description = description
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: academic_benchmarks
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Benjamin Porter
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-11-18 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.13'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.13'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: activesupport
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '4.2'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '4.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: byebug
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '3.1'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '3.1'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: awesome_print
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.6'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.6'
|
83
|
+
description: A ruby api for accessing the Academic Benchmarks API. A valid subscription
|
84
|
+
with accompanying credentials will be required to access the API
|
85
|
+
email: bporter@instructure.com
|
86
|
+
executables: []
|
87
|
+
extensions: []
|
88
|
+
extra_rdoc_files: []
|
89
|
+
files:
|
90
|
+
- lib/academic_benchmarks.rb
|
91
|
+
- lib/academic_benchmarks/api/auth.rb
|
92
|
+
- lib/academic_benchmarks/api/constants.rb
|
93
|
+
- lib/academic_benchmarks/api/handle.rb
|
94
|
+
- lib/academic_benchmarks/api/standards.rb
|
95
|
+
- lib/academic_benchmarks/lib/inst_vars_to_hash.rb
|
96
|
+
- lib/academic_benchmarks/standards/authority.rb
|
97
|
+
- lib/academic_benchmarks/standards/course.rb
|
98
|
+
- lib/academic_benchmarks/standards/document.rb
|
99
|
+
- lib/academic_benchmarks/standards/grade.rb
|
100
|
+
- lib/academic_benchmarks/standards/has_relations.rb
|
101
|
+
- lib/academic_benchmarks/standards/parent.rb
|
102
|
+
- lib/academic_benchmarks/standards/standard.rb
|
103
|
+
- lib/academic_benchmarks/standards/standards_forest.rb
|
104
|
+
- lib/academic_benchmarks/standards/standards_tree.rb
|
105
|
+
- lib/academic_benchmarks/standards/subject.rb
|
106
|
+
- lib/academic_benchmarks/standards/subject_doc.rb
|
107
|
+
homepage: https://github.com/instructure/academic_benchmarks
|
108
|
+
licenses:
|
109
|
+
- GPL
|
110
|
+
metadata: {}
|
111
|
+
post_install_message:
|
112
|
+
rdoc_options: []
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
requirements:
|
117
|
+
- - ">="
|
118
|
+
- !ruby/object:Gem::Version
|
119
|
+
version: '0'
|
120
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ">="
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
requirements: []
|
126
|
+
rubyforge_project:
|
127
|
+
rubygems_version: 2.2.3
|
128
|
+
signing_key:
|
129
|
+
specification_version: 4
|
130
|
+
summary: A ruby api for accessing the Academic Benchmarks API
|
131
|
+
test_files: []
|
132
|
+
has_rdoc:
|