sniff 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +73 -0
- data/lib/sniff/active_record_ext.rb +12 -0
- data/lib/sniff/conversions_ext.rb +45 -0
- data/lib/sniff/database.rb +157 -0
- data/lib/sniff/emitter.rb +65 -0
- data/lib/sniff/tasks.rb +14 -0
- data/lib/sniff/timeframe.rb +251 -0
- data/lib/sniff.rb +31 -0
- data/lib/test_support/data_models/census_division.rb +9 -0
- data/lib/test_support/data_models/census_region.rb +9 -0
- data/lib/test_support/data_models/climate_division.rb +8 -0
- data/lib/test_support/data_models/country.rb +9 -0
- data/lib/test_support/data_models/egrid_region.rb +7 -0
- data/lib/test_support/data_models/egrid_subregion.rb +8 -0
- data/lib/test_support/data_models/gender.rb +6 -0
- data/lib/test_support/data_models/petroleum_administration_for_defense_district.rb +9 -0
- data/lib/test_support/data_models/state.rb +13 -0
- data/lib/test_support/data_models/urbanity.rb +6 -0
- data/lib/test_support/data_models/zip_code.rb +14 -0
- data/lib/test_support/db/fixtures/census_divisions.csv +10 -0
- data/lib/test_support/db/fixtures/census_regions.csv +5 -0
- data/lib/test_support/db/fixtures/climate_divisions.csv +360 -0
- data/lib/test_support/db/fixtures/countries.csv +247 -0
- data/lib/test_support/db/fixtures/egrid_regions.csv +6 -0
- data/lib/test_support/db/fixtures/egrid_subregions.csv +27 -0
- data/lib/test_support/db/fixtures/genders.csv +3 -0
- data/lib/test_support/db/fixtures/petroleum_administration_for_defense_districts.csv +8 -0
- data/lib/test_support/db/fixtures/states.csv +58 -0
- data/lib/test_support/db/fixtures/urbanities.csv +5 -0
- data/lib/test_support/db/fixtures/yearly_anonymous_emissions.csv +0 -0
- data/lib/test_support/db/fixtures/yearly_typical_emissions.csv +0 -0
- data/lib/test_support/db/fixtures/zip_codes.csv +3390 -0
- data/lib/test_support/db/schema.rb +117 -0
- data/spec/lib/sniff/database_spec.rb +22 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +5 -0
- data/vendor/geokit-rails/CHANGELOG.rdoc +46 -0
- data/vendor/geokit-rails/MIT-LICENSE +20 -0
- data/vendor/geokit-rails/README.markdown +561 -0
- data/vendor/geokit-rails/Rakefile +18 -0
- data/vendor/geokit-rails/about.yml +9 -0
- data/vendor/geokit-rails/assets/api_keys_template +61 -0
- data/vendor/geokit-rails/init.rb +1 -0
- data/vendor/geokit-rails/install.rb +14 -0
- data/vendor/geokit-rails/lib/geokit-rails/acts_as_mappable.rb +456 -0
- data/vendor/geokit-rails/lib/geokit-rails/adapters/abstract.rb +31 -0
- data/vendor/geokit-rails/lib/geokit-rails/adapters/mysql.rb +22 -0
- data/vendor/geokit-rails/lib/geokit-rails/adapters/postgresql.rb +22 -0
- data/vendor/geokit-rails/lib/geokit-rails/adapters/sqlserver.rb +43 -0
- data/vendor/geokit-rails/lib/geokit-rails/defaults.rb +22 -0
- data/vendor/geokit-rails/lib/geokit-rails/geocoder_control.rb +16 -0
- data/vendor/geokit-rails/lib/geokit-rails/ip_geocode_lookup.rb +46 -0
- data/vendor/geokit-rails/lib/geokit-rails.rb +24 -0
- data/vendor/geokit-rails/test/acts_as_mappable_test.rb +474 -0
- data/vendor/geokit-rails/test/boot.rb +25 -0
- data/vendor/geokit-rails/test/database.yml +20 -0
- data/vendor/geokit-rails/test/fixtures/companies.yml +7 -0
- data/vendor/geokit-rails/test/fixtures/custom_locations.yml +54 -0
- data/vendor/geokit-rails/test/fixtures/locations.yml +54 -0
- data/vendor/geokit-rails/test/fixtures/mock_addresses.yml +17 -0
- data/vendor/geokit-rails/test/fixtures/mock_families.yml +2 -0
- data/vendor/geokit-rails/test/fixtures/mock_houses.yml +9 -0
- data/vendor/geokit-rails/test/fixtures/mock_organizations.yml +5 -0
- data/vendor/geokit-rails/test/fixtures/mock_people.yml +5 -0
- data/vendor/geokit-rails/test/fixtures/stores.yml +0 -0
- data/vendor/geokit-rails/test/ip_geocode_lookup_test.rb +77 -0
- data/vendor/geokit-rails/test/models/company.rb +3 -0
- data/vendor/geokit-rails/test/models/custom_location.rb +12 -0
- data/vendor/geokit-rails/test/models/location.rb +4 -0
- data/vendor/geokit-rails/test/models/mock_address.rb +4 -0
- data/vendor/geokit-rails/test/models/mock_family.rb +3 -0
- data/vendor/geokit-rails/test/models/mock_house.rb +3 -0
- data/vendor/geokit-rails/test/models/mock_organization.rb +4 -0
- data/vendor/geokit-rails/test/models/mock_person.rb +4 -0
- data/vendor/geokit-rails/test/models/store.rb +3 -0
- data/vendor/geokit-rails/test/schema.rb +60 -0
- data/vendor/geokit-rails/test/tasks.rake +31 -0
- data/vendor/geokit-rails/test/test_helper.rb +23 -0
- metadata +492 -0
data/README.markdown
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
# sniff
|
2
|
+
Testing environment for Brighter Planet Climate Middleware emission calculation gems.
|
3
|
+
|
4
|
+
This gem provides:
|
5
|
+
* Sample climate data, representative of data found on http://data.brighterplanet.com
|
6
|
+
* References to gems needed by each emitter gem
|
7
|
+
|
8
|
+
# Usage
|
9
|
+
Within an emitter gem's test setup, you can:
|
10
|
+
require 'sniff'
|
11
|
+
|
12
|
+
Sniff.init '/path/to/emitter_project'
|
13
|
+
|
14
|
+
# How to contribute
|
15
|
+
Typical contributions will include updates to test data.
|
16
|
+
|
17
|
+
1. Fork the project.
|
18
|
+
1. Make your feature addition or bug fix.
|
19
|
+
1. Commit, do not mess with rakefile, version, or history. (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
20
|
+
1. Send me a pull request. Bonus points for topic branches.
|
21
|
+
|
22
|
+
# Local Gems
|
23
|
+
Sniff depends on several gems, some of which are developed by Brighter Planet. You can tell Sniff or any of the carbon gems to use your local repos in lieu of installed rubygems through the following steps:
|
24
|
+
|
25
|
+
|
26
|
+
Paste the following functions into your ~/.bash_profile
|
27
|
+
function mod_devgem() {
|
28
|
+
var="LOCAL_`echo $2 | tr 'a-z' 'A-Z'`"
|
29
|
+
|
30
|
+
if [ "$1" == "disable" ]
|
31
|
+
then
|
32
|
+
echo "unset $var"
|
33
|
+
unset $var
|
34
|
+
else
|
35
|
+
dir=${3:-"~/$2"}
|
36
|
+
echo "export $var=$dir"
|
37
|
+
export $var=$dir
|
38
|
+
fi
|
39
|
+
}
|
40
|
+
|
41
|
+
function devgems () {
|
42
|
+
# Usage: devgems [enable|disable] [gemname]
|
43
|
+
cmd=${1:-"enable"}
|
44
|
+
if [ -z $2 ]
|
45
|
+
then
|
46
|
+
mod_devgem $cmd characterizable
|
47
|
+
mod_devgem $cmd cohort_scope
|
48
|
+
mod_devgem $cmd falls_back_on
|
49
|
+
mod_devgem $cmd leap
|
50
|
+
mod_devgem $cmd loose_tight_dictionary
|
51
|
+
mod_devgem $cmd sniff
|
52
|
+
mod_devgem $cmd data_miner
|
53
|
+
else
|
54
|
+
mod_devgem $cmd $2
|
55
|
+
fi
|
56
|
+
}
|
57
|
+
|
58
|
+
To enable all local gems, run `devgems enable`
|
59
|
+
To turn off devgems, run `devgems disable`
|
60
|
+
To turn off a specific gem, run `devgems disable leap`
|
61
|
+
To turn on a specific gem, run `devgems enable leap`
|
62
|
+
|
63
|
+
Typical development process:
|
64
|
+
cd ~
|
65
|
+
git clone http://github.com/rossmeissl/leap.git
|
66
|
+
cd leap
|
67
|
+
<do some development on leap>
|
68
|
+
cd ~/sniff
|
69
|
+
devgems enable leap
|
70
|
+
rake gemspec
|
71
|
+
rm -f Gemfile.lock
|
72
|
+
bundle install
|
73
|
+
<run tests, e.g. `spec spec`>
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'conversions'
|
2
|
+
Conversions.register(:miles, :nautical_miles, 0.868976242)
|
3
|
+
Conversions.register(:kilometres, :nautical_miles, 0.539956803)
|
4
|
+
Conversions.register(:pounds_per_gallon, :kilograms_per_litre, 0.119826427)
|
5
|
+
Conversions.register(:inches, :meters, 0.0254)
|
6
|
+
Conversions.register(:kilowatt_hours, :watt_hours, 1_000.0)
|
7
|
+
Conversions.register(:kilowatt_hours, :btus, 3_412.14163) # obsolete?
|
8
|
+
Conversions.register(:watt_hours, :btus, 3.4121414799) # obsolete?
|
9
|
+
Conversions.register(:watt_hours, :joules, 3_600.0)
|
10
|
+
Conversions.register(:kilowatt_hours, :joules, 3_600_000.0)
|
11
|
+
|
12
|
+
# Conversions.register(:btus, :gallons_of_kerosene, 0.000007419521097)
|
13
|
+
Conversions.register(:joules, :litres_of_kerosene, 1.0 / (135_000.0 * 3.78541178 * 1_055.05585)) # should only be used for RECS 2005
|
14
|
+
|
15
|
+
# Conversions.register(:btus, :gallons_of_propane, 0.000010471204188)
|
16
|
+
Conversions.register(:joules, :litres_of_propane, 1.0 / (91_333.0 * 3.78541178 * 1_055.05585)) # should only be used for RECS 2005
|
17
|
+
|
18
|
+
# Conversions.register(:btus, :therms, 0.000010002388121)
|
19
|
+
Conversions.register(:therms, :joules, 105_505_585.0) # should only be used for RECS 2005
|
20
|
+
|
21
|
+
# Conversions.register(:btus, :gallons_of_fuel_oil, 0.0000072007637183)
|
22
|
+
Conversions.register(:joules, :litres_of_fuel_oil, 1.0 / (138_690.0 * 3.78541178 * 1_055.05585)) # should only be used for RECS 2005
|
23
|
+
|
24
|
+
# Conversions.register(:tons_of_coal, :btus, 20_169_000.0)
|
25
|
+
Conversions.register(:joules, :kilograms_of_coal, 1.0 / (22_342_000.0 * 0.00110231131 * 1_055.05585)) # should only be used for RECS 2005
|
26
|
+
|
27
|
+
Conversions.register(:kilograms, :lbs, 2.20462262)
|
28
|
+
Conversions.register(:kbtus, :btus, 1_000.0)
|
29
|
+
Conversions.register(:square_feet, :square_metres, 0.09290304)
|
30
|
+
Conversions.register(:pounds_per_square_foot, :kilograms_per_square_metre, 4.88242764)
|
31
|
+
Conversions.register(:kilograms_per_kilowatt_hour, :kilograms_per_megawatt_hour, 1_000.0)
|
32
|
+
Conversions.register(:btus, :joules, 1_055.05585)
|
33
|
+
Conversions.register(:kbtus, :joules, 1_000.0 * 1_055.05585)
|
34
|
+
Conversions.register(:cords, :joules, 2.11011171e10)
|
35
|
+
|
36
|
+
Conversions.register(:gallons_per_mile, :litres_per_kilometre, 2.35214583)
|
37
|
+
Conversions.register(:pounds_per_mile, :kilograms_per_kilometre, 0.281849232)
|
38
|
+
Conversions.register(:dollars, :cents, 100)
|
39
|
+
Conversions.register(:cubic_feet, :cubic_metres, 0.0283168466)
|
40
|
+
# 1 (kilocalories per pound) = 9 224.14105 joules per kilogram
|
41
|
+
Conversions.register :kilocalories_per_pound, :joules_per_kilogram, 9_224.14105
|
42
|
+
# 1 (grams per kilocalories) = 2.39005736 × 10-7 kilograms per joules
|
43
|
+
Conversions.register :grams_per_kilocalorie, :kilograms_per_joule, 2.39005736e-7
|
44
|
+
# 1 joule = 0.000239005736 kilocalories
|
45
|
+
Conversions.register :joules, :kilocalories, 0.000239005736
|
@@ -0,0 +1,157 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'logger'
|
3
|
+
|
4
|
+
module Sniff
|
5
|
+
class Database
|
6
|
+
class << self
|
7
|
+
def init(local_root, options = {})
|
8
|
+
environments = []
|
9
|
+
environments << really_init(local_root, options)
|
10
|
+
|
11
|
+
unless local_root == Sniff.root
|
12
|
+
environments << really_init(Sniff.root)
|
13
|
+
end
|
14
|
+
|
15
|
+
db_init local_root
|
16
|
+
environments.each { |e| e.populate_fixtures }
|
17
|
+
end
|
18
|
+
|
19
|
+
def really_init(root, options = {})
|
20
|
+
db = new root, options
|
21
|
+
db.init
|
22
|
+
db
|
23
|
+
end
|
24
|
+
|
25
|
+
def db_init(local_root)
|
26
|
+
db_path = File.join(local_root, 'db')
|
27
|
+
db_file_path = File.join(db_path, 'emitter_data.sqlite3')
|
28
|
+
FileUtils.mkdir_p db_path
|
29
|
+
db_drop db_file_path
|
30
|
+
connect db_file_path
|
31
|
+
db_create
|
32
|
+
load_all_schemas
|
33
|
+
end
|
34
|
+
|
35
|
+
def connect(db_file_path)
|
36
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3',
|
37
|
+
:database => db_file_path,
|
38
|
+
:pool => 5,
|
39
|
+
:timeout => 5000
|
40
|
+
end
|
41
|
+
|
42
|
+
def db_drop(db_file_path)
|
43
|
+
FileUtils.rm db_file_path if File.exists?(db_file_path)
|
44
|
+
end
|
45
|
+
|
46
|
+
def db_create
|
47
|
+
ActiveRecord::Base.connection
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_schema(&blk)
|
51
|
+
@schemas = [] unless defined?(@schemas)
|
52
|
+
@schemas << blk
|
53
|
+
end
|
54
|
+
|
55
|
+
def schemas
|
56
|
+
@schemas
|
57
|
+
end
|
58
|
+
|
59
|
+
def load_all_schemas
|
60
|
+
orig_std_out = STDOUT.clone
|
61
|
+
STDOUT.reopen File.open(File.join('/tmp', 'schema_output'), 'w')
|
62
|
+
|
63
|
+
ActiveRecord::Schema.define(:version => 1) do
|
64
|
+
ar_schema = self
|
65
|
+
Sniff::Database.schemas.each do |s|
|
66
|
+
ar_schema.instance_eval &s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
ensure
|
70
|
+
STDOUT.reopen(orig_std_out)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
attr_accessor :root, :lib_path, :schema_path, :fixtures_path,
|
75
|
+
:load_data, :fixtures, :logger
|
76
|
+
|
77
|
+
def initialize(root, options)
|
78
|
+
self.root = root
|
79
|
+
self.lib_path = File.join(root, 'lib', 'test_support')
|
80
|
+
self.load_data = options[:load_data]
|
81
|
+
self.schema_path = options[:schema_path]
|
82
|
+
self.fixtures_path = options[:fixtures_path]
|
83
|
+
self.logger = Logger.new options[:logdev]
|
84
|
+
end
|
85
|
+
|
86
|
+
def log(str)
|
87
|
+
logger.log str
|
88
|
+
end
|
89
|
+
|
90
|
+
def load_data?
|
91
|
+
@load_data = true if @load_data.nil?
|
92
|
+
@load_data
|
93
|
+
end
|
94
|
+
|
95
|
+
def schema_path
|
96
|
+
@schema_path ||= File.join(lib_path, 'db', 'schema.rb')
|
97
|
+
end
|
98
|
+
|
99
|
+
def fixtures_path
|
100
|
+
@fixtures_path ||= File.join(lib_path, 'db', 'fixtures')
|
101
|
+
end
|
102
|
+
|
103
|
+
def fixtures
|
104
|
+
@fixtures ||= []
|
105
|
+
end
|
106
|
+
|
107
|
+
def init
|
108
|
+
load_models
|
109
|
+
load_supporting_libs
|
110
|
+
read_schema
|
111
|
+
read_fixtures if load_data?
|
112
|
+
end
|
113
|
+
|
114
|
+
def read_schema
|
115
|
+
log "Reading schema #{schema_path}"
|
116
|
+
load(schema_path)
|
117
|
+
end
|
118
|
+
|
119
|
+
def read_fixtures
|
120
|
+
require 'active_record/fixtures'
|
121
|
+
log "Reading fixtures from #{fixtures_path}/**/*.{yml,csv}"
|
122
|
+
|
123
|
+
Dir["#{fixtures_path}/**/*.{yml,csv}"].each do |fixture_file|
|
124
|
+
fixtures << fixture_file
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def populate_fixtures
|
129
|
+
fixtures.each do |fixture_file|
|
130
|
+
log "Loading fixture #{fixture_file}"
|
131
|
+
Fixtures.create_fixtures(fixtures_path, fixture_file[(fixtures_path.size + 1)..-5])
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def load_supporting_libs
|
136
|
+
$:.unshift File.join(root, 'lib')
|
137
|
+
Dir[File.join(root, 'lib', 'test_support', '*.rb')].each do |lib|
|
138
|
+
log "Loading #{lib}"
|
139
|
+
require lib
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def load_models
|
144
|
+
require 'falls_back_on'
|
145
|
+
require 'falls_back_on/active_record_ext'
|
146
|
+
|
147
|
+
require 'cohort_scope'
|
148
|
+
require 'leap'
|
149
|
+
# require 'data_miner'
|
150
|
+
|
151
|
+
Dir["#{lib_path}/data_models/**/*.rb"].each do |lib|
|
152
|
+
log "Loading model #{lib}"
|
153
|
+
require lib
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'summary_judgement'
|
2
|
+
require 'fast_timestamp'
|
3
|
+
require 'common_name'
|
4
|
+
require 'falls_back_on'
|
5
|
+
|
6
|
+
module Sniff
|
7
|
+
module Emitter
|
8
|
+
def self.included(target)
|
9
|
+
target.send :extend, ClassMethods
|
10
|
+
target.send :extend, SummaryJudgement
|
11
|
+
target.send :extend, FastTimestamp
|
12
|
+
target.send :include, Characterizable
|
13
|
+
end
|
14
|
+
|
15
|
+
def parent_class
|
16
|
+
Emitter
|
17
|
+
end
|
18
|
+
|
19
|
+
def visible_effective_characteristics
|
20
|
+
characteristics.effective.reject { |_, c| c.hidden? }
|
21
|
+
end
|
22
|
+
|
23
|
+
def retired?
|
24
|
+
has_attribute?(:retirement) and retirement
|
25
|
+
end
|
26
|
+
|
27
|
+
def pattern?
|
28
|
+
self.class.pattern?
|
29
|
+
end
|
30
|
+
|
31
|
+
module ClassMethods
|
32
|
+
def from_params_hash(params = Hash.new)
|
33
|
+
resolved_params = Hash.new
|
34
|
+
instance = new
|
35
|
+
associations = reflect_on_all_associations
|
36
|
+
params.each do |k, v|
|
37
|
+
next if v.blank?
|
38
|
+
c = characteristics[k.to_sym]
|
39
|
+
next if c.nil?
|
40
|
+
if associations.map(&:name).include?(c.name.to_sym)
|
41
|
+
association = associations.find { |a| a.name == c.name.to_sym }
|
42
|
+
klass = association.options[:class_name] || association.name.to_s.pluralize.classify
|
43
|
+
klass = klass.constantize
|
44
|
+
if v.is_a?(Hash)
|
45
|
+
# h[:origin_airport][:iata_code] => 'MIA'
|
46
|
+
attr_name, attr_value = v.to_a.flatten[0, 2]
|
47
|
+
resolved_params[k] = klass.send "find_by_#{attr_name}", attr_value
|
48
|
+
else
|
49
|
+
# h[:origin_airport] => 'MIA'
|
50
|
+
resolved_params[k] = klass.send "find_by_#{k}", v
|
51
|
+
end
|
52
|
+
else
|
53
|
+
resolved_params[k] = v
|
54
|
+
end
|
55
|
+
end
|
56
|
+
new resolved_params
|
57
|
+
end
|
58
|
+
|
59
|
+
def pattern?
|
60
|
+
common_name.ends_with? 'pattern'
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
data/lib/sniff/tasks.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
task :environment do
|
2
|
+
ENV['SCHEMA'] = File.join Sniff.root, 'db', 'schema.rb'
|
3
|
+
ENV['FIXTURES_PATH'] = File.join Sniff.root, 'test', 'fixtures'
|
4
|
+
end
|
5
|
+
|
6
|
+
task :console do
|
7
|
+
require 'sniff'
|
8
|
+
cwd = Dir.pwd
|
9
|
+
Sniff.init cwd
|
10
|
+
|
11
|
+
require 'irb'
|
12
|
+
ARGV.clear
|
13
|
+
IRB.start
|
14
|
+
end
|
@@ -0,0 +1,251 @@
|
|
1
|
+
# Encapsulates a timeframe between two dates. The dates provided to the class are always until the last date. That means
|
2
|
+
# that the last date is excluded.
|
3
|
+
#
|
4
|
+
# # from 2007-10-01 00:00:00.000 to 2007-10-31 23:59:59.999
|
5
|
+
# Timeframe.new(Date(2007,10,1), Date(2007,11,1))
|
6
|
+
# # and holds 31 days
|
7
|
+
# Timeframe.new(Date(2007,10,1), Date(2007,11,1)).days #=> 31
|
8
|
+
|
9
|
+
module Sniff
|
10
|
+
class Timeframe
|
11
|
+
attr_accessor :from, :to
|
12
|
+
|
13
|
+
# Creates a new instance of Timeframe. You can either pass a start and end Date or a Hash with named arguments,
|
14
|
+
# with the following options:
|
15
|
+
#
|
16
|
+
# <tt>:month</tt>: Start date becomes the first day of this month, and the end date becomes the first day of
|
17
|
+
# the next month. If no <tt>:year</tt> is specified, the current year is used.
|
18
|
+
# <tt>:year</tt>: Start date becomes the first day of this year, and the end date becomes the first day of the
|
19
|
+
# next year.
|
20
|
+
#
|
21
|
+
# Examples:
|
22
|
+
#
|
23
|
+
# Timeframe.new Date.new(2007, 2, 1), Date.new(2007, 4, 1) # February and March
|
24
|
+
# Timeframe.new :year => 2004 # The year 2004
|
25
|
+
# Timeframe.new :month => 4 # April
|
26
|
+
# Timeframe.new :year => 2004, :month => 2 # Feburary 2004
|
27
|
+
def initialize(*args)
|
28
|
+
options = args.extract_options!
|
29
|
+
|
30
|
+
if month = options[:month]
|
31
|
+
month = Date.parse(month).month if month.is_a? String
|
32
|
+
year = options[:year] || Time.zone.today.year
|
33
|
+
from = Date.new(year, month, 1)
|
34
|
+
to = from.next_month
|
35
|
+
elsif year = options[:year]
|
36
|
+
from = Date.new(year, 1, 1)
|
37
|
+
to = Date.new(year+1, 1, 1)
|
38
|
+
end
|
39
|
+
|
40
|
+
from ||= args.shift.andand.to_date
|
41
|
+
to ||= args.shift.andand.to_date
|
42
|
+
|
43
|
+
raise ArgumentError, "Please supply a start and end date, `#{args.map(&:inspect).to_sentence}' is not enough" if from.nil? or to.nil?
|
44
|
+
raise ArgumentError, "Start date #{from} should be earlier than end date #{to}" if from > to
|
45
|
+
raise ArgumentError, 'Timeframes that cross year boundaries are dangerous' unless options[:skip_year_boundary_crossing_check] or from.year == to.yesterday.year or from == to
|
46
|
+
|
47
|
+
@from, @to = from, to
|
48
|
+
end
|
49
|
+
|
50
|
+
def inspect
|
51
|
+
"<Timeframe(#{object_id}) #{days} days starting #{from} ending #{to}>"
|
52
|
+
end
|
53
|
+
|
54
|
+
# The number of days in the timeframe
|
55
|
+
#
|
56
|
+
# Timeframe.new(Date.new(2007, 11, 1), Date.new(2007, 12, 1)).days #=> 30
|
57
|
+
# Timeframe.new(:month => 1).days #=> 31
|
58
|
+
# Timeframe.new(:year => 2004).days #=> 366
|
59
|
+
def days
|
60
|
+
(to - from).to_i
|
61
|
+
end
|
62
|
+
|
63
|
+
# Returns a string representation of the timeframe
|
64
|
+
def to_s
|
65
|
+
if [from.day, from.month, to.day, to.month].uniq == [1]
|
66
|
+
from.year.to_s
|
67
|
+
elsif from.day == 1 and to.day == 1 and to.month - from.month == 1
|
68
|
+
"#{Date::MONTHNAMES[from.month]} #{from.year}"
|
69
|
+
else
|
70
|
+
"the period from #{from.strftime('%d %B')} to #{to.yesterday.strftime('%d %B %Y')}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns true when the date is included in this Timeframe
|
75
|
+
def include?(obj)
|
76
|
+
# puts "checking to see if #{date} is between #{from} and #{to}" if Emitter::DEBUG
|
77
|
+
case obj
|
78
|
+
when Date
|
79
|
+
(from...to).include?(obj)
|
80
|
+
when Time
|
81
|
+
# (from...to).include?(obj.to_date)
|
82
|
+
raise "this wasn't previously supported, but it could be"
|
83
|
+
when Timeframe
|
84
|
+
from <= obj.from and to >= obj.to
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def proper_include?(other_timeframe)
|
89
|
+
raise ArgumentError, 'Proper inclusion only makes sense when testing other Timeframes' unless other_timeframe.is_a? Timeframe
|
90
|
+
(from < other_timeframe.from) and (to > other_timeframe.to)
|
91
|
+
end
|
92
|
+
|
93
|
+
# Returns true when this timeframe is equal to the other timeframe
|
94
|
+
def ==(other)
|
95
|
+
# puts "checking to see if #{self} is equal to #{other}" if Emitter::DEBUG
|
96
|
+
return false unless other.is_a?(Timeframe)
|
97
|
+
from == other.from and to == other.to
|
98
|
+
end
|
99
|
+
alias :eql? :==
|
100
|
+
|
101
|
+
# Calculates a hash value for the Timeframe, used for equality checking and Hash lookups.
|
102
|
+
# This needs to be an integer or else it won't use #eql?
|
103
|
+
def hash
|
104
|
+
from.hash + to.hash
|
105
|
+
end
|
106
|
+
alias :to_param :hash
|
107
|
+
|
108
|
+
# Returns an array of month-long subtimeframes
|
109
|
+
# TODO: rename to month_subtimeframes
|
110
|
+
def months
|
111
|
+
raise ArgumentError, "Please only provide whole-month timeframes to Timeframe#months" unless from.day == 1 and to.day == 1
|
112
|
+
raise ArgumentError, 'Timeframes that cross year boundaries are dangerous during Timeframe#months' unless from.year == to.yesterday.year
|
113
|
+
year = from.year # therefore this only works in the from year
|
114
|
+
(from.month..to.yesterday.month).map { |m| Timeframe.new :month => m, :year => year }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Returns the relevant year as a Timeframe
|
118
|
+
def year
|
119
|
+
raise ArgumentError, 'Timeframes that cross year boundaries are dangerous during Timeframe#year' unless from.year == to.yesterday.year
|
120
|
+
Timeframe.new :year => from.year
|
121
|
+
end
|
122
|
+
|
123
|
+
# multiyear safe
|
124
|
+
def month_subtimeframes
|
125
|
+
(from.year..to.yesterday.year).map do |year|
|
126
|
+
(1..12).map do |month|
|
127
|
+
Timeframe.new(:year => year, :month => month) & self
|
128
|
+
end
|
129
|
+
end.flatten.compact
|
130
|
+
end
|
131
|
+
|
132
|
+
# multiyear safe
|
133
|
+
def full_month_subtimeframes
|
134
|
+
month_subtimeframes.map { |st| Timeframe.new(:year => st.from.year, :month => st.from.month) }
|
135
|
+
end
|
136
|
+
|
137
|
+
# multiyear safe
|
138
|
+
def year_subtimeframes
|
139
|
+
(from.year..to.yesterday.year).map do |year|
|
140
|
+
Timeframe.new(:year => year) & self
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# multiyear safe
|
145
|
+
def full_year_subtimeframes
|
146
|
+
(from.year..to.yesterday.year).map do |year|
|
147
|
+
Timeframe.new :year => year
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# multiyear safe
|
152
|
+
def ending_no_later_than(date)
|
153
|
+
if to < date
|
154
|
+
self
|
155
|
+
elsif from >= date
|
156
|
+
nil
|
157
|
+
else
|
158
|
+
Timeframe.multiyear from, date
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
# Returns a timeframe representing the intersection of the timeframes
|
163
|
+
def &(other_timeframe)
|
164
|
+
this_timeframe = self
|
165
|
+
if other_timeframe == this_timeframe
|
166
|
+
this_timeframe
|
167
|
+
elsif this_timeframe.from > other_timeframe.from and this_timeframe.to < other_timeframe.to
|
168
|
+
this_timeframe
|
169
|
+
elsif other_timeframe.from > this_timeframe.from and other_timeframe.to < this_timeframe.to
|
170
|
+
other_timeframe
|
171
|
+
elsif this_timeframe.from >= other_timeframe.to or this_timeframe.to <= other_timeframe.from
|
172
|
+
nil
|
173
|
+
else
|
174
|
+
Timeframe.new [this_timeframe.from, other_timeframe.from].max, [this_timeframe.to, other_timeframe.to].min, :skip_year_boundary_crossing_check => true
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns a fraction of another Timeframe
|
179
|
+
def /(other_timeframe)
|
180
|
+
raise ArgumentError, 'You can only divide a Timeframe by another Timeframe' unless other_timeframe.is_a? Timeframe
|
181
|
+
self.days.to_f / other_timeframe.days.to_f
|
182
|
+
end
|
183
|
+
|
184
|
+
def crop(container)
|
185
|
+
raise ArgumentError, 'You can only crop a timeframe by another timeframe' unless container.is_a? Timeframe
|
186
|
+
self.class.new [from, container.from].max, [to, container.to].min
|
187
|
+
end
|
188
|
+
|
189
|
+
def gaps_left_by(*timeframes)
|
190
|
+
# remove extraneous timeframes
|
191
|
+
timeframes.reject! { |t| t.to <= from }
|
192
|
+
timeframes.reject! { |t| t.from >= to }
|
193
|
+
|
194
|
+
# crop timeframes
|
195
|
+
timeframes.map! { |t| t.crop self }
|
196
|
+
|
197
|
+
# remove proper subtimeframes
|
198
|
+
timeframes.reject! { |t| timeframes.detect { |u| u.proper_include? t } }
|
199
|
+
|
200
|
+
# escape
|
201
|
+
return [self] if timeframes.empty?
|
202
|
+
|
203
|
+
timeframes.sort! { |x, y| x.from <=> y.from }
|
204
|
+
|
205
|
+
timeframes.collect(&:to).unshift(from).ykk(timeframes.collect(&:from).push(to)) do |gap|
|
206
|
+
Timeframe.new(*gap) if gap[1] > gap[0]
|
207
|
+
end.compact
|
208
|
+
end
|
209
|
+
|
210
|
+
def covered_by?(*timeframes)
|
211
|
+
gaps_left_by(*timeframes).empty?
|
212
|
+
end
|
213
|
+
|
214
|
+
def last_year
|
215
|
+
self.class.new((from - 1.year), (to - 1.year))
|
216
|
+
end
|
217
|
+
|
218
|
+
class << self
|
219
|
+
def this_year
|
220
|
+
new :year => Time.now.year
|
221
|
+
end
|
222
|
+
|
223
|
+
def constrained_new(from, to, constraint)
|
224
|
+
raise ArgumentError, 'Need Date, Date, Timeframe as args' unless from.is_a? Date and to.is_a? Date and constraint.is_a? Timeframe
|
225
|
+
raise ArgumentError, "Start date #{from} should be earlier than end date #{to}" if from > to
|
226
|
+
if to <= constraint.from or from >= constraint.to
|
227
|
+
new constraint.from, constraint.from
|
228
|
+
elsif from.year == to.yesterday.year
|
229
|
+
new(from, to) & constraint
|
230
|
+
elsif from.year < constraint.from.year and constraint.from.year < to.yesterday.year
|
231
|
+
constraint
|
232
|
+
else
|
233
|
+
new [constraint.from, from].max, [constraint.to, to].min
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def multiyear(from, to)
|
238
|
+
from = Date.parse(from) if from.is_a?(String)
|
239
|
+
to = Date.parse(to) if to.is_a?(String)
|
240
|
+
new from, to, :skip_year_boundary_crossing_check => true
|
241
|
+
end
|
242
|
+
|
243
|
+
# create a multiyear timeframe +/- number of years around today
|
244
|
+
def mid(number)
|
245
|
+
from = Time.zone.today - number.years
|
246
|
+
to = Time.zone.today + number.years
|
247
|
+
multiyear from, to
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
end
|
data/lib/sniff.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Sniff
|
2
|
+
extend self
|
3
|
+
|
4
|
+
def root
|
5
|
+
File.join(File.dirname(__FILE__), '..')
|
6
|
+
end
|
7
|
+
|
8
|
+
def init(local_root, options = {})
|
9
|
+
require 'active_record'
|
10
|
+
require 'sqlite3'
|
11
|
+
|
12
|
+
load_plugins
|
13
|
+
|
14
|
+
Sniff::Database.init local_root, options
|
15
|
+
end
|
16
|
+
|
17
|
+
def load_plugins
|
18
|
+
Dir[File.join(Sniff.root, 'vendor', '**', 'init.rb')].each do |pluginit|
|
19
|
+
$:.unshift File.join(File.dirname(pluginit), 'lib')
|
20
|
+
load pluginit
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require 'characterizable'
|
26
|
+
$:.unshift File.dirname(__FILE__)
|
27
|
+
require 'sniff/active_record_ext'
|
28
|
+
require 'sniff/database'
|
29
|
+
require 'sniff/emitter'
|
30
|
+
require 'sniff/conversions_ext'
|
31
|
+
require 'sniff/timeframe'
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class CensusDivision < ActiveRecord::Base
|
2
|
+
set_primary_key :number
|
3
|
+
|
4
|
+
belongs_to :census_region, :foreign_key => 'census_region_number'
|
5
|
+
has_many :states, :foreign_key => 'census_division_number'
|
6
|
+
has_many :zip_codes, :through => :states
|
7
|
+
has_many :climate_divisions, :through => :states
|
8
|
+
has_many :residential_energy_consumption_survey_responses, :foreign_key => 'census_division_number'
|
9
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class CensusRegion < ActiveRecord::Base
|
2
|
+
set_primary_key :number
|
3
|
+
|
4
|
+
has_many :census_divisions, :foreign_key => 'census_region_number'
|
5
|
+
has_many :states, :through => :census_divisions
|
6
|
+
# has_many :climate_divisions, :through => :census_divisions
|
7
|
+
# has_many :zip_codes, :through => :census_divisions
|
8
|
+
has_many :residential_energy_consumption_survey_responses, :foreign_key => 'census_region_number'
|
9
|
+
end
|