eaternet 0.2.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.
@@ -0,0 +1,38 @@
1
+ require 'open-uri'
2
+ require 'zip'
3
+
4
+ module Eaternet
5
+ module Util
6
+ # A utility function to download a zip file,
7
+ # extract it into a temp directory.
8
+ #
9
+ # @return [String] the directory path
10
+ def self.download_and_extract_zipfile(url)
11
+ dir = make_temp_dir
12
+ zip_path = File.join(dir, 'zip-file.zip')
13
+ download(source: url, dest: zip_path)
14
+ extract_zipfile(path: zip_path, dest_dir: dir)
15
+ dir
16
+ end
17
+
18
+ # Create a temporary directory which is automatically
19
+ # deleted when the program exits.
20
+ def self.make_temp_dir
21
+ dir = Dir.mktmpdir
22
+ at_exit { FileUtils.remove_entry dir }
23
+ dir
24
+ end
25
+
26
+ def self.download(source:, dest:)
27
+ open(dest, 'wb') { |file| file << open(source).read }
28
+ end
29
+
30
+ def self.extract_zipfile(path:, dest_dir:)
31
+ open(path) do |zip_file|
32
+ Zip::File.open(zip_file, 'rb') do |zip_data|
33
+ zip_data.each { |entry| entry.extract(File.join(dest_dir, entry.name)) }
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Eaternet
2
+ VERSION = '0.2.0'
3
+ end
data/lib/ext/lazy.rb ADDED
@@ -0,0 +1,13 @@
1
+ class Enumerator
2
+ class Lazy
3
+ # Create a lazy compact similar to Array#compact.
4
+ def compact
5
+ select { |i| !i.nil? }
6
+ end
7
+
8
+ # Return only my unique elements
9
+ def unique
10
+ Set.new(self).each
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ CAMIS,DBA,BORO,BUILDING,STREET,ZIPCODE,PHONE,CUISINE DESCRIPTION,INSPECTION DATE,ACTION,VIOLATION CODE,VIOLATION DESCRIPTION,CRITICAL FLAG,SCORE,GRADE,GRADE DATE,RECORD DATE,INSPECTION TYPE
2
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,02/09/2015,Violations were cited in the following area(s).,06C,"Food not protected from potential source of contamination during storage, preparation, transportation, display or service.",Critical,6,A,02/09/2015,03/06/2015,Cycle Inspection / Initial Inspection
3
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,03/03/2014,Violations were cited in the following area(s).,10F,"Non-food contact surface improperly constructed. Unacceptable material used. Non-food contact surface or equipment improperly maintained and/or not properly sealed, raised, spaced or movable to allow accessibility for cleaning on all sides, above and underneath the unit.",Not Critical,2,A,03/03/2014,03/06/2015,Cycle Inspection / Initial Inspection
4
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,10/10/2013,No violations were recorded at the time of this inspection.,,,Not Applicable,,,,03/06/2015,Trans Fat / Second Compliance Inspection
5
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,09/11/2013,Violations were cited in the following area(s).,04L,Evidence of mice or live mice present in facility's food and/or non-food areas.,Critical,6,A,09/11/2013,03/06/2015,Cycle Inspection / Re-inspection
6
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,09/11/2013,Violations were cited in the following area(s).,04N,"Filth flies or food/refuse/sewage-associated (FRSA) flies present in facilitys food and/or non-food areas. Filth flies include house flies, little house flies, blow flies, bottle flies and flesh flies. Food/refuse/sewage-associated flies include fruit flies, drain flies and Phorid flies.",Critical,6,A,09/11/2013,03/06/2015,Cycle Inspection / Re-inspection
7
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,04C,Food worker does not use proper utensil to eliminate bare hand contact with food that will not receive adequate additional heat treatment.,Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
8
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,04L,Evidence of mice or live mice present in facility's food and/or non-food areas.,Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
9
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,06A,Personal cleanliness inadequate. Outer garment soiled with possible contaminant. Effective hair restraint not worn in an area where food is prepared.,Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
10
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,06C,"Food not protected from potential source of contamination during storage, preparation, transportation, display or service.",Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
11
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,08A,Facility not vermin proof. Harborage or conditions conducive to attracting vermin to the premises and/or allowing vermin to exist.,Not Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
12
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,08/14/2013,Violations were cited in the following area(s).,10F,"Non-food contact surface improperly constructed. Unacceptable material used. Non-food contact surface or equipment improperly maintained and/or not properly sealed, raised, spaced or movable to allow accessibility for cleaning on all sides, above and underneath the unit.",Not Critical,32,,,03/06/2015,Cycle Inspection / Initial Inspection
13
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,06/01/2013,Violations were cited in the following area(s).,16B,"The original nutritional fact labels and/or ingredient label for a cooking oil, shortening or margarine or food item sold in bulk, or acceptable manufacturers documentation not maintained on site.",Not Critical,,,,03/06/2015,Trans Fat / Compliance Inspection
14
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,01/24/2013,Violations were cited in the following area(s).,02G,Cold food item held above 41º F (smoked fish and reduced oxygen packaged foods above 38 ºF) except during necessary preparation.,Critical,10,A,01/24/2013,03/06/2015,Cycle Inspection / Re-inspection
15
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,01/24/2013,Violations were cited in the following area(s).,10F,"Non-food contact surface improperly constructed. Unacceptable material used. Non-food contact surface or equipment improperly maintained and/or not properly sealed, raised, spaced or movable to allow accessibility for cleaning on all sides, above and underneath the unit.",Not Critical,10,A,01/24/2013,03/06/2015,Cycle Inspection / Re-inspection
16
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,01/24/2013,No violations were recorded at the time of this inspection.,,,Not Applicable,,,,03/06/2015,Trans Fat / Re-inspection
17
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,02G,Cold food item held above 41º F (smoked fish and reduced oxygen packaged foods above 38 ºF) except during necessary preparation.,Critical,25,,,03/06/2015,Cycle Inspection / Initial Inspection
18
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,04L,Evidence of mice or live mice present in facility's food and/or non-food areas.,Critical,25,,,03/06/2015,Cycle Inspection / Initial Inspection
19
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,06C,"Food not protected from potential source of contamination during storage, preparation, transportation, display or service.",Critical,25,,,03/06/2015,Cycle Inspection / Initial Inspection
20
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,08A,Facility not vermin proof. Harborage or conditions conducive to attracting vermin to the premises and/or allowing vermin to exist.,Not Critical,25,,,03/06/2015,Cycle Inspection / Initial Inspection
21
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,10F,"Non-food contact surface improperly constructed. Unacceptable material used. Non-food contact surface or equipment improperly maintained and/or not properly sealed, raised, spaced or movable to allow accessibility for cleaning on all sides, above and underneath the unit.",Not Critical,25,,,03/06/2015,Cycle Inspection / Initial Inspection
22
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,12/31/2012,Violations were cited in the following area(s).,16B,"The original nutritional fact labels and/or ingredient label for a cooking oil, shortening or margarine or food item sold in bulk, or acceptable manufacturers documentation not maintained on site.",Not Critical,,,,03/06/2015,Trans Fat / Initial Inspection
23
+ 30075445,MORRIS PARK BAKE SHOP,BRONX,1007,MORRIS PARK AVE,10462,7188924968,Bakery,05/03/2012,No violations were recorded at the time of this inspection.,,,Not Applicable,,,,03/06/2015,Trans Fat / Second Compliance Inspection
@@ -0,0 +1,84 @@
1
+ require 'test_helper'
2
+
3
+ require 'eaternet/framework/lives_1_0'
4
+ include Eaternet::Framework
5
+
6
+ class BusinessTest < Minitest::Test
7
+
8
+ #
9
+ # Business#latitude and Business#longitude
10
+ #
11
+
12
+ PORTLAND_OR_US = { lat: 45.5236111, lon: -122.675 }
13
+
14
+ def test_business_without_lat_and_lon
15
+ business = Lives_1_0::Business.new do |i|
16
+ i.business_id = '789123hjd'
17
+ i.name = 'Starbucks'
18
+ i.address = '123 Happy Lane'
19
+ end
20
+ refute_nil business
21
+ end
22
+
23
+ def test_business_rejects_lat_and_lon_which_are_not_numbers
24
+ assert_raises(ArgumentError) {
25
+ business = Lives_1_0::Business.new do |i|
26
+ i.business_id = '789123hjd'
27
+ i.name = 'Starbucks'
28
+ i.address = '123 Happy Lane'
29
+ i.latitude = 'north'
30
+ i.longitude = '45.2'
31
+ end
32
+ }
33
+ end
34
+
35
+ def test_business_with_lat_and_lon
36
+ business = Lives_1_0::Business.new do |i|
37
+ i.business_id = '789123hjd'
38
+ i.name = 'Starbucks'
39
+ i.address = '123 Happy Lane'
40
+ i.latitude = PORTLAND_OR_US[:lat]
41
+ i.longitude = PORTLAND_OR_US[:lon]
42
+ end
43
+ refute_nil business
44
+ end
45
+
46
+ def test_business_rejects_invalid_latitude
47
+ assert_raises(ArgumentError) {
48
+ business = Lives_1_0::Business.new do |i|
49
+ i.business_id = '789123hjd'
50
+ i.name = 'Starbucks'
51
+ i.address = '123 Happy Lane'
52
+ i.latitude = 91
53
+ i.longitude = PORTLAND_OR_US[:lon]
54
+ end
55
+ }
56
+ end
57
+
58
+ def test_business_rejects_invalid_longitude
59
+ assert_raises(ArgumentError) {
60
+ business = Lives_1_0::Business.new do |i|
61
+ i.business_id = '789123hjd'
62
+ i.name = 'Starbucks'
63
+ i.address = '123 Happy Lane'
64
+ i.latitude = PORTLAND_OR_US[:lat]
65
+ i.longitude = -181
66
+ end
67
+ }
68
+ end
69
+
70
+ #
71
+ # Business#name
72
+ #
73
+
74
+ def test_business_rejects_blank_name
75
+ assert_raises(ArgumentError) {
76
+ business = Lives_1_0::Business.new do |i|
77
+ i.business_id = '789123hjd'
78
+ i.name = ''
79
+ i.address = '123 Happy Lane'
80
+ end
81
+ }
82
+ end
83
+
84
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+
3
+ require 'eaternet/framework/lives_1_0'
4
+ include Eaternet::Framework::Lives_1_0
5
+
6
+ class FeedInfoTest < Minitest::Test
7
+
8
+ def set_required_attrs(feed_info)
9
+ feed_info.feed_date = Date.new(2015, 1, 1)
10
+ feed_info.feed_version = '1.0'
11
+ feed_info.municipality_name = 'Dallas'
12
+ end
13
+
14
+ def setup
15
+ @dallas = FeedInfo.new { |i| set_required_attrs(i) }
16
+ end
17
+
18
+
19
+ #
20
+ # FeedInfo#date
21
+ #
22
+
23
+ def test_rejects_when_missing_feed_date
24
+ assert_raises(ArgumentError) {
25
+ FeedInfo.new do |i|
26
+ i.feed_version = '1.0'
27
+ i.municipality_name = 'NYC'
28
+ end
29
+ }
30
+ end
31
+
32
+ def test_rejects_incorrect_feed_date_type
33
+ @dallas.feed_date = '2015-01-01'
34
+ refute @dallas.valid?
35
+ end
36
+
37
+ def test_rejects_nil_feed_date
38
+ @dallas.feed_date = nil
39
+ refute @dallas.valid?
40
+ end
41
+
42
+ #
43
+ # FeedInfo#municipality_url
44
+ #
45
+
46
+ def test_with_url
47
+ @dallas.municipality_url = 'http://www.nyc.gov/html/doh/html/services/restaurant-inspection.shtml'
48
+ assert @dallas.valid?
49
+ end
50
+
51
+ def test_rejects_malformed_url
52
+ @dallas.municipality_url = 'www.nyc.gov'
53
+ refute @dallas.valid?
54
+ end
55
+
56
+ #
57
+ # FeedInfo#contact_email
58
+ #
59
+
60
+ def test_with_email
61
+ @dallas.contact_email = 'Pressoffice@health.nyc.gov'
62
+ assert @dallas.valid?
63
+ end
64
+
65
+ def test_rejects_malformed_email
66
+ @dallas.contact_email = 'myaddress.com'
67
+ refute @dallas.valid?
68
+ end
69
+
70
+ end
@@ -0,0 +1,100 @@
1
+ require 'test_helper'
2
+
3
+ require 'eaternet/framework/lives_1_0'
4
+ include Eaternet::Framework
5
+
6
+ class InspectionTest < Minitest::Test
7
+
8
+ #
9
+ # Inspection#type
10
+ #
11
+
12
+ def test_with_a_valid_type
13
+ inspection = Lives_1_0::Inspection.new do |i|
14
+ i.business_id = 'abcdef1'
15
+ i.score = 98
16
+ i.date = Date.new(2012, 2, 3)
17
+ i.type = 'initial'
18
+ end
19
+ refute_nil inspection
20
+ end
21
+
22
+ def test_rejects_unspecified_types
23
+ assert_raises(ArgumentError) {
24
+ inspection = Lives_1_0::Inspection.new do |i|
25
+ i.business_id = 'abcdef1'
26
+ i.score = 98
27
+ i.date = Date.new(2012, 2, 3)
28
+ i.type = 'auto inspection'
29
+ end
30
+ }
31
+ end
32
+
33
+ def test_without_a_type
34
+ inspection = Lives_1_0::Inspection.new do |i|
35
+ i.business_id = 'abcdef1'
36
+ i.score = 98
37
+ i.date = Date.new(2012, 2, 3)
38
+ end
39
+ refute_nil inspection
40
+ end
41
+
42
+
43
+ #
44
+ # Inspection#score
45
+ #
46
+
47
+ def test_accepts_a_blank_score
48
+ inspection = Lives_1_0::Inspection.new do |i|
49
+ i.score = ''
50
+ i.business_id = 'abcdef1'
51
+ i.date = Date.new(2012, 2, 3)
52
+ end
53
+ refute_nil inspection
54
+ end
55
+
56
+ def test_accepts_a_numeric_score
57
+ inspection = Lives_1_0::Inspection.new do |i|
58
+ i.score = 96
59
+ i.business_id = 'abcdef1'
60
+ i.date = Date.new(2012, 2, 3)
61
+ end
62
+ refute_nil inspection
63
+ end
64
+
65
+ def test_rejects_a_score_out_of_bounds
66
+ assert_raises(ArgumentError) {
67
+ inspection = Lives_1_0::Inspection.new do |i|
68
+ i.score = 101
69
+ i.business_id = 'abcdef1'
70
+ i.date = Date.new(2012, 2, 3)
71
+ end
72
+ }
73
+ end
74
+
75
+ def test_rejects_a_non_numeric_score
76
+ assert_raises(ArgumentError) {
77
+ inspection = Lives_1_0::Inspection.new do |i|
78
+ i.score = '100'
79
+ i.business_id = 'abcdef1'
80
+ i.date = Date.new(2012, 2, 3)
81
+ end
82
+ }
83
+ assert_raises(ArgumentError) {
84
+ inspection = Lives_1_0::Inspection.new do |i|
85
+ i.score = 'A'
86
+ i.business_id = 'abcdef1'
87
+ i.date = Date.new(2012, 2, 3)
88
+ end
89
+ }
90
+ end
91
+
92
+ def test_with_no_score_sets_it_to_blank
93
+ inspection = Lives_1_0::Inspection.new do |i|
94
+ i.business_id = 'abcdef1'
95
+ i.date = Date.new(2012, 2, 3)
96
+ end
97
+ assert_equal('', inspection.score)
98
+ end
99
+
100
+ end
@@ -0,0 +1,152 @@
1
+ require 'test_helper'
2
+
3
+ require 'eaternet/framework/lives_1_0'
4
+ include Eaternet::Framework::Lives_1_0
5
+
6
+ class LegendTest < Minitest::Test
7
+
8
+ def set_required_attrs(legend_record)
9
+ legend_record.minimum_score = 98
10
+ legend_record.maximum_score = 100
11
+ legend_record.description = 'A+'
12
+ end
13
+
14
+ def setup
15
+ @a_plus = Legend.new { |l| set_required_attrs(l) }
16
+ end
17
+
18
+
19
+ def test_with_all_required_attributes
20
+ assert @a_plus.valid?
21
+ end
22
+
23
+ def test_rejects_incorrect_minimum_type
24
+ @a_plus.minimum_score = '98'
25
+ refute @a_plus.valid?
26
+ end
27
+
28
+ def test_rejects_incorrect_maximum_type
29
+ @a_plus.maximum_score = '100'
30
+ refute @a_plus.valid?
31
+ end
32
+
33
+ def test_rejects_when_missing_maximum_score
34
+ assert_raises(ArgumentError) {
35
+ Legend.new do |legend_record|
36
+ legend_record.minimum_score = 100
37
+ legend_record.description = 'A+'
38
+ end
39
+ }
40
+ end
41
+
42
+ def test_rejects_when_missing_minimum_score
43
+ assert_raises(ArgumentError) {
44
+ Legend.new do |legend_record|
45
+ legend_record.maximum_score = 100
46
+ legend_record.description = 'A+'
47
+ end
48
+ }
49
+ end
50
+
51
+ def test_rejects_when_missing_description
52
+ assert_raises(ArgumentError) {
53
+ Legend.new do |legend_record|
54
+ legend_record.minimum_score = 95
55
+ legend_record.maximum_score = 100
56
+ end
57
+ }
58
+ end
59
+
60
+ def test_rejects_when_description_not_a_string
61
+ @a_plus.description = ['the', 'highest', 'score']
62
+ refute @a_plus.valid?
63
+ end
64
+
65
+ #
66
+ # LegendGroup
67
+ #
68
+
69
+ def test_group_accepts_comprehensive_records
70
+ group = LegendGroup.new do |lg|
71
+ lg.legends = [
72
+ Legend.new do |l|
73
+ l.minimum_score = 95
74
+ l.maximum_score = 100
75
+ l.description = 'A+'
76
+ end,
77
+ Legend.new do |l|
78
+ l.minimum_score = 0
79
+ l.maximum_score = 94
80
+ l.description = 'F'
81
+ end,
82
+ ]
83
+ end
84
+ refute_nil group
85
+ end
86
+
87
+ def test_group_accepts_records_overlapping_by_1
88
+ group = LegendGroup.new do |lg|
89
+ lg.legends = [
90
+ Legend.new do |l|
91
+ l.minimum_score = 95
92
+ l.maximum_score = 100
93
+ l.description = 'A+'
94
+ end,
95
+ Legend.new do |l|
96
+ l.minimum_score = 70
97
+ l.maximum_score = 95
98
+ l.description = 'B'
99
+ end,
100
+ Legend.new do |l|
101
+ l.minimum_score = 0
102
+ l.maximum_score = 69
103
+ l.description = 'F'
104
+ end,
105
+ ]
106
+ end
107
+ refute_nil group
108
+ end
109
+
110
+ def test_legend_group_rejects_non_comprehensive_records
111
+ assert_raises(ArgumentError) {
112
+ group = LegendGroup.new do |lg|
113
+ lg.legends = [ @a_plus ]
114
+ end
115
+ }
116
+ end
117
+
118
+ def test_legend_group_rejects_overlapping_records
119
+ assert_raises(ArgumentError) {
120
+ group = LegendGroup.new do |lg|
121
+ lg.legends = [
122
+ Legend.new do |l|
123
+ l.minimum_score = 90
124
+ l.maximum_score = 100
125
+ l.description = 'A'
126
+ end,
127
+ Legend.new do |l|
128
+ l.minimum_score = 0
129
+ l.maximum_score = 91
130
+ l.description = 'B'
131
+ end
132
+ ]
133
+ end
134
+ }
135
+ end
136
+
137
+ def test_legend_group_rejects_non_legend_objects
138
+ assert_raises(ArgumentError) {
139
+ group = LegendGroup.new do |lg|
140
+ lg.legends = [
141
+ Legend.new do |l|
142
+ l.minimum_score = 90
143
+ l.maximum_score = 100
144
+ l.description = 'A'
145
+ end,
146
+ '0-91, B'
147
+ ]
148
+ end
149
+ }
150
+ end
151
+
152
+ end