academic_benchmarks 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5d61a80eca5cb7536525e3359da6693d928d24d4
4
- data.tar.gz: 254a3dc74b516c970eb2ec49fb160ef6f285ebbf
3
+ metadata.gz: 7541ca8bbc79a966e924b5de4bd0cfd407d154c3
4
+ data.tar.gz: 00c65e4ec6204e68cff41b59db745b5e70982194
5
5
  SHA512:
6
- metadata.gz: 69667639168d5e6fb7c90f6fe927a0fedfe21675820432182b0f88776b453ee530fd477f6ae0b35aaa8bd85e8780c0a794e86628ba08a98619a4be5146dbcf2d
7
- data.tar.gz: 09e2bacc1aae34f23180c0f0e313e2c49e918f61cbbe4fdc2ce01e65adf8e4b9fd262840fc9a83869fa87360e4d0b861824662cbc62520c0200340ef002c191a
6
+ metadata.gz: 57c6ccc816f6552ae6b5fd5d755ab2bd912660ea73d16c6dc7efaeea89e6c490a1580d0853261735e6f18f5abf3688d8b0b5e05c0b63c5d2d9854cef44213cc9
7
+ data.tar.gz: 7cb00c099914d545e8011c3a93afd270d1b65440f2c977ac4252e8edfa2fbf08c2865d10a1ef84ecb88052254d94e4e7ff6c77ba9834a85d1a29acf7ceef200f
@@ -49,51 +49,86 @@ module AcademicBenchmarks
49
49
  request_search_pages_and_concat_resources(auth_query_params)
50
50
  end
51
51
 
52
- def authorities
53
- raw_search(list: "authority").map do |a|
52
+ def authorities(query_params = {})
53
+ raw_search({list: "authority"}.merge(query_params)).map do |a|
54
54
  AcademicBenchmarks::Standards::Authority.from_hash(a["data"]["authority"])
55
55
  end
56
56
  end
57
57
 
58
- def documents
59
- raw_search(list: "document").map do |a|
58
+ def documents(query_params = {})
59
+ raw_search({list: "document"}.merge(query_params)).map do |a|
60
60
  AcademicBenchmarks::Standards::Document.from_hash(a["data"]["document"])
61
61
  end
62
62
  end
63
63
 
64
- def authority_tree(authority_or_authority_code_guid_or_desc)
65
- authority = if authority_or_authority_code_guid_or_desc.is_a?(Authority)
66
- authority_or_authority_code_guid_or_desc
67
- else
68
- find_authority(authority_or_authority_code_guid_or_desc)
69
- end
64
+ def authority_documents(authority_or_auth_code_guid_or_desc)
65
+ authority = auth_from_code_guid_or_desc(authority_or_auth_code_guid_or_desc)
66
+ documents(authority: authority.code)
67
+ end
68
+
69
+ def authority_tree(authority_or_auth_code_guid_or_desc, include_obsolete_standards: true)
70
+ authority = auth_from_code_guid_or_desc(authority_or_auth_code_guid_or_desc)
70
71
  auth_children = search(authority: authority.code)
71
- StandardsForest.new(auth_children).consolidate_under_root(authority)
72
+ AcademicBenchmarks::Standards::StandardsForest.new(
73
+ auth_children,
74
+ include_obsoletes: include_obsolete_standards
75
+ ).consolidate_under_root(authority)
76
+ end
77
+
78
+ def document_tree(document_or_guid, include_obsolete_standards: true)
79
+ document = doc_from_guid(document_or_guid)
80
+ doc_children = search(document: document.guid)
81
+ AcademicBenchmarks::Standards::StandardsForest.new(
82
+ doc_children,
83
+ include_obsoletes: include_obsolete_standards
84
+ ).consolidate_under_root(document)
72
85
  end
73
86
 
74
87
  private
75
88
 
76
- def find_authority(authority_code_guid_or_desc)
77
- auths = match_authority(authority_code_guid_or_desc)
78
- if auths.empty?
79
- raise StandardError,
80
- "No authority code, guid, or description matched '#{authority_code_guid_or_desc}'"
81
- elsif auths.count > 1
82
- raise StandardError,
89
+ def doc_from_guid(document_or_guid)
90
+ if document_or_guid.is_a?(AcademicBenchmarks::Standards::Document)
91
+ document_or_guid
92
+ else
93
+ find_type(type: "document", data: document_or_guid)
94
+ end
95
+ end
96
+
97
+ def auth_from_code_guid_or_desc(authority_or_auth_code_guid_or_desc)
98
+ if authority_or_auth_code_guid_or_desc.is_a?(AcademicBenchmarks::Standards::Authority)
99
+ authority_or_auth_code_guid_or_desc
100
+ else
101
+ find_type(type: "authority", data: authority_or_auth_code_guid_or_desc)
102
+ end
103
+ end
104
+
105
+ def find_type(type:, data:)
106
+ matches = send("match_#{type}", data)
107
+ if matches.empty?
108
+ raise StandardError.new(
109
+ "No #{type} code, guid, or description matched '#{data}'"
110
+ )
111
+ elsif matches.count > 1
112
+ raise StandardError.new(
83
113
  "Authority code, guid, or description matched more than one authority. " \
84
- "matched '#{auths.map(&:to_json).join('; ')}'"
114
+ "matched '#{matches.map(&:to_json).join('; ')}'"
115
+ )
85
116
  end
86
- auths.first
117
+ matches.first
87
118
  end
88
119
 
89
- def match_authority(authority_code_guid_or_desc)
120
+ def match_authority(data)
90
121
  authorities.select do |auth|
91
- auth.code == authority_code_guid_or_desc ||
92
- auth.guid == authority_code_guid_or_desc ||
93
- auth.descr == authority_code_guid_or_desc
122
+ auth.code == data ||
123
+ auth.guid == data ||
124
+ auth.descr == data
94
125
  end
95
126
  end
96
127
 
128
+ def match_document(data)
129
+ documents.select { |doc| doc.guid == data }
130
+ end
131
+
97
132
  def raw_search(opts = {})
98
133
  request_search_pages_and_concat_resources(opts.merge(auth_query_params))
99
134
  end
@@ -1,4 +1,3 @@
1
-
2
1
  #
3
2
  # This module will allow you to properly to_s, to_h, and to_json
4
3
  # on classes in which it is included.
@@ -10,22 +9,28 @@
10
9
  #
11
10
 
12
11
  module InstVarsToHash
13
- def to_s(omit_parent: true)
14
- to_h(omit_parent: omit_parent).to_s
12
+ def to_s(omit_parent: true, omit_empty_children: true)
13
+ to_h(
14
+ omit_parent: omit_parent,
15
+ omit_empty_children: omit_empty_children
16
+ ).to_s
15
17
  end
16
18
 
17
- def to_h(omit_parent: true)
19
+ def to_h(omit_parent: true, omit_empty_children: true)
18
20
  retval = {}
19
21
  instance_variables.each do |iv|
20
- unless omit_parent && (iv =~ /^@?parent$/i)
22
+ if !(skip_parent?(omit_parent, iv) || skip_children?(omit_empty_children, iv))
21
23
  retval[iv.to_s.delete('@').to_sym] = elem_to_h(instance_variable_get(iv))
22
24
  end
23
25
  end
24
26
  retval
25
27
  end
26
28
 
27
- def to_json(omit_parent: true)
28
- to_h(omit_parent: omit_parent).to_json
29
+ def to_json(omit_parent: true, omit_empty_children: true)
30
+ to_h(
31
+ omit_parent: omit_parent,
32
+ omit_empty_children: omit_empty_children
33
+ ).to_json
29
34
  end
30
35
 
31
36
  private
@@ -47,4 +52,12 @@ module InstVarsToHash
47
52
  elem
48
53
  end
49
54
  end
55
+
56
+ def skip_parent?(omit_parent, iv)
57
+ omit_parent && (iv =~ /^@?parent$/i)
58
+ end
59
+
60
+ def skip_children?(omit_empty_children, iv)
61
+ omit_empty_children && (iv =~ /^@?children$/i) && instance_variable_get(iv).empty?
62
+ end
50
63
  end
@@ -0,0 +1,10 @@
1
+ module RemoveObsoleteChildren
2
+ def remove_obsolete_children
3
+ @children.delete_if do |child|
4
+ unless child.obsolete?
5
+ child.remove_obsolete_children
6
+ end
7
+ child.obsolete?
8
+ end
9
+ end
10
+ end
@@ -1,9 +1,11 @@
1
1
  require_relative '../lib/inst_vars_to_hash'
2
+ require_relative '../lib/remove_obsolete_children'
2
3
 
3
4
  module AcademicBenchmarks
4
5
  module Standards
5
6
  class Authority
6
7
  include InstVarsToHash
8
+ include RemoveObsoleteChildren
7
9
 
8
10
  attr_accessor :code, :guid, :description, :children
9
11
 
@@ -1,19 +1,22 @@
1
1
  require_relative '../lib/inst_vars_to_hash'
2
+ require_relative '../lib/remove_obsolete_children'
2
3
 
3
4
  module AcademicBenchmarks
4
5
  module Standards
5
6
  class Document
6
7
  include InstVarsToHash
8
+ include RemoveObsoleteChildren
7
9
 
8
- attr_accessor :title, :guid
10
+ attr_accessor :title, :guid, :children
9
11
 
10
12
  def self.from_hash(hash)
11
13
  self.new(title: hash["title"], guid: hash["guid"])
12
14
  end
13
15
 
14
- def initialize(title:, guid:)
16
+ def initialize(title:, guid:, children: [])
15
17
  @title = title
16
18
  @guid = guid
19
+ @children = children
17
20
  end
18
21
  end
19
22
  end
@@ -1,15 +1,18 @@
1
1
  require_relative '../lib/inst_vars_to_hash'
2
+ require_relative '../lib/remove_obsolete_children'
2
3
 
3
4
  module AcademicBenchmarks
4
5
  module Standards
5
6
  class Standard
6
7
  include InstVarsToHash
8
+ include RemoveObsoleteChildren
7
9
 
8
10
  attr_reader :status, :deepest, :children
11
+ attr_writer :grade
9
12
  attr_accessor :guid, :description, :number, :stem, :label, :level,
10
13
  :version, :seq, :adopt_year, :authority, :course,
11
- :document, :grade, :has_relations, :subject,
12
- :subject_doc, :parent, :parent_guid
14
+ :document, :has_relations, :subject, :subject_doc,
15
+ :parent, :parent_guid
13
16
 
14
17
  alias_method :descr, :description
15
18
 
@@ -34,7 +37,7 @@ module AcademicBenchmarks
34
37
  @subject_doc = attr_to_val_or_nil(SubjectDoc, data, "subject_doc")
35
38
  @has_relations = attr_to_val_or_nil(HasRelations, data, "has_relations")
36
39
 
37
- # Parent guid extraction can be a little more complicated. Thanks AB!
40
+ # Parent guid extraction can be a little more complicated
38
41
  if data["parent"] && data["parent"].is_a?(String)
39
42
  @parent_guid = data["parent"]
40
43
  elsif data["parent"] && data["parent"].is_a?(Hash)
@@ -42,6 +45,8 @@ module AcademicBenchmarks
42
45
  end
43
46
  end
44
47
 
48
+ alias_method :from_hash, :initialize
49
+
45
50
  def active?
46
51
  status == "Active"
47
52
  end
@@ -71,13 +76,17 @@ module AcademicBenchmarks
71
76
  end
72
77
 
73
78
  def add_child(child)
79
+ raise StandardError.new("Tried to add self as a child") if self == child
80
+
74
81
  unless child.is_a?(Standard)
75
82
  raise ArgumentError.new("Tried to set child that isn't a Standard")
76
83
  end
84
+ child.parent = self
77
85
  @children.push(child)
78
86
  end
79
87
 
80
88
  def remove_child(child)
89
+ child.parent = nil
81
90
  @children.delete(child)
82
91
  end
83
92
 
@@ -85,10 +94,26 @@ module AcademicBenchmarks
85
94
  @children.count > 0
86
95
  end
87
96
 
97
+ def leaf?
98
+ !has_children?
99
+ end
100
+
101
+ def grade
102
+ return @grade if @grade
103
+
104
+ # check to see if one of our parents has a grade. Use that if so
105
+ p = parent
106
+ while p
107
+ return p.grade if p.grade
108
+ p = p.parent
109
+ end
110
+ nil
111
+ end
112
+
88
113
  private
89
114
 
90
115
  def attr_to_val_or_nil(klass, hash, attr)
91
- return nil unless hash.has_key?(attr)
116
+ return nil unless hash.key?(attr)
92
117
  klass.from_hash(hash)
93
118
  end
94
119
  end
@@ -1,7 +1,7 @@
1
1
  module AcademicBenchmarks
2
2
  module Standards
3
3
  class StandardsForest
4
- attr_reader :trees, :data_hash
4
+ attr_reader :trees, :data_hash, :orphans
5
5
 
6
6
  # The guid to standard hash can optionally be saved to permit speedily
7
7
  # adding standards to the tree (since the tree is unordered,
@@ -13,21 +13,29 @@ module AcademicBenchmarks
13
13
  def initialize(
14
14
  data_hash,
15
15
  save_guid_to_standard_hash: true,
16
- save_initial_data_hash: false
16
+ save_initial_data_hash: false,
17
+ include_obsoletes: true
17
18
  )
18
19
  @data_hash = data_hash.dup.freeze if save_initial_data_hash
19
20
  @guid_to_standard = {} # a hash of guids to standards
20
21
  @trees = []
22
+ @orphans = []
21
23
  process_items(data_hash)
22
24
 
25
+ @trees.delete_if(&:obsolete?) unless include_obsoletes
26
+
23
27
  # upgrade the hash data to a StandardsTree object
24
28
  @trees.map! do |item|
25
- StandardsTree.new(item, build_item_hash: save_guid_to_standard_hash)
29
+ StandardsTree.new(
30
+ item,
31
+ build_item_hash: save_guid_to_standard_hash,
32
+ include_obsoletes: include_obsoletes
33
+ )
26
34
  end
27
35
 
28
- unless save_guid_to_standard_hash
29
- remove_instance_variable('@guid_to_standard')
30
- end
36
+ # We will still have the guid-to-standards saved at the Tree level,
37
+ # so we can safely remove this variable and let the GC free the memory
38
+ remove_instance_variable('@guid_to_standard')
31
39
  end
32
40
 
33
41
  def consolidate_under_root(root)
@@ -36,7 +44,7 @@ module AcademicBenchmarks
36
44
  tree.root.parent_guid = root.guid
37
45
  root.children.push(tree.root)
38
46
  end
39
- StandardsTree.new(root)
47
+ StandardsTree.new(root).tap{ |st| st.add_orphans(@orphans) }
40
48
  end
41
49
 
42
50
  def add_standard(standard)
@@ -49,7 +57,7 @@ module AcademicBenchmarks
49
57
  else
50
58
  raise ArgumentError.new(
51
59
  "standard must be an 'AcademicBenchmarks::Standards::Standard' " \
52
- "or a 'Hash' but was a #{standard.class.to_s}"
60
+ "or a 'Hash' but was a #{standard.class}"
53
61
  )
54
62
  end
55
63
  end
@@ -62,16 +70,20 @@ module AcademicBenchmarks
62
70
  @trees.empty?
63
71
  end
64
72
 
73
+ def has_orphans?
74
+ @orphans.count > 0
75
+ end
76
+
65
77
  def to_s
66
- trees.map{|tree| tree.to_s}
78
+ trees.map(&:to_s)
67
79
  end
68
80
 
69
81
  def to_h
70
- trees.map{|tree| tree.to_h}
82
+ trees.map(&:to_h)
71
83
  end
72
84
 
73
85
  def to_json
74
- trees.map{|tree| tree.to_json}
86
+ trees.map(&:to_h).to_json
75
87
  end
76
88
 
77
89
  private
@@ -96,22 +108,21 @@ module AcademicBenchmarks
96
108
  def link_parent_and_children
97
109
  @guid_to_standard.values.each do |child|
98
110
  if child.parent_guid
99
- present_in_hash_or_raise(child.parent_guid)
100
- parent = @guid_to_standard[child.parent_guid]
101
- parent.add_child(child)
102
- child.parent = parent
111
+ parent_in_hash?(child.parent_guid) ? set_parent_and_child(child) : @orphans.push(child)
103
112
  else
104
113
  @trees.push(child)
105
114
  end
106
115
  end
107
116
  end
108
117
 
109
- def present_in_hash_or_raise(guid)
110
- unless @guid_to_standard.has_key?(guid)
111
- raise StandardError.new(
112
- "item missing from guid_to_standard hash"
113
- )
114
- end
118
+ def set_parent_and_child(child)
119
+ parent = @guid_to_standard[child.parent_guid]
120
+ parent.add_child(child)
121
+ child.parent = parent
122
+ end
123
+
124
+ def parent_in_hash?(guid)
125
+ @guid_to_standard.key?(guid)
115
126
  end
116
127
  end
117
128
  end
@@ -3,15 +3,18 @@ require 'active_support/core_ext/module'
3
3
  module AcademicBenchmarks
4
4
  module Standards
5
5
  class StandardsTree
6
- attr_reader :root
6
+ attr_reader :root, :orphans
7
7
  delegate :children, :to_s, :to_h, :to_json, to: :root
8
8
 
9
9
  # The item hash can optionally be built to permit the speedy
10
10
  # addition of standards to the tree. since the tree is unordered,
11
11
  # adding to it can be expensive without this
12
12
 
13
- def initialize(root, build_item_hash: true)
13
+ def initialize(root, build_item_hash: true, include_obsoletes: true)
14
+ @orphans = []
14
15
  @root = root
16
+ prune_obsolete_branches unless include_obsoletes
17
+
15
18
  if build_item_hash
16
19
  @item_hash = {}
17
20
  go_ahead_and_build_item_hash
@@ -34,11 +37,23 @@ module AcademicBenchmarks
34
37
  else
35
38
  raise ArgumentError.new(
36
39
  "standard must be an 'AcademicBenchmarks::Standards::Standard' " \
37
- "or a 'Hash' but was a #{standard.class.to_s}"
40
+ "or a 'Hash' but was a #{standard.class}"
38
41
  )
39
42
  end
40
43
  end
41
44
 
45
+ def add_orphan(orphan)
46
+ add_orphans([orphan])
47
+ end
48
+
49
+ def add_orphans(orphans)
50
+ @orphans.concat(orphans)
51
+ end
52
+
53
+ def has_orphans?
54
+ @orphans.present?
55
+ end
56
+
42
57
  private
43
58
 
44
59
  def go_ahead_and_build_item_hash
@@ -65,6 +80,10 @@ module AcademicBenchmarks
65
80
  check_children_for_parent(parent_guid, child) if child.has_children?
66
81
  end
67
82
  end
83
+
84
+ def prune_obsolete_branches
85
+ root.remove_obsolete_children
86
+ end
68
87
  end
69
88
  end
70
89
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: academic_benchmarks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Porter
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-15 00:00:00.000000000 Z
11
+ date: 2016-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -155,6 +155,7 @@ files:
155
155
  - lib/academic_benchmarks/api/handle.rb
156
156
  - lib/academic_benchmarks/api/standards.rb
157
157
  - lib/academic_benchmarks/lib/inst_vars_to_hash.rb
158
+ - lib/academic_benchmarks/lib/remove_obsolete_children.rb
158
159
  - lib/academic_benchmarks/standards/authority.rb
159
160
  - lib/academic_benchmarks/standards/course.rb
160
161
  - lib/academic_benchmarks/standards/document.rb