verso 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.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/LICENSE.md +20 -0
- data/README.md +75 -0
- data/Rakefile +1 -0
- data/lib/verso.rb +33 -0
- data/lib/verso/cluster.rb +43 -0
- data/lib/verso/cluster_list.rb +19 -0
- data/lib/verso/correlation_list.rb +53 -0
- data/lib/verso/course.rb +84 -0
- data/lib/verso/course_list.rb +36 -0
- data/lib/verso/credential.rb +34 -0
- data/lib/verso/credential_list.rb +28 -0
- data/lib/verso/duty_area.rb +17 -0
- data/lib/verso/edition_list.rb +19 -0
- data/lib/verso/emphasis.rb +21 -0
- data/lib/verso/emphasis_list.rb +19 -0
- data/lib/verso/examination_list.rb +21 -0
- data/lib/verso/extra.rb +38 -0
- data/lib/verso/extras_list.rb +22 -0
- data/lib/verso/frontmatter.rb +18 -0
- data/lib/verso/http_get.rb +9 -0
- data/lib/verso/occupation.rb +25 -0
- data/lib/verso/occupation_data.rb +31 -0
- data/lib/verso/occupation_list.rb +32 -0
- data/lib/verso/pathway.rb +25 -0
- data/lib/verso/program_area.rb +39 -0
- data/lib/verso/program_area_list.rb +20 -0
- data/lib/verso/sol_correlation_list.rb +53 -0
- data/lib/verso/standard.rb +37 -0
- data/lib/verso/standards_list.rb +49 -0
- data/lib/verso/task.rb +30 -0
- data/lib/verso/task_list.rb +31 -0
- data/lib/verso/version.rb +3 -0
- data/spec/cluster_list_spec.rb +12 -0
- data/spec/cluster_spec.rb +18 -0
- data/spec/correlation_list_spec.rb +55 -0
- data/spec/course_list_spec.rb +36 -0
- data/spec/course_spec.rb +193 -0
- data/spec/credential_list_spec.rb +75 -0
- data/spec/credential_spec.rb +34 -0
- data/spec/duty_area_spec.rb +27 -0
- data/spec/edition_list_spec.rb +11 -0
- data/spec/emphasis_list_spec.rb +21 -0
- data/spec/emphasis_spec.rb +31 -0
- data/spec/examination_list_spec.rb +29 -0
- data/spec/extra_spec.rb +36 -0
- data/spec/extras_list_spec.rb +25 -0
- data/spec/frontmatter_spec.rb +11 -0
- data/spec/occupation_data_spec.rb +24 -0
- data/spec/occupation_list_spec.rb +21 -0
- data/spec/occupation_spec.rb +41 -0
- data/spec/pathway_spec.rb +41 -0
- data/spec/program_area_list_spec.rb +11 -0
- data/spec/program_area_spec.rb +39 -0
- data/spec/sol_correlation_list_spec.rb +74 -0
- data/spec/spec_helper.rb +23 -0
- data/spec/standard_spec.rb +53 -0
- data/spec/standards_list_spec.rb +51 -0
- data/spec/task_list_spec.rb +72 -0
- data/spec/task_spec.rb +55 -0
- data/verso.gemspec +25 -0
- metadata +189 -0
data/Gemfile
ADDED
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
|
data/lib/verso/course.rb
ADDED
@@ -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
|