verso 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gitignore +5 -0
  2. data/Gemfile +4 -0
  3. data/LICENSE.md +20 -0
  4. data/README.md +75 -0
  5. data/Rakefile +1 -0
  6. data/lib/verso.rb +33 -0
  7. data/lib/verso/cluster.rb +43 -0
  8. data/lib/verso/cluster_list.rb +19 -0
  9. data/lib/verso/correlation_list.rb +53 -0
  10. data/lib/verso/course.rb +84 -0
  11. data/lib/verso/course_list.rb +36 -0
  12. data/lib/verso/credential.rb +34 -0
  13. data/lib/verso/credential_list.rb +28 -0
  14. data/lib/verso/duty_area.rb +17 -0
  15. data/lib/verso/edition_list.rb +19 -0
  16. data/lib/verso/emphasis.rb +21 -0
  17. data/lib/verso/emphasis_list.rb +19 -0
  18. data/lib/verso/examination_list.rb +21 -0
  19. data/lib/verso/extra.rb +38 -0
  20. data/lib/verso/extras_list.rb +22 -0
  21. data/lib/verso/frontmatter.rb +18 -0
  22. data/lib/verso/http_get.rb +9 -0
  23. data/lib/verso/occupation.rb +25 -0
  24. data/lib/verso/occupation_data.rb +31 -0
  25. data/lib/verso/occupation_list.rb +32 -0
  26. data/lib/verso/pathway.rb +25 -0
  27. data/lib/verso/program_area.rb +39 -0
  28. data/lib/verso/program_area_list.rb +20 -0
  29. data/lib/verso/sol_correlation_list.rb +53 -0
  30. data/lib/verso/standard.rb +37 -0
  31. data/lib/verso/standards_list.rb +49 -0
  32. data/lib/verso/task.rb +30 -0
  33. data/lib/verso/task_list.rb +31 -0
  34. data/lib/verso/version.rb +3 -0
  35. data/spec/cluster_list_spec.rb +12 -0
  36. data/spec/cluster_spec.rb +18 -0
  37. data/spec/correlation_list_spec.rb +55 -0
  38. data/spec/course_list_spec.rb +36 -0
  39. data/spec/course_spec.rb +193 -0
  40. data/spec/credential_list_spec.rb +75 -0
  41. data/spec/credential_spec.rb +34 -0
  42. data/spec/duty_area_spec.rb +27 -0
  43. data/spec/edition_list_spec.rb +11 -0
  44. data/spec/emphasis_list_spec.rb +21 -0
  45. data/spec/emphasis_spec.rb +31 -0
  46. data/spec/examination_list_spec.rb +29 -0
  47. data/spec/extra_spec.rb +36 -0
  48. data/spec/extras_list_spec.rb +25 -0
  49. data/spec/frontmatter_spec.rb +11 -0
  50. data/spec/occupation_data_spec.rb +24 -0
  51. data/spec/occupation_list_spec.rb +21 -0
  52. data/spec/occupation_spec.rb +41 -0
  53. data/spec/pathway_spec.rb +41 -0
  54. data/spec/program_area_list_spec.rb +11 -0
  55. data/spec/program_area_spec.rb +39 -0
  56. data/spec/sol_correlation_list_spec.rb +74 -0
  57. data/spec/spec_helper.rb +23 -0
  58. data/spec/standard_spec.rb +53 -0
  59. data/spec/standards_list_spec.rb +51 -0
  60. data/spec/task_list_spec.rb +72 -0
  61. data/spec/task_spec.rb +55 -0
  62. data/verso.gemspec +25 -0
  63. metadata +189 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ tmp/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in verso.gemspec
4
+ gemspec
data/LICENSE.md ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Virginia Department of Education
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # The Verso Ruby Gem
2
+
3
+ A Ruby wrapper for the Virginia CTE Resource Center's [Web
4
+ API](http://api.cteresource.org), providing access to Virginia's Career and
5
+ Technical Education curriculum, course, and occupation data.
6
+
7
+ ## Installation
8
+
9
+ git clone git://github.com/jlcapps/verso.git
10
+ cd verso
11
+ rake install
12
+
13
+ ## Caveat
14
+
15
+ This gem has been *extracted* from a larger project, the Web application that
16
+ presents [Verso](http://www.cteresource.org/verso/), the [Administrative
17
+ Planning Guide](http://www.cteresource.org/apg/), and the [Career Planning
18
+ Guide](http://www.cteresource.org/cpg/) on the Virginia CTE Resource Center Web
19
+ site. For this reason the object interfaces are sometimes inconsistent (for
20
+ instance collection objects do not always respond to all the expected
21
+ methods -- `#to_a` is your friend), the specs are incomplete, and the
22
+ documentation is nil.
23
+
24
+ All that said, the wrapper is reasonably complete, and should make a great
25
+ starting pointing for getting course, curriculum, and occupation data out of
26
+ Verso. The classes available in lib/verso correspond to the resources described
27
+ in the [API documentation](http://api.cteresource.org/docs). The usual points
28
+ of entry are the [Cluster List](http://api.cteresource.org/docs/clusters), the
29
+ [Course List](http://api.cteresource.org/docs/courses), the [Occupation
30
+ List](http://api.cteresource.org/docs/occupations), and the [Credential
31
+ List](http://api.cteresource.org/docs/credentials).
32
+
33
+ ## Quick Start
34
+
35
+ ### Start with Clusters
36
+
37
+ require 'verso'
38
+ cluster = Verso::ClusterList.new.last
39
+ cluster.pathways.first.title # => "Facility and Mobile Equipment Maintenance"
40
+ cluster.pathways.first.occupations.first.title # => "Aircraft Mechanic and Service Technician"
41
+ cluster.pathways.first.occupations.first.related_courses.first.title # => "Automotive Technology I"
42
+
43
+ ### Start with Courses
44
+
45
+ require 'verso'
46
+ course = Verso::CourseList.new(:code => "6321").first
47
+ course.title # => "Accouting, Advanced"
48
+ da = course.duty_areas.to_a[5] # I warned you about the incomplete interface . . . .
49
+ da.title # => "Using Technology to Implement Accounting Procedures"
50
+ da.tasks.last.statement # => "Apply emerging technology trends used in the accounting profession."
51
+
52
+ ### Start with Occupations
53
+
54
+ Search for Occupations, get back lists of "occupation data," i.e., a list of lists of Occupations, grouped by Cluster and Pathway. To wit:
55
+
56
+ require 'verso'
57
+ ods = Verso::OccupationList.new(:text => "golf")
58
+ ods.count # => 3
59
+ ods.first.cluster.title # => "Agriculture, Food and Natural Resources"
60
+ ods.first.pathway.title # => "Environmental Service Systems"
61
+ ods.first.occupations.count # => 3
62
+ occupation = ods.first.occupations.first
63
+ occupation.title => "Turf Farmer"
64
+ course = occupation.related_courses.last.title # => "Turf Grass Applications, Advanced"
65
+
66
+ ### Start with Credentials
67
+
68
+ require 'verso'
69
+ creds = Verso::CredentialList.new(:text => "programming")
70
+ creds.first.title # => "Computer Programming (NOCTI)"
71
+ creds.first.related_courses.last # => "Programming, Advanced"
72
+
73
+ In general, the objects returned from the wrapper should respond to calls to
74
+ the attributes described in the API documentation. Open an issue on GitHub or
75
+ send an email to lcapps@cteresource.org if you have questions or concerns.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/verso.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'addressable/uri'
2
+ require 'json'
3
+ require 'net/http'
4
+ require 'ostruct'
5
+
6
+ require 'verso/http_get'
7
+ require 'verso/cluster'
8
+ require 'verso/cluster_list'
9
+ require 'verso/correlation_list'
10
+ require 'verso/course'
11
+ require 'verso/course_list'
12
+ require 'verso/credential'
13
+ require 'verso/credential_list'
14
+ require 'verso/duty_area'
15
+ require 'verso/edition_list'
16
+ require 'verso/emphasis'
17
+ require 'verso/emphasis_list'
18
+ require 'verso/examination_list'
19
+ require 'verso/extra'
20
+ require 'verso/extras_list'
21
+ require 'verso/frontmatter'
22
+ require 'verso/occupation'
23
+ require 'verso/occupation_data'
24
+ require 'verso/occupation_list'
25
+ require 'verso/pathway'
26
+ require 'verso/program_area_list'
27
+ require 'verso/program_area'
28
+ require 'verso/sol_correlation_list'
29
+ require 'verso/standard'
30
+ require 'verso/standards_list'
31
+ require 'verso/task'
32
+ require 'verso/task_list'
33
+ require 'verso/version'
@@ -0,0 +1,43 @@
1
+ module Verso
2
+ class Cluster
3
+ include HTTPGet
4
+
5
+ def initialize(opts)
6
+ @raw_cluster = opts
7
+ end
8
+
9
+ def method_missing(mname)
10
+ if !@raw_cluster.has_key?(mname.to_s)
11
+ @raw_cluster.merge!(JSON.parse(http_get("/clusters/#{id}"))["cluster"])
12
+ end
13
+ @raw_cluster[mname.to_s]
14
+ end
15
+
16
+ def id
17
+ @raw_cluster["id"]
18
+ end
19
+
20
+ def title
21
+ @title ||= @raw_cluster["title"] || @raw_cluster["cluster"]["title"]
22
+ end
23
+
24
+ def contact
25
+ @contact ||= OpenStruct.new(method_missing(:contact))
26
+ end
27
+
28
+ def slug
29
+ @raw_cluster["title"].parameterize
30
+ end
31
+
32
+ def pathways
33
+ @pathways ||= method_missing(:pathways).
34
+ collect { |p| Pathway.new(p) }
35
+ end
36
+
37
+ def courses
38
+ @courses ||= CourseList.new(:cluster => slug.gsub('-', ' ')).
39
+ sort_by { |c| c.title + c.edition }.
40
+ uniq { |c| c.code + c.edition }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,19 @@
1
+ module Verso
2
+ class ClusterList
3
+ include Enumerable
4
+ include HTTPGet
5
+
6
+ def clusters
7
+ @clusters ||= JSON.parse(http_get('/clusters/'))["clusters"].
8
+ collect { |c| Cluster.new(c) }
9
+ end
10
+
11
+ def each &block
12
+ clusters.each &block
13
+ end
14
+
15
+ def last
16
+ clusters[-1]
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,53 @@
1
+ module Verso
2
+ class CorrelationList
3
+ include Enumerable
4
+ include HTTPGet
5
+
6
+ def initialize(context, name)
7
+ @context = context
8
+ @name = name
9
+ end
10
+
11
+ def title
12
+ @title ||= @context.standards.find { |s| s.name == @name }.title
13
+ end
14
+
15
+ def code
16
+ @context.code
17
+ end
18
+
19
+ def edition
20
+ @context.edition
21
+ end
22
+
23
+ def tasklist
24
+ @context.duty_areas
25
+ end
26
+
27
+ def sol_names
28
+ @sol_names ||= @context.standards.sols.collect { |s| s.name }
29
+ end
30
+
31
+ def correlations
32
+ @correlations ||= JSON.parse(
33
+ http_get("/courses/#{code},#{edition}/standards/#{@name}/correlations")
34
+ )["correlations"]
35
+ end
36
+
37
+ def each &block
38
+ count = 0
39
+ tasklist.each do |da|
40
+ da.tasks.each do |task|
41
+ count += 1
42
+ related = correlations.find { |c| c["id"] == task.id }
43
+ if related.nil?
44
+ next
45
+ else
46
+ related["number"] = count
47
+ yield Task.new(related)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,84 @@
1
+ module Verso
2
+ class Course
3
+ include HTTPGet
4
+
5
+ def initialize(raw_course)
6
+ @raw_course = raw_course
7
+ end
8
+
9
+ def method_missing(mname)
10
+ if !@raw_course.has_key?(mname.to_s)
11
+ @raw_course = JSON.parse(http_get("/courses/#{code},#{edition}"))
12
+ end
13
+ @raw_course.has_key?(mname.to_s) ? @raw_course[mname.to_s] : super
14
+ end
15
+
16
+ def task(tid)
17
+ @tasks ||= {}
18
+ @tasks[tid] ||= Task.new("code" => code, "edition" => edition, "id" => tid)
19
+ end
20
+
21
+ def frontmatter
22
+ @frontmatter ||= if self.related_resources.include?("frontmatter")
23
+ Frontmatter.new("code" => code, "edition" => edition)
24
+ else
25
+ nil
26
+ end
27
+ end
28
+
29
+ def duty_areas
30
+ @duty_areas ||= TaskList.new(:code => code, :edition => edition)
31
+ end
32
+
33
+ def occupation_data
34
+ @occupation_data ||= method_missing(:occupation_data).
35
+ collect { |od| OccupationData.new(od) }
36
+ end
37
+
38
+ def prerequisites
39
+ @prerequisites ||= prerequisite_courses.collect { |c| Course.new(c) }
40
+ end
41
+
42
+ def related_courses
43
+ @related_courses ||= method_missing(:related_courses).
44
+ collect { |c| Course.new(c) }
45
+ end
46
+
47
+ def credentials
48
+ @credentials ||= method_missing(:credentials).
49
+ collect { |c| Credential.new(c) }
50
+ end
51
+
52
+ def certifications
53
+ @certfications ||= credentials.select { |c| c.type == "Certification" }
54
+ end
55
+
56
+ def licenses
57
+ @licenses ||= credentials.select { |c| c.type == "License" }
58
+ end
59
+
60
+ def standards
61
+ @standards ||= if self.related_resources.include?("standards")
62
+ StandardsList.from_course(self)
63
+ else
64
+ StandardsList.new([], self)
65
+ end
66
+ end
67
+
68
+ def is_ms?
69
+ grade_levels.split.first.to_i < 9
70
+ end
71
+
72
+ def is_hs?
73
+ grade_levels.split.last.to_i > 8
74
+ end
75
+
76
+ def extras
77
+ @extras ||= if related_resources.include?("extras")
78
+ ExtrasList.new(:code => code, :edition => edition)
79
+ else
80
+ []
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,36 @@
1
+ module Verso
2
+ class CourseList
3
+ include Enumerable
4
+ include HTTPGet
5
+
6
+ def initialize(raw_query)
7
+ @q_uri = Addressable::URI.new(
8
+ :path => '/courses',
9
+ :query_values => raw_query.
10
+ select { |k, v| v }.
11
+ reject { |k, v| v.to_s.empty? }
12
+ )
13
+ end
14
+
15
+ def courses
16
+ @courses ||= if @q_uri.query_values.values.any?
17
+ JSON.parse(http_get(@q_uri.request_uri))["courses"].
18
+ collect { |c| Course.new(c) }
19
+ else
20
+ []
21
+ end
22
+ end
23
+
24
+ def each &block
25
+ courses.each &block
26
+ end
27
+
28
+ def last
29
+ courses[-1]
30
+ end
31
+
32
+ def empty?
33
+ courses.empty?
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,34 @@
1
+ module Verso
2
+ class Credential
3
+ include HTTPGet
4
+
5
+ def initialize(opts)
6
+ @raw_credential = opts
7
+ end
8
+
9
+ def method_missing(mname)
10
+ if @raw_credential[mname.to_s].nil?
11
+ @raw_credential.merge!(
12
+ JSON.parse(http_get("/credentials/#{id}"))["credential"]
13
+ )
14
+ end
15
+ @raw_credential[mname.to_s]
16
+ end
17
+
18
+ def source
19
+ @source ||= OpenStruct.new(method_missing(:source))
20
+ end
21
+
22
+ def details
23
+ @details ||= if @raw_credential.has_key?("details")
24
+ @raw_credential["details"]
25
+ else
26
+ method_missing(:details)
27
+ end.to_s
28
+ end
29
+
30
+ def related_courses
31
+ method_missing(:related_courses).collect { |rc| Course.new(rc) }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ module Verso
2
+ class CredentialList
3
+ include Enumerable
4
+ include HTTPGet
5
+
6
+ attr_reader :credentials
7
+
8
+ def initialize(opts={})
9
+ @q_uri = Addressable::URI.new(:path => '/credentials')
10
+ @q_uri.query_values = opts unless opts[:text].to_s.empty?
11
+ @credentials = JSON.parse(http_get(@q_uri.request_uri))["credentials"].
12
+ collect { |c| Credential.new(c) }.
13
+ sort_by { |c| c.title }
14
+ end
15
+
16
+ def each &block
17
+ credentials.each &block
18
+ end
19
+
20
+ def last
21
+ credentials[-1]
22
+ end
23
+
24
+ def empty?
25
+ credentials.empty?
26
+ end
27
+ end
28
+ end