eaternet 0.3.11 → 0.3.12

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 11257485cfdcbda82f42ae36c646ebfc9616203d
4
- data.tar.gz: 9f3bbe15c16db1bc73527cbb25aa69c4421ea630
3
+ metadata.gz: cfd0797bd4c249ea732ef25485f7310ee1d9ba74
4
+ data.tar.gz: 84e4914a0375a5c84b1d8a48196a19a4f06053ec
5
5
  SHA512:
6
- metadata.gz: 9bd3634278276974dcecb89c48f1285ff652245dd968d8be8349b7063b853c8acfa6af8c5e7fcea6bb2703c50e4dcd3e2cf993784ff5eff92578f8f327de9e4c
7
- data.tar.gz: 2484a3f65c718bd457b6ee56a6c40bb64aa4326950723ee59198c1b05f52f336bde72f3b1035c246638bb606483023c832a81573d5c0bb48e9ae47e4a47cbbf4
6
+ metadata.gz: 12ba76b4684ae01e31e2b97a8e0b18c6cace0a7cebc381ef4550a8ad13d03784982f8f660064de0f849caac930db81365b65f6d096e77302471deff71d041611
7
+ data.tar.gz: 5b207a3fb1c0520bf9721a156a36dc008ac1abfa3c592ede5a57548a9f6e18ca5024f440fa6664ff86c23a93b3cd1e5d7735419bc6b728f4cf4c4e1a5b0679df
@@ -25,6 +25,7 @@ Gem::Specification.new do |spec|
25
25
  spec.add_development_dependency 'minitest'
26
26
  spec.add_development_dependency 'pry'
27
27
  spec.add_development_dependency 'rake', '~> 10'
28
+ spec.add_development_dependency 'timecop'
28
29
  spec.add_development_dependency 'vcr'
29
30
  spec.add_development_dependency 'webmock'
30
31
 
@@ -1,7 +1,6 @@
1
1
  require 'csv'
2
2
  require 'json'
3
3
  require 'logger'
4
- require 'open-uri'
5
4
  require 'set'
6
5
  require 'tempfile'
7
6
 
@@ -14,8 +13,9 @@ require 'eaternet/loggable'
14
13
  module Eaternet
15
14
  module Agencies
16
15
  # A data source for New York City food service health inspections. It
17
- # retrieves the latest info from the official source and makes it easy
18
- # to work with.
16
+ # retrieves the latest info from
17
+ # [the official source](https://data.cityofnewyork.us/Health/DOHMH-New-York-City-Restaurant-Inspection-Results/xx67-kt59)
18
+ # and makes it easy to work with.
19
19
  #
20
20
  # This library conforms to the
21
21
  # [LIVES 1.0 standard](http://www.yelp.com/healthscores) developed by
@@ -66,7 +66,6 @@ module Eaternet
66
66
  end
67
67
 
68
68
  # @example Compute the average inspection score for NYC.
69
- #
70
69
  # # The library is optimized for memory use at the expense
71
70
  # # of speed. E.g., each call to #inspections will iterate
72
71
  # # through the raw CSV. So here, we first retrieve the
@@ -104,6 +103,7 @@ module Eaternet
104
103
  #
105
104
  # @return [FeedInfo]
106
105
  def feed_info
106
+ # Anyone know a contact email?
107
107
  FeedInfo.new do |fi|
108
108
  fi.feed_date = Date.today
109
109
  fi.feed_version = '1.0'
@@ -233,29 +233,17 @@ module Eaternet
233
233
  .map { |row| block.call(row) }
234
234
  end
235
235
 
236
- def unique(objects)
237
- Set.new(objects)
238
- end
239
-
240
236
  def table_file
241
- if @table_file.nil?
242
- @table_file = Tempfile.new('all.csv.')
243
- Nyc.download_to(@table_file)
244
- end
245
- @table_file
246
- end
247
-
248
- def self.download_to(a_file)
249
- download_via_url(a_file)
237
+ @table_file ||= Nyc.download_via_url
250
238
  end
251
239
 
252
240
  # Fastest method for downloading all data but may be non-standard
253
241
  # for Socrata.
254
- def self.download_via_url(a_file)
255
- a_file.write(open(csv_url).read)
242
+ def self.download_via_url
243
+ Eaternet::Util.download_and_cache(source: csv_url, dest: Tempfile.new('nyc'))
256
244
  end
257
245
 
258
- def csv_url
246
+ def self.csv_url
259
247
  domain = 'data.cityofnewyork.us'
260
248
  dataset = 'xx67-kt59'
261
249
  "https://#{domain}/api/views/#{dataset}/rows.csv?accessType=DOWNLOAD"
@@ -12,6 +12,7 @@ module Eaternet
12
12
  logger = Logger.new(ENV['EATERNET_LOG_FILE'] || $stderr)
13
13
  logger.datetime_format = '%Y-%m-%d %H:%M:%S'
14
14
  logger.progname = 'Eaternet'
15
+ logger.level = Logger::ERROR
15
16
  logger
16
17
  end
17
18
  end
@@ -1,3 +1,4 @@
1
+ require 'base64'
1
2
  require 'open-uri'
2
3
  require 'zip'
3
4
 
@@ -22,12 +23,24 @@ module Eaternet
22
23
  dir
23
24
  end
24
25
 
26
+ # Download a file from the network, caching it for 12 hours.
27
+ #
28
+ # @param [String] source the URL to retrieve
29
+ # @param [String] dest pathname in which to save the file
30
+ def self.download_and_cache(source:, dest:)
31
+ cache_path = generate_cache_path(source)
32
+ download(source: source, dest: cache_path) if expired? cache_path
33
+ FileUtils.cp cache_path, dest
34
+ File.open dest
35
+ end
36
+
25
37
  # Download a file from the network.
26
38
  #
27
39
  # @param [String] source the URL to retrieve
28
40
  # @param [String] dest pathname in which to save the file
41
+ # @return [File] the file
29
42
  def self.download(source:, dest:)
30
- open(dest, 'wb') { |file| file << open(source).read }
43
+ File.open(dest, 'wb') { |file| file << open(source).read }
31
44
  end
32
45
 
33
46
  # Extract a Zip archive.
@@ -56,5 +69,29 @@ module Eaternet
56
69
  return nil if a_string.nil?
57
70
  a_string.strip.gsub(/ +/, ' ')
58
71
  end
72
+
73
+ def self.file_age_in_days(path)
74
+ (Time.now - File.mtime(path)).to_i / 86_400.0
75
+ end
76
+
77
+ #
78
+ # private
79
+ #
80
+
81
+ def self.expired?(cache_path)
82
+ !File.exist?(cache_path) || file_age_in_days(cache_path) > 0.5
83
+ end
84
+
85
+ def self.generate_cache_path(url)
86
+ cache_dir = prepare_cache_dir
87
+ cache_key = Base64.strict_encode64(url)
88
+ File.join(cache_dir, cache_key)
89
+ end
90
+
91
+ def self.prepare_cache_dir
92
+ cache_dir = '/tmp/eaternet'
93
+ `mkdir -p #{cache_dir}`
94
+ cache_dir
95
+ end
59
96
  end
60
97
  end
@@ -1,3 +1,3 @@
1
1
  module Eaternet
2
- VERSION = '0.3.11'
2
+ VERSION = '0.3.12'
3
3
  end
@@ -9,7 +9,7 @@ class LoggableTest < Minitest::Test
9
9
 
10
10
  def test_creates_and_sends_log_messages_to_stderr
11
11
  assert_output(nil, /Border Collie/) do
12
- logger.info('Border Collie')
12
+ logger.error('Border Collie')
13
13
  end
14
14
  end
15
15
  end
@@ -0,0 +1,47 @@
1
+ require 'tempfile'
2
+ require 'test_helper'
3
+ require 'timecop'
4
+
5
+ require 'eaternet/util'
6
+
7
+ class UtilTest < Minitest::Test
8
+
9
+ def setup
10
+ @file_contents = 'This is the file'
11
+ @url = "http://downloadtest.com/file-#{rand(1_000_000)}.txt"
12
+ @file = Tempfile.new('temp')
13
+ stub_request(:get, @url).to_return(body: @file_contents)
14
+ end
15
+
16
+ def download
17
+ Eaternet::Util.download(source: @url, dest: @file)
18
+ end
19
+
20
+ def download_and_cache
21
+ Eaternet::Util.download_and_cache(source: @url, dest: @file)
22
+ end
23
+
24
+ def test_downloads_a_file
25
+ download
26
+ assert_equal @file_contents, `cat #{@file.path}`.strip
27
+ assert_requested :get, @url, times: 1
28
+ end
29
+
30
+ def test_downloads_a_file_with_cache_option
31
+ download_and_cache
32
+ assert_equal @file_contents, `cat #{@file.path}`.strip
33
+ assert_requested :get, @url, times: 1
34
+ end
35
+
36
+ def test_uses_a_file_cache
37
+ (1..3).each { download_and_cache }
38
+ assert_requested :get, @url, times: 1
39
+ end
40
+
41
+ def test_expires_a_cache_file
42
+ thirteen_hours = 13 * 60 * 60
43
+ download_and_cache
44
+ Timecop.freeze(Time.now + thirteen_hours) { download_and_cache }
45
+ assert_requested :get, @url, times: 2
46
+ end
47
+ end
@@ -1,13 +1,14 @@
1
1
  require 'minitest/autorun'
2
2
  require 'pry'
3
+ require 'webmock/minitest'
3
4
  require 'vcr'
4
5
 
5
6
  VCR.configure do |c|
6
7
  c.cassette_library_dir = 'test/vcr_cassettes'
7
8
  c.hook_into :webmock
9
+ c.ignore_hosts 'downloadtest.com'
8
10
  end
9
11
 
10
-
11
12
  #
12
13
  # Testing helpers
13
14
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: eaternet
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.11
4
+ version: 0.3.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robb Shecter
@@ -94,6 +94,20 @@ dependencies:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '10'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
97
111
  - !ruby/object:Gem::Dependency
98
112
  name: vcr
99
113
  requirement: !ruby/object:Gem::Requirement
@@ -194,6 +208,7 @@ files:
194
208
  - test/eaternet/lives_1_0/inspection_test.rb
195
209
  - test/eaternet/lives_1_0/legend_test.rb
196
210
  - test/eaternet/loggable_test.rb
211
+ - test/eaternet/util_test.rb
197
212
  - test/fixtures/morris-park-bake-shop.csv
198
213
  - test/script.rb
199
214
  - test/test_helper.rb
@@ -230,6 +245,7 @@ test_files:
230
245
  - test/eaternet/lives_1_0/inspection_test.rb
231
246
  - test/eaternet/lives_1_0/legend_test.rb
232
247
  - test/eaternet/loggable_test.rb
248
+ - test/eaternet/util_test.rb
233
249
  - test/fixtures/morris-park-bake-shop.csv
234
250
  - test/script.rb
235
251
  - test/test_helper.rb