eaternet 0.2.0

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