great_schools 0.1.0

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.
Files changed (45) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +58 -0
  6. data/Rakefile +6 -0
  7. data/great_schools.gemspec +28 -0
  8. data/lib/great_schools.rb +88 -0
  9. data/lib/great_schools/census.rb +52 -0
  10. data/lib/great_schools/city.rb +72 -0
  11. data/lib/great_schools/district.rb +29 -0
  12. data/lib/great_schools/error.rb +51 -0
  13. data/lib/great_schools/ethnicity.rb +5 -0
  14. data/lib/great_schools/model.rb +45 -0
  15. data/lib/great_schools/rank.rb +5 -0
  16. data/lib/great_schools/result.rb +5 -0
  17. data/lib/great_schools/review.rb +47 -0
  18. data/lib/great_schools/school.rb +177 -0
  19. data/lib/great_schools/score.rb +47 -0
  20. data/lib/great_schools/test.rb +22 -0
  21. data/lib/great_schools/version.rb +14 -0
  22. data/spec/fixtures/browse_districts.xml +35 -0
  23. data/spec/fixtures/browse_schools.xml +46 -0
  24. data/spec/fixtures/city_overview.xml +12 -0
  25. data/spec/fixtures/error.xml +7 -0
  26. data/spec/fixtures/nearby_cities.xml +25 -0
  27. data/spec/fixtures/nearby_schools.xml +37 -0
  28. data/spec/fixtures/school_census_data.xml +40 -0
  29. data/spec/fixtures/school_profile.xml +70 -0
  30. data/spec/fixtures/school_reviews.xml +21 -0
  31. data/spec/fixtures/school_search.xml +38 -0
  32. data/spec/fixtures/school_test_scores.xml +33 -0
  33. data/spec/great_schools/census_spec.rb +28 -0
  34. data/spec/great_schools/city_spec.rb +72 -0
  35. data/spec/great_schools/district_spec.rb +34 -0
  36. data/spec/great_schools/ethnicity_spec.rb +17 -0
  37. data/spec/great_schools/rank_spec.rb +33 -0
  38. data/spec/great_schools/result_spec.rb +26 -0
  39. data/spec/great_schools/review_spec.rb +72 -0
  40. data/spec/great_schools/school_spec.rb +167 -0
  41. data/spec/great_schools/score_spec.rb +20 -0
  42. data/spec/great_schools/test_spec.rb +35 -0
  43. data/spec/great_schools_spec.rb +14 -0
  44. data/spec/spec_helper.rb +18 -0
  45. metadata +195 -0
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MmVjNzJmYTI4YjA4NjQ5OTFjNzQ5YmJlMTYzOTI0OWJmYzk0Yzk2OQ==
5
+ data.tar.gz: !binary |-
6
+ YmZmNjZkZDVkOGE3MDU1MzU2MWYyNDY4MTEyNTFhNmVjZmExZWE2ZA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ NDg4NWFkYTI1NWIwMTNlZmIxNDVhZTc0ODkwZjkyOWU3N2ZhYWM5NDY0ODNk
10
+ YTNkMzRkOWEyMzgxNjgwNTc0OTA2MGE4ZTEwYWM0ZTgzZDk2MmQ5MjE0MDky
11
+ MzY0NmRmNTcyMTFkZTM5MzcyMjhhZDQ5ODYwNDQ5Y2Q2MmQzNDY=
12
+ data.tar.gz: !binary |-
13
+ MDA1ZWVhYjcyNjMyMTU5NWJhODI0MjI1Y2M0ZmI1Yjg3MzEwYzA2YmZmMDYx
14
+ ZWEzNTI0ZDAwM2UwZDAwZmU2ZDJmNjExYzI4ZDFiMjc4ODVlY2Q4OTJmZTQ5
15
+ OGRhMGI0MWNmZjQ4MTRmZDkyNGVjZjc1M2FlZDI1ZTRmZTI0MGU=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in great_schools.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Michael Sepcot
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,58 @@
1
+ # GreatSchools
2
+
3
+ A Ruby interface to the GreatSchools API.
4
+
5
+ The GreatSchools API allows access to School Profiles, Test Scores, School
6
+ Reviews, GreatSchools Ratings, School Districts, and City Profiles.
7
+
8
+ Before you can start using the GreatSchool API, you must register and
9
+ request an access key at: http://www.greatschools.org/api/registration.page
10
+
11
+ ## Installation
12
+
13
+ Add this line to your application's Gemfile:
14
+
15
+ gem 'great_schools'
16
+
17
+ And then execute:
18
+
19
+ $ bundle
20
+
21
+ Or install it yourself as:
22
+
23
+ $ gem install great_schools
24
+
25
+ ## Usage
26
+
27
+ The first call you make should setup your API key:
28
+
29
+ GreatSchools::API.key = 'MY_API_KEY'
30
+
31
+ The first entry point is `GreatSchools::City` to lookup nearby cities; get an
32
+ overview of a specific city; and find the districts and reviews associated with
33
+ it.
34
+
35
+ GreatSchools::City.nearby('CA', 'San Francisco') # => list of cities neighboring San Francisco, CA.
36
+
37
+ city = GreatSchools::City.overview('CA', 'San Francisco')
38
+ city.districts # => list of districts in San Francisco, CA
39
+ city.reviews # => list of reviews for schools in San Francisco, CA
40
+
41
+ The second main entry point is `GreatSchools::School` to lookup schools in a
42
+ specific city or near an address; get a school profile; and find census data,
43
+ test scores, and reviews associated with it.
44
+
45
+ schools = GreatSchools::School.browse('CA', 'San Francisco') # => list of schools
46
+
47
+ school = GreatSchools::School.nearby('CA', zip_code: 94105).first
48
+ school.census # => lunch pricing, student/teacher ratio, ethnicities, etc.
49
+ school.review # => list of reviews for the school
50
+ school.score # => GreatSchool rank and recent test results
51
+
52
+ ## Contributing
53
+
54
+ 1. Fork it
55
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
56
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
57
+ 4. Push to the branch (`git push origin my-new-feature`)
58
+ 5. Create new Pull Request
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
5
+
6
+ task default: :spec
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'great_schools/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'great_schools'
8
+ spec.version = GreatSchools::Version
9
+ spec.authors = ['Michael J. Sepcot']
10
+ spec.email = ['developer@sepcot.com']
11
+ spec.description = %q{The GreatSchools API allows access to School Profiles, Test Scores, School Reviews, GreatSchools Ratings, School Districts, and City Profiles.}
12
+ spec.summary = %q{A Ruby interface to the GreatSchools API.}
13
+ spec.homepage = 'https://github.com/msepcot/great_schools'
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_development_dependency 'bundler', '~> 1.3'
22
+ spec.add_development_dependency 'fakeweb'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec'
25
+
26
+ spec.add_dependency 'activesupport', '>= 3.0.0'
27
+ spec.add_dependency 'httparty', '~> 0.12.0'
28
+ end
@@ -0,0 +1,88 @@
1
+ require 'great_schools/error'
2
+ require 'great_schools/model'
3
+
4
+ require 'great_schools/census'
5
+ require 'great_schools/city'
6
+ require 'great_schools/district'
7
+ require 'great_schools/ethnicity'
8
+ require 'great_schools/rank'
9
+ require 'great_schools/result'
10
+ require 'great_schools/review'
11
+ require 'great_schools/school'
12
+ require 'great_schools/score'
13
+ require 'great_schools/test'
14
+
15
+ require 'great_schools/version'
16
+
17
+ require 'active_support'
18
+ require 'active_support/core_ext/array/wrap'
19
+ require 'active_support/core_ext/hash/slice'
20
+ require 'active_support/core_ext/object/blank'
21
+ require 'active_support/core_ext/object/try'
22
+
23
+ require 'httparty'
24
+
25
+ # = GreatSchools
26
+ #
27
+ # GreatSchools profiles more than 90,000 public and 30,000 private schools in
28
+ # the United States. With hundreds of thousands of ratings and Parent Reviews
29
+ # about schools nationwide, we provide the most comprehensive data about
30
+ # elementary, middle and high schools.
31
+ module GreatSchools
32
+ # The GreatSchools API allows you to add valuable information to your
33
+ # application, including:
34
+ #
35
+ # * Schools in a city
36
+ # * Schools near an address
37
+ # * Data about specific schools, including school directory information,
38
+ # grade levels, enrollment, test scores, ratings and reviews and more.
39
+ #
40
+ # Before you can start using the GreatSchool API, you must register and
41
+ # request an access key at: http://www.greatschools.org/api/registration.page
42
+ # --
43
+ # TODO: load API access key from a config file (great_schools.yml or something)
44
+ # ++
45
+ class API
46
+ class << self # Class methods
47
+ DOMAIN = 'http://api.greatschools.org'
48
+
49
+ # The API access key, must be set before making any requests.
50
+ attr_accessor :key
51
+
52
+ # Makes an API request to +path+ with the supplied query +parameters+ (merges
53
+ # in the API access +key+).
54
+ #
55
+ # Returns a +Hash+ or an +Array+ with the encompassing XML container stripped.
56
+ #
57
+ # Raises a +GreatSchools::Error+ if the server response code is not 200.
58
+ #
59
+ # ==== Attributes
60
+ #
61
+ # * +path+ - component path of the URL
62
+ # * +parameters+ - +Hash+ of query string elements
63
+ def get(path, parameters = {})
64
+ parameters.merge!(key: key).keep_if { |_,v| v.present? }
65
+
66
+ response = HTTParty.get("#{DOMAIN}/#{path}", query: parameters, format: :xml)
67
+
68
+ if response.code.eql?(200)
69
+ parse(response.values.first) # strip the container element before parsing
70
+ else
71
+ raise GreatSchools::Error.new(response)
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ # Returns a +Hash+ of a single element, or an +Array+ of elements without
78
+ # the container hash.
79
+ def parse(hash)
80
+ if hash.keys.size.eql?(1) # we have an array of elements
81
+ hash.values.first # strip the container and return the array
82
+ else # we have one element, return the hash
83
+ hash
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,52 @@
1
+ module GreatSchools # :nodoc:
2
+ # = School Census and Profile Data
3
+ #
4
+ # While you can pull census data for a school manually, I'd recommend going
5
+ # through the school model and letting it make the call for you:
6
+ #
7
+ # schools = GreatSchools::School.browse('CA', 'San Mateo')
8
+ # school = schools.first
9
+ # school.census # equivalent to: GreatSchools::Census.for_school(school.state, school.id)
10
+ class Census < Model
11
+ # --
12
+ # NOTE: these are all +GreatSchool::School+ attributes, should we build a
13
+ # school model instead of attaching these attributes?
14
+ # ++
15
+ attr_accessor :school_name, :address, :latitude, :longitude, :phone, :type, :district, :enrollment
16
+
17
+ attr_accessor :free_and_reduced_price_lunch, :student_teacher_ratio, :ethnicities
18
+
19
+ class << self # Class methods
20
+ # Returns census and profile data for a school.
21
+ #
22
+ # ==== Attributes
23
+ #
24
+ # * +state+ - Two letter state abbreviation
25
+ # * +id+ - Numeric id of a school. This GreatSchools ID is included in
26
+ # other listing requests like +GreatSchools::School#browse+
27
+ # and +GreatSchools::School#nearby+
28
+ def for_school(state, id)
29
+ response = GreatSchools::API.get("school/census/#{state.upcase}/#{id}")
30
+
31
+ new(response)
32
+ end
33
+ end
34
+
35
+ # Set an array of +GreatSchools::Ethnicity+.
36
+ #
37
+ # ==== Attributes
38
+ #
39
+ # * +params+ - a +Hash+ or +Array+ of +GreatSchools::Ethnicity+ attributes.
40
+ def ethnicities=(params)
41
+ @ethnicities = []
42
+
43
+ params = params['ethnicity'] if params.is_a?(Hash) && params.key?('ethnicity')
44
+
45
+ Array.wrap(params).each do |hash|
46
+ @ethnicities << GreatSchools::Ethnicity.new(hash)
47
+ end
48
+
49
+ @ethnicities
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,72 @@
1
+ module GreatSchools # :nodoc:
2
+ # = GreatSchools City
3
+ #
4
+ # --
5
+ # TODO: add method to grab nearby schools using +GreatSchools::School#nearby+
6
+ # with the +city+ and +state+ options.
7
+ # ++
8
+ class City < Model
9
+ attr_accessor :name, :state, :rating, :total_schools
10
+ attr_accessor :elementary_schools, :middle_schools, :high_schools
11
+ attr_accessor :public_schools, :charter_schools, :private_schools
12
+
13
+ class << self # Class methods
14
+ # Returns a list of cities near another city.
15
+ #
16
+ # ==== Attributes
17
+ #
18
+ # * +state+ - Two letter state abbreviation
19
+ # * +city+ - Name of city
20
+ #
21
+ # ==== Options
22
+ #
23
+ # * +:radius+ - Radius in miles to confine search to. Defaults to 15 and
24
+ # can be in the range 1-100.
25
+ # * +:sort+ - How to sort the results. Defaults to 'distance'. Other
26
+ # options are 'name' to sort by city name in alphabetical
27
+ # order, and 'rating' to sort by GreatSchool city rating,
28
+ # highest first.
29
+ # --
30
+ # TODO: handle validations
31
+ # ++
32
+ def nearby(state, city, options = {})
33
+ options.slice!(:radius, :sort)
34
+
35
+ response = GreatSchools::API.get("cities/nearby/#{state.upcase}/#{parameterize(city)}", options)
36
+
37
+ Array.wrap(response).map { |city| new(city.merge(state: state)) }
38
+ end
39
+
40
+ # Returns information about a city.
41
+ #
42
+ # ==== Attributes
43
+ #
44
+ # * +state+ - Two letter state abbreviation
45
+ # * +city+ - Name of city
46
+ def overview(state, city)
47
+ response = GreatSchools::API.get("cities/#{state.upcase}/#{parameterize(city)}")
48
+
49
+ new(response.merge(state: state))
50
+ end
51
+ end
52
+
53
+ alias_method :city, :name
54
+ alias_method :city=, :name=
55
+
56
+ # Returns a list of districts in a city.
57
+ def districts
58
+ @districts ||= GreatSchools::District.browse(state, city)
59
+ end
60
+
61
+ # Returns a list of the most recent reviews for any schools in a city.
62
+ #
63
+ # ==== Options
64
+ #
65
+ # * +:cutoff_age+ - Reviews must have been published after this many days
66
+ # ago to be returned.
67
+ # * +:limit+ - Maximum number of reviews to return. This defaults to 5.
68
+ def reviews(options = {})
69
+ GreatSchools::Review.for_city(state, name, options.slice(:cuttoff_age, :limit))
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,29 @@
1
+ module GreatSchools # :nodoc:
2
+ # = GreatSchools District
3
+ #
4
+ # --
5
+ # TODO: add method to grab schools using +GreatSchools::School#nearby+ with
6
+ # the +address+, +city+, +state+, and +zip_code+ options (parsing address).
7
+ # Filter results to schools with a matching district name.
8
+ # ++
9
+ class District < Model
10
+ attr_accessor :name, :address, :phone, :fax, :website
11
+ attr_accessor :nces_code, :district_rating, :grade_range, :total_schools
12
+ attr_accessor :elementary_schools, :middle_schools, :high_schools
13
+ attr_accessor :public_schools, :charter_schools
14
+
15
+ class << self # Class methods
16
+ # Returns a list of school districts in a city.
17
+ #
18
+ # ==== Attributes
19
+ #
20
+ # * +state+ - Two letter state abbreviation
21
+ # * +city+ - Name of city
22
+ def browse(state, city)
23
+ response = GreatSchools::API.get("districts/#{state.upcase}/#{parameterize(city)}")
24
+
25
+ Array.wrap(response).map { |district| new(district) }
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,51 @@
1
+ module GreatSchools # :nodoc:
2
+ # = GreatSchools Error
3
+ #
4
+ # Encompass any errors sent back by the GreatSchools API.
5
+ #
6
+ # GreatSchools sends back XML to all API requests. The error response looks
7
+ # like:
8
+ #
9
+ # <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
10
+ # <error>
11
+ # <errorCode>3</errorCode>
12
+ # <faultString>Invalid API key.</faultString>
13
+ # <date>2013/11/22</date>
14
+ # <call>/reviews/city/CA/Foster-City</call>
15
+ # </error>
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # The most common error is trying to request data that your API key does not
20
+ # have access to.
21
+ #
22
+ # GreatSchools::API.key = 'INVALID_KEY'
23
+ # GreatSchools::Review.for_city('CA', 'Foster City')
24
+ # # => #<GreatSchools::Error error_code: "3", fault_string: "Invalid API key.", call: "/reviews/city/CA/Foster-City", date: "2013/11/22">
25
+ class Error < StandardError
26
+ attr_reader :call, :date, :error_code
27
+
28
+ alias_method :fault_string, :message
29
+
30
+ # Creates a new +GreatSchools::Error+ from a parsed +HTTParty+ response.
31
+ # The +faultString+ is used as the error +message+.
32
+ #
33
+ # ==== Attributes
34
+ #
35
+ # * +response+ - a parsed response object from +HTTParty+
36
+ # --
37
+ # TODO: add error handling - ensure we have a +Hash+, use +fetch+ with defaults
38
+ # ++
39
+ def initialize(response)
40
+ super(response['error']['faultString'])
41
+
42
+ @call = response['error']['call']
43
+ @date = response['error']['date']
44
+ @error_code = response['error']['errorCode']
45
+ end
46
+
47
+ def inspect # :nodoc:
48
+ "#<#{self.class} error_code: \"#{error_code}\", fault_string: \"#{message}\", call: \"#{call}\", date: \"#{date}\">"
49
+ end
50
+ end
51
+ end