great_schools 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +6 -0
- data/great_schools.gemspec +28 -0
- data/lib/great_schools.rb +88 -0
- data/lib/great_schools/census.rb +52 -0
- data/lib/great_schools/city.rb +72 -0
- data/lib/great_schools/district.rb +29 -0
- data/lib/great_schools/error.rb +51 -0
- data/lib/great_schools/ethnicity.rb +5 -0
- data/lib/great_schools/model.rb +45 -0
- data/lib/great_schools/rank.rb +5 -0
- data/lib/great_schools/result.rb +5 -0
- data/lib/great_schools/review.rb +47 -0
- data/lib/great_schools/school.rb +177 -0
- data/lib/great_schools/score.rb +47 -0
- data/lib/great_schools/test.rb +22 -0
- data/lib/great_schools/version.rb +14 -0
- data/spec/fixtures/browse_districts.xml +35 -0
- data/spec/fixtures/browse_schools.xml +46 -0
- data/spec/fixtures/city_overview.xml +12 -0
- data/spec/fixtures/error.xml +7 -0
- data/spec/fixtures/nearby_cities.xml +25 -0
- data/spec/fixtures/nearby_schools.xml +37 -0
- data/spec/fixtures/school_census_data.xml +40 -0
- data/spec/fixtures/school_profile.xml +70 -0
- data/spec/fixtures/school_reviews.xml +21 -0
- data/spec/fixtures/school_search.xml +38 -0
- data/spec/fixtures/school_test_scores.xml +33 -0
- data/spec/great_schools/census_spec.rb +28 -0
- data/spec/great_schools/city_spec.rb +72 -0
- data/spec/great_schools/district_spec.rb +34 -0
- data/spec/great_schools/ethnicity_spec.rb +17 -0
- data/spec/great_schools/rank_spec.rb +33 -0
- data/spec/great_schools/result_spec.rb +26 -0
- data/spec/great_schools/review_spec.rb +72 -0
- data/spec/great_schools/school_spec.rb +167 -0
- data/spec/great_schools/score_spec.rb +20 -0
- data/spec/great_schools/test_spec.rb +35 -0
- data/spec/great_schools_spec.rb +14 -0
- data/spec/spec_helper.rb +18 -0
- metadata +195 -0
checksums.yaml
ADDED
@@ -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=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -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.
|
data/README.md
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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
|