verso 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|