great_schools 0.1.0

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