barometer 0.6.4 → 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. data/README.rdoc +1 -1
  2. data/VERSION.yml +1 -1
  3. data/barometer.gemspec +9 -2
  4. data/bin/barometer +1 -1
  5. data/lib/barometer/data/geo.rb +12 -1
  6. data/lib/barometer/formats/coordinates.rb +1 -1
  7. data/lib/barometer/query.rb +1 -1
  8. data/spec/barometer_spec.rb +13 -6
  9. data/spec/data/distance_spec.rb +1 -1
  10. data/spec/data/geo_spec.rb +1 -1
  11. data/spec/data/local_datetime_spec.rb +1 -1
  12. data/spec/data/local_time_spec.rb +1 -1
  13. data/spec/data/location_spec.rb +1 -1
  14. data/spec/data/pressure_spec.rb +1 -1
  15. data/spec/data/speed_spec.rb +1 -1
  16. data/spec/data/sun_spec.rb +1 -1
  17. data/spec/data/temperature_spec.rb +1 -1
  18. data/spec/data/units_spec.rb +1 -1
  19. data/spec/data/zone_spec.rb +2 -2
  20. data/spec/fixtures/geocode/40_73.json +173 -0
  21. data/spec/fixtures/geocode/90210.json +41 -0
  22. data/spec/fixtures/geocode/T5B4M9.json +35 -0
  23. data/spec/fixtures/geocode/atlanta.json +38 -0
  24. data/spec/fixtures/geocode/calgary_ab.json +38 -0
  25. data/spec/fixtures/geocode/ksfo.json +42 -0
  26. data/spec/fixtures/geocode/newyork_ny.json +38 -0
  27. data/spec/formats/coordinates_spec.rb +31 -31
  28. data/spec/formats/format_spec.rb +1 -1
  29. data/spec/formats/geocode_spec.rb +2 -2
  30. data/spec/formats/icao_spec.rb +1 -1
  31. data/spec/formats/postalcode_spec.rb +1 -1
  32. data/spec/formats/short_zipcode_spec.rb +1 -1
  33. data/spec/formats/weather_id_spec.rb +1 -1
  34. data/spec/formats/zipcode_spec.rb +1 -1
  35. data/spec/measurements/measurement_spec.rb +14 -14
  36. data/spec/measurements/result_array_spec.rb +1 -1
  37. data/spec/measurements/result_spec.rb +1 -1
  38. data/spec/query_spec.rb +3 -3
  39. data/spec/spec_helper.rb +31 -52
  40. data/spec/weather_services/google_spec.rb +4 -4
  41. data/spec/weather_services/services_spec.rb +2 -2
  42. data/spec/weather_services/weather_bug_spec.rb +6 -6
  43. data/spec/weather_services/weather_dot_com_spec.rb +3 -3
  44. data/spec/weather_services/wunderground_spec.rb +5 -5
  45. data/spec/weather_services/yahoo_spec.rb +3 -3
  46. data/spec/weather_spec.rb +15 -15
  47. data/spec/web_services/geocode_spec.rb +1 -1
  48. data/spec/web_services/web_services_spec.rb +1 -1
  49. metadata +9 -2
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "T5B 4M9",
3
+ "Status": {
4
+ "code": 200,
5
+ "request": "geocode"
6
+ },
7
+ "Placemark": [ {
8
+ "id": "p1",
9
+ "address": "Alberta T5B 4M9, Canada",
10
+ "AddressDetails": {
11
+ "Accuracy" : 5,
12
+ "Country" : {
13
+ "AdministrativeArea" : {
14
+ "AdministrativeAreaName" : "AB",
15
+ "PostalCode" : {
16
+ "PostalCodeNumber" : "T5B 4M9"
17
+ }
18
+ },
19
+ "CountryName" : "Canada",
20
+ "CountryNameCode" : "CA"
21
+ }
22
+ },
23
+ "ExtendedData": {
24
+ "LatLonBox": {
25
+ "north": 53.5735946,
26
+ "south": 53.5672994,
27
+ "east": -113.4529354,
28
+ "west": -113.4592306
29
+ }
30
+ },
31
+ "Point": {
32
+ "coordinates": [ -113.4560830, 53.5704470, 0 ]
33
+ }
34
+ } ]
35
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "Atlanta, GA, US",
3
+ "Status": {
4
+ "code": 200,
5
+ "request": "geocode"
6
+ },
7
+ "Placemark": [ {
8
+ "id": "p1",
9
+ "address": "Atlanta, GA, USA",
10
+ "AddressDetails": {
11
+ "Accuracy" : 4,
12
+ "Country" : {
13
+ "AdministrativeArea" : {
14
+ "AdministrativeAreaName" : "GA",
15
+ "SubAdministrativeArea" : {
16
+ "Locality" : {
17
+ "LocalityName" : "Atlanta"
18
+ },
19
+ "SubAdministrativeAreaName" : "Fulton"
20
+ }
21
+ },
22
+ "CountryName" : "USA",
23
+ "CountryNameCode" : "US"
24
+ }
25
+ },
26
+ "ExtendedData": {
27
+ "LatLonBox": {
28
+ "north": 33.8231844,
29
+ "south": 33.6747422,
30
+ "east": -84.2599230,
31
+ "west": -84.5160418
32
+ }
33
+ },
34
+ "Point": {
35
+ "coordinates": [ -84.3879824, 33.7489954, 0 ]
36
+ }
37
+ } ]
38
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "Calgary,AB",
3
+ "Status": {
4
+ "code": 200,
5
+ "request": "geocode"
6
+ },
7
+ "Placemark": [ {
8
+ "id": "p1",
9
+ "address": "Calgary, AB, Canada",
10
+ "AddressDetails": {
11
+ "Accuracy" : 4,
12
+ "Country" : {
13
+ "AdministrativeArea" : {
14
+ "AdministrativeAreaName" : "AB",
15
+ "SubAdministrativeArea" : {
16
+ "Locality" : {
17
+ "LocalityName" : "Calgary"
18
+ },
19
+ "SubAdministrativeAreaName" : "Division No. 6"
20
+ }
21
+ },
22
+ "CountryName" : "Canada",
23
+ "CountryNameCode" : "CA"
24
+ }
25
+ },
26
+ "ExtendedData": {
27
+ "LatLonBox": {
28
+ "north": 51.1672307,
29
+ "south": 50.9427954,
30
+ "east": -113.8063192,
31
+ "west": -114.3185568
32
+ }
33
+ },
34
+ "Point": {
35
+ "coordinates": [ -114.0624380, 51.0551490, 0 ]
36
+ }
37
+ } ]
38
+ }
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "KSFO",
3
+ "Status": {
4
+ "code": 200,
5
+ "request": "geocode"
6
+ },
7
+ "Placemark": [ {
8
+ "id": "p1",
9
+ "address": "San Francisco International Airport, Millbrae, CA 94030, USA",
10
+ "AddressDetails": {
11
+ "Accuracy" : 9,
12
+ "Country" : {
13
+ "AdministrativeArea" : {
14
+ "AdministrativeAreaName" : "CA",
15
+ "SubAdministrativeArea" : {
16
+ "Locality" : {
17
+ "AddressLine" : [ "San Francisco International Airport" ],
18
+ "LocalityName" : "Millbrae",
19
+ "PostalCode" : {
20
+ "PostalCodeNumber" : "94030"
21
+ }
22
+ },
23
+ "SubAdministrativeAreaName" : "San Mateo"
24
+ }
25
+ },
26
+ "CountryName" : "USA",
27
+ "CountryNameCode" : "US"
28
+ }
29
+ },
30
+ "ExtendedData": {
31
+ "LatLonBox": {
32
+ "north": 37.6392104,
33
+ "south": 37.6043769,
34
+ "east": -122.3549471,
35
+ "west": -122.4014108
36
+ }
37
+ },
38
+ "Point": {
39
+ "coordinates": [ -122.3899790, 37.6152230, 0 ]
40
+ }
41
+ } ]
42
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "New York, NY",
3
+ "Status": {
4
+ "code": 200,
5
+ "request": "geocode"
6
+ },
7
+ "Placemark": [ {
8
+ "id": "p1",
9
+ "address": "New York, NY, USA",
10
+ "AddressDetails": {
11
+ "Accuracy" : 4,
12
+ "Country" : {
13
+ "AdministrativeArea" : {
14
+ "AdministrativeAreaName" : "NY",
15
+ "SubAdministrativeArea" : {
16
+ "Locality" : {
17
+ "LocalityName" : "New York"
18
+ },
19
+ "SubAdministrativeAreaName" : "New York"
20
+ }
21
+ },
22
+ "CountryName" : "USA",
23
+ "CountryNameCode" : "US"
24
+ }
25
+ },
26
+ "ExtendedData": {
27
+ "LatLonBox": {
28
+ "north": 40.8494506,
29
+ "south": 40.5788125,
30
+ "east": -73.7498541,
31
+ "west": -74.2620917
32
+ }
33
+ },
34
+ "Point": {
35
+ "coordinates": [ -74.0059729, 40.7142691, 0 ]
36
+ }
37
+ } ]
38
+ }
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Coordinates" do
4
4
 
@@ -16,28 +16,28 @@ describe "Query::Coordinates" do
16
16
  describe "and class methods" do
17
17
 
18
18
  it "returns a format" do
19
- Query::Format::Coordinates.format.should == :coordinates
19
+ Barometer::Query::Format::Coordinates.format.should == :coordinates
20
20
  end
21
21
 
22
22
  it "returns a country" do
23
- Query::Format::Coordinates.country_code.should be_nil
23
+ Barometer::Query::Format::Coordinates.country_code.should be_nil
24
24
  end
25
25
 
26
26
  it "returns a regex" do
27
- Query::Format::Coordinates.regex.should_not be_nil
28
- Query::Format::Coordinates.regex.is_a?(Regexp).should be_true
27
+ Barometer::Query::Format::Coordinates.regex.should_not be_nil
28
+ Barometer::Query::Format::Coordinates.regex.is_a?(Regexp).should be_true
29
29
  end
30
30
 
31
31
  it "returns the convertable_formats" do
32
- Query::Format::Coordinates.convertable_formats.should_not be_nil
33
- Query::Format::Coordinates.convertable_formats.is_a?(Array).should be_true
34
- Query::Format::Coordinates.convertable_formats.include?(:short_zipcode).should be_true
35
- Query::Format::Coordinates.convertable_formats.include?(:zipcode).should be_true
36
- Query::Format::Coordinates.convertable_formats.include?(:postalcode).should be_true
37
- Query::Format::Coordinates.convertable_formats.include?(:weather_id).should be_true
38
- Query::Format::Coordinates.convertable_formats.include?(:coordinates).should be_true
39
- Query::Format::Coordinates.convertable_formats.include?(:icao).should be_true
40
- Query::Format::Coordinates.convertable_formats.include?(:geocode).should be_true
32
+ Barometer::Query::Format::Coordinates.convertable_formats.should_not be_nil
33
+ Barometer::Query::Format::Coordinates.convertable_formats.is_a?(Array).should be_true
34
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:short_zipcode).should be_true
35
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:zipcode).should be_true
36
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:postalcode).should be_true
37
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:weather_id).should be_true
38
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:coordinates).should be_true
39
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:icao).should be_true
40
+ Barometer::Query::Format::Coordinates.convertable_formats.include?(:geocode).should be_true
41
41
  end
42
42
 
43
43
  describe "is?," do
@@ -48,11 +48,11 @@ describe "Query::Coordinates" do
48
48
  end
49
49
 
50
50
  it "recognizes a valid format" do
51
- Query::Format::Coordinates.is?(@valid).should be_true
51
+ Barometer::Query::Format::Coordinates.is?(@valid).should be_true
52
52
  end
53
53
 
54
54
  it "recognizes non-valid format" do
55
- Query::Format::Coordinates.is?(@invalid).should be_false
55
+ Barometer::Query::Format::Coordinates.is?(@invalid).should be_false
56
56
  end
57
57
 
58
58
  end
@@ -60,22 +60,22 @@ describe "Query::Coordinates" do
60
60
  describe "when converting using 'to'," do
61
61
 
62
62
  it "requires a Query::Format object" do
63
- lambda { Query::Format::Coordinates.to }.should raise_error(ArgumentError)
64
- lambda { Query::Format::Coordinates.to("invalid") }.should raise_error(ArgumentError)
63
+ lambda { Barometer::Query::Format::Coordinates.to }.should raise_error(ArgumentError)
64
+ lambda { Barometer::Query::Format::Coordinates.to("invalid") }.should raise_error(ArgumentError)
65
65
  query = Barometer::Query.new(@zipcode)
66
66
  query.is_a?(Barometer::Query).should be_true
67
- lambda { Query::Format::Coordinates.to(original_query) }.should_not raise_error(ArgumentError)
67
+ lambda { Barometer::Query::Format::Coordinates.to(original_query) }.should_not raise_error(ArgumentError)
68
68
  end
69
69
 
70
70
  it "returns a Barometer::Query" do
71
71
  query = Barometer::Query.new(@short_zipcode)
72
- Query::Format::Coordinates.to(query).is_a?(Barometer::Query).should be_true
72
+ Barometer::Query::Format::Coordinates.to(query).is_a?(Barometer::Query).should be_true
73
73
  end
74
74
 
75
75
  it "converts from short_zipcode" do
76
76
  query = Barometer::Query.new(@short_zipcode)
77
77
  query.format.should == :short_zipcode
78
- new_query = Query::Format::Coordinates.to(query)
78
+ new_query = Barometer::Query::Format::Coordinates.to(query)
79
79
  new_query.q.should == "34.1030032,-118.4104684"
80
80
  new_query.country_code.should == "US"
81
81
  new_query.format.should == :coordinates
@@ -86,7 +86,7 @@ describe "Query::Coordinates" do
86
86
  query = Barometer::Query.new(@zipcode)
87
87
  query.format = :zipcode
88
88
  query.format.should == :zipcode
89
- new_query = Query::Format::Coordinates.to(query)
89
+ new_query = Barometer::Query::Format::Coordinates.to(query)
90
90
  new_query.q.should == "34.1030032,-118.4104684"
91
91
  new_query.country_code.should == "US"
92
92
  new_query.format.should == :coordinates
@@ -96,8 +96,8 @@ describe "Query::Coordinates" do
96
96
  it "converts from weather_id" do
97
97
  query = Barometer::Query.new(@weather_id)
98
98
  query.format.should == :weather_id
99
- new_query = Query::Format::Coordinates.to(query)
100
- new_query.q.should == "33.754487,-84.389663"
99
+ new_query = Barometer::Query::Format::Coordinates.to(query)
100
+ new_query.q.should == "33.7489954,-84.3879824"
101
101
  new_query.country_code.should == "US"
102
102
  new_query.format.should == :coordinates
103
103
  new_query.geo.should_not be_nil
@@ -106,8 +106,8 @@ describe "Query::Coordinates" do
106
106
  it "converts from geocode" do
107
107
  query = Barometer::Query.new(@geocode)
108
108
  query.format.should == :geocode
109
- new_query = Query::Format::Coordinates.to(query)
110
- new_query.q.should == "40.756054,-73.986951"
109
+ new_query = Barometer::Query::Format::Coordinates.to(query)
110
+ new_query.q.should == "40.7142691,-74.0059729"
111
111
  new_query.country_code.should == "US"
112
112
  new_query.format.should == :coordinates
113
113
  new_query.geo.should_not be_nil
@@ -116,7 +116,7 @@ describe "Query::Coordinates" do
116
116
  it "converts from postal_code" do
117
117
  query = Barometer::Query.new(@postal_code)
118
118
  query.format.should == :postalcode
119
- new_query = Query::Format::Coordinates.to(query)
119
+ new_query = Barometer::Query::Format::Coordinates.to(query)
120
120
  new_query.q.should == "53.570447,-113.456083"
121
121
  new_query.country_code.should == "CA"
122
122
  new_query.format.should == :coordinates
@@ -126,7 +126,7 @@ describe "Query::Coordinates" do
126
126
  it "converts from icao" do
127
127
  query = Barometer::Query.new(@icao)
128
128
  query.format.should == :icao
129
- new_query = Query::Format::Coordinates.to(query)
129
+ new_query = Barometer::Query::Format::Coordinates.to(query)
130
130
  new_query.q.should == "37.615223,-122.389979"
131
131
  new_query.country_code.should == "US"
132
132
  new_query.format.should == :coordinates
@@ -136,8 +136,8 @@ describe "Query::Coordinates" do
136
136
  it "returns nil for other formats" do
137
137
  query = Barometer::Query.new(@coordinates)
138
138
  query.format.should == :coordinates
139
- new_query = Query::Format::Coordinates.to(query)
140
- new_query.q.should == "40.756201,-73.986513"
139
+ new_query = Barometer::Query::Format::Coordinates.to(query)
140
+ new_query.q.should == "40.7560561,-73.9869562"
141
141
  new_query.country_code.should == "US"
142
142
  new_query.format.should == :coordinates
143
143
  new_query.geo.should_not be_nil
@@ -147,7 +147,7 @@ describe "Query::Coordinates" do
147
147
  Barometer.google_geocode_key = nil
148
148
  Barometer.google_geocode_key.should be_nil
149
149
  query = Barometer::Query.new(@short_zipcode)
150
- Query::Format::Coordinates.to(query).q.should be_nil
150
+ Barometer::Query::Format::Coordinates.to(query).q.should be_nil
151
151
  Barometer.google_geocode_key = KEY
152
152
  end
153
153
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Format" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Geocode" do
4
4
 
@@ -110,7 +110,7 @@ describe "Query::Geocode" do
110
110
  query = Barometer::Query.new(@icao)
111
111
  query.format.should == :icao
112
112
  new_query = Query::Format::Geocode.to(query)
113
- new_query.q.should == "San Francisco Airport, USA"
113
+ new_query.q.should == "Millbrae, CA, USA"
114
114
  new_query.country_code.should == "US"
115
115
  new_query.format.should == :geocode
116
116
  new_query.geo.should_not be_nil
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Icao" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Postalcode" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::ShortZipcode" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::WeatherID" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Query::Zipcode" do
4
4
 
@@ -1,4 +1,4 @@
1
- require 'spec_helper'
1
+ require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
2
2
 
3
3
  describe "Measurement" do
4
4
 
@@ -101,21 +101,21 @@ describe "Measurement" do
101
101
 
102
102
  it "only accepts Measurement::Result for current" do
103
103
  invalid_data = "invalid"
104
- invalid_data.class.should_not == Measurement::Result
104
+ invalid_data.class.should_not == Barometer::Measurement::Result
105
105
  lambda { @measurement.current = invalid_data }.should raise_error(ArgumentError)
106
106
 
107
- valid_data = Measurement::Result.new
108
- valid_data.class.should == Measurement::Result
107
+ valid_data = Barometer::Measurement::Result.new
108
+ valid_data.class.should == Barometer::Measurement::Result
109
109
  lambda { @measurement.current = valid_data }.should_not raise_error(ArgumentError)
110
110
  end
111
111
 
112
112
  it "only accepts Data::ResultArray for forecast" do
113
113
  invalid_data = 1
114
- invalid_data.class.should_not == Measurement::ResultArray
114
+ invalid_data.class.should_not == Barometer::Measurement::ResultArray
115
115
  lambda { @measurement.forecast = invalid_data }.should raise_error(ArgumentError)
116
116
 
117
- valid_data = Measurement::ResultArray.new
118
- valid_data.class.should == Measurement::ResultArray
117
+ valid_data = Barometer::Measurement::ResultArray.new
118
+ valid_data.class.should == Barometer::Measurement::ResultArray
119
119
  lambda { @measurement.forecast = valid_data }.should_not raise_error(ArgumentError)
120
120
  end
121
121
 
@@ -194,7 +194,7 @@ describe "Measurement" do
194
194
  @measurement.current.should be_nil
195
195
  @measurement.success.should be_false
196
196
 
197
- @measurement.current = Measurement::Result.new
197
+ @measurement.current = Barometer::Measurement::Result.new
198
198
  @measurement.current.temperature = Data::Temperature.new
199
199
  @measurement.current.temperature.c = 10
200
200
  @measurement.utc_time_stamp.should_not be_nil
@@ -203,7 +203,7 @@ describe "Measurement" do
203
203
  end
204
204
 
205
205
  it "returns successful state" do
206
- @measurement.current = Measurement::Result.new
206
+ @measurement.current = Barometer::Measurement::Result.new
207
207
  @measurement.current.temperature = Data::Temperature.new
208
208
  @measurement.current.temperature.c = 10
209
209
  @measurement.success!
@@ -226,7 +226,7 @@ describe "Measurement" do
226
226
  @measurement.current.should be_nil
227
227
  @measurement.current?.should be_true
228
228
 
229
- @measurement.current = Measurement::Result.new
229
+ @measurement.current = Barometer::Measurement::Result.new
230
230
  @measurement.current.current_at.should be_nil
231
231
  @measurement.current?.should be_true
232
232
 
@@ -290,9 +290,9 @@ describe "Measurement" do
290
290
 
291
291
  # create a measurement object with a result array that includes
292
292
  # dates for 4 consecutive days starting with tommorrow
293
- @measurement.forecast = Measurement::ResultArray.new
293
+ @measurement.forecast = Barometer::Measurement::ResultArray.new
294
294
  1.upto(4) do |i|
295
- forecast_measurement = Measurement::Result.new
295
+ forecast_measurement = Barometer::Measurement::Result.new
296
296
  forecast_measurement.date = Date.parse((Time.now + (i * 60 * 60 * 24)).to_s)
297
297
  @measurement.forecast << forecast_measurement
298
298
  end
@@ -302,7 +302,7 @@ describe "Measurement" do
302
302
  end
303
303
 
304
304
  it "returns nil when there are no forecasts" do
305
- @measurement.forecast = Measurement::ResultArray.new
305
+ @measurement.forecast = Barometer::Measurement::ResultArray.new
306
306
  @measurement.forecast.size.should == 0
307
307
  @measurement.for.should be_nil
308
308
  end
@@ -348,7 +348,7 @@ describe "Measurement" do
348
348
 
349
349
  before(:each) do
350
350
  @measurement = Barometer::Measurement.new(:wunderground)
351
- @measurement.current = Measurement::Result.new
351
+ @measurement.current = Barometer::Measurement::Result.new
352
352
  @now = Data::LocalDateTime.parse("2009-05-01 2:05 pm")
353
353
  end
354
354