purchase 0.0.4 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/features/purchase_committees.feature +91 -70
- data/features/purchase_emissions.feature +42 -13
- data/features/support/env.rb +1 -1
- data/lib/purchase/carbon_model.rb +135 -79
- data/lib/purchase/characterization.rb +2 -7
- data/lib/test_support/db/schema.rb +0 -1
- data/lib/test_support/purchase_record.rb +2 -3
- metadata +14 -18
- data/README.html +0 -102
- data/features/step_definitions/committee_steps.rb +0 -93
@@ -2,7 +2,7 @@ Feature: Purchase Committee Calculations
|
|
2
2
|
The purchase model should generate correct committee calculations
|
3
3
|
|
4
4
|
Scenario Outline: Adjusted cost committee from cost and date
|
5
|
-
Given a purchase emitter
|
5
|
+
Given a purchase emitter
|
6
6
|
And a characteristic "cost" of "<cost>"
|
7
7
|
And characteristic "date" of "<date>"
|
8
8
|
When the "adjusted_cost" committee is calculated
|
@@ -10,23 +10,24 @@ Feature: Purchase Committee Calculations
|
|
10
10
|
And the conclusion of the committee should be "<adjusted_cost>"
|
11
11
|
Examples:
|
12
12
|
| cost | date | adjusted_cost |
|
13
|
-
| 831.23 | 2010-08-01 |
|
14
|
-
| 11.00 | 2005-07-14 |
|
13
|
+
| 831.23 | 2010-08-01 | 688.67439 |
|
14
|
+
| 11.00 | 2005-07-14 | 9.11350 |
|
15
15
|
|
16
16
|
Scenario Outline: Adjusted cost committee from purchase amount and date
|
17
|
-
Given a purchase emitter
|
17
|
+
Given a purchase emitter
|
18
18
|
And a characteristic "purchase_amount" of "<amount>"
|
19
19
|
And characteristic "date" of "<date>"
|
20
|
-
When the "
|
21
|
-
|
20
|
+
When the "cost" committee is calculated
|
21
|
+
And the "adjusted_cost" committee is calculated
|
22
|
+
Then the committee should have used quorum "from cost and date"
|
22
23
|
And the conclusion of the committee should be "<adjusted_cost>"
|
23
24
|
Examples:
|
24
25
|
| amount | date | adjusted_cost |
|
25
|
-
| 831.23 | 2010-08-01 |
|
26
|
-
| 11.00 | 2005-07-14 |
|
26
|
+
| 831.23 | 2010-08-01 | 619.80695 |
|
27
|
+
| 11.00 | 2005-07-14 | 8.20215 |
|
27
28
|
|
28
29
|
Scenario Outline: Merchant category committee from merchant
|
29
|
-
Given a purchase emitter
|
30
|
+
Given a purchase emitter
|
30
31
|
And a characteristic "merchant.id" of "<id>"
|
31
32
|
When the "merchant_category" committee is calculated
|
32
33
|
Then the conclusion of the committee should have "mcc" of "<mcc>"
|
@@ -35,94 +36,114 @@ Feature: Purchase Committee Calculations
|
|
35
36
|
| 1 | 5111 |
|
36
37
|
| 2 | 5732 |
|
37
38
|
|
38
|
-
Scenario Outline: Industry shares committee
|
39
|
-
Given a purchase emitter
|
39
|
+
Scenario Outline: Industry shares committee from merchant category
|
40
|
+
Given a purchase emitter
|
40
41
|
And a characteristic "merchant_category.mcc" of "<mcc>"
|
41
42
|
When the "industry_shares" committee is calculated
|
42
|
-
Then the conclusion of the committee should have a record identified with "naics_code" of "<naics>" and having "ratio" of "<
|
43
|
+
Then the conclusion of the committee should have a record identified with "naics_code" of "<naics>" and having "ratio" of "<share>"
|
43
44
|
Examples:
|
44
|
-
| mcc | naics |
|
45
|
-
| 5111 | 45321 |
|
46
|
-
|
|
47
|
-
| 5172 |
|
48
|
-
| 5172 |
|
49
|
-
| 5172 |
|
50
|
-
| 5172 |
|
51
|
-
| 5172 | 324199 | 0.05 |
|
45
|
+
| mcc | naics | share |
|
46
|
+
| 5111 | 45321 | 1.0 |
|
47
|
+
| 5172 | 32411 | 0.8 |
|
48
|
+
| 5172 | 324121 | 0.05 |
|
49
|
+
| 5172 | 324122 | 0.05 |
|
50
|
+
| 5172 | 324191 | 0.05 |
|
51
|
+
| 5172 | 324199 | 0.05 |
|
52
52
|
|
53
|
-
Scenario Outline: Product line shares committee
|
54
|
-
Given a purchase emitter
|
53
|
+
Scenario Outline: Product line shares committee from merchant category
|
54
|
+
Given a purchase emitter
|
55
55
|
And a characteristic "merchant_category.mcc" of "<mcc>"
|
56
56
|
When the "industry_shares" committee is calculated
|
57
57
|
And the "product_line_shares" committee is calculated
|
58
|
-
Then the conclusion of the committee should
|
58
|
+
Then the conclusion of the committee should have a record identified with "ps_code" of "<ps_code>" and having "ratio" of "<share>"
|
59
59
|
Examples:
|
60
|
-
| mcc | ps_code |
|
60
|
+
| mcc | ps_code | share |
|
61
61
|
| 5111 | 20370 | 0.6 |
|
62
|
-
|
|
63
|
-
|
|
64
|
-
| 5172 | 30861 | 0.0225 |
|
65
|
-
| 5172 | 30862 | 0.0175 |
|
66
|
-
| 5172 | 30863 | 0.018 |
|
67
|
-
| 5172 | 30864 | 0.019 |
|
68
|
-
|
69
|
-
Scenario Outline: Sector shares committee from industry shares
|
70
|
-
Given a purchase emitter
|
71
|
-
And a characteristic "merchant_category.mcc" of "<mcc>"
|
72
|
-
When the "industry_shares" committee is calculated
|
73
|
-
And the "sector_shares" committee is calculated
|
74
|
-
Then the conclusion of the committee should include a key of "<io_code>" and subvalue "share" of "<share>" and subvalue "emission_factor" of "<emission_factor>"
|
75
|
-
Examples:
|
76
|
-
| mcc | io_code | emission_factor | share |
|
77
|
-
| 5111 | | | |
|
78
|
-
| 5732 | | | |
|
79
|
-
| 5172 | 324110 | 2.0 | 0.8 |
|
80
|
-
| 5172 | 324121 | 1.3 | 0.05 |
|
81
|
-
| 5172 | 324122 | 0.9 | 0.05 |
|
82
|
-
| 5172 | 324191 | 0.2 | 0.05 |
|
83
|
-
| 5172 | 324199 | 1.2 | 0.05 |
|
62
|
+
| 5111 | 20852 | 0.2 |
|
63
|
+
| 5111 | 20853 | 0.2 |
|
84
64
|
|
85
|
-
Scenario Outline: Sector shares committee from
|
86
|
-
Given a purchase emitter
|
65
|
+
Scenario Outline: Sector shares committee from merchant category
|
66
|
+
Given a purchase emitter
|
87
67
|
And a characteristic "merchant_category.mcc" of "<mcc>"
|
88
68
|
When the "industry_shares" committee is calculated
|
89
69
|
And the "product_line_shares" committee is calculated
|
90
70
|
And the "sector_shares" committee is calculated
|
91
|
-
Then the conclusion of the committee should
|
71
|
+
Then the conclusion of the committee should have a record identified with "io_code" of "<io_code>" and having "emission_factor" of "<emission_factor>"
|
72
|
+
And the conclusion of the committee should have a record identified with "io_code" of "<io_code>" and having "share" of "<share>"
|
92
73
|
Examples:
|
93
74
|
| mcc | io_code | emission_factor | share |
|
94
75
|
| 5111 | 334111 | 1.3 | 0.24 |
|
95
|
-
| 5111 | 33411A | 0.5 | 0.18 |
|
96
76
|
| 5111 | 511200 | 1.0 | 0.18 |
|
97
|
-
| 5111 |
|
98
|
-
| 5111 | 322230 | 1.4 |
|
99
|
-
|
|
77
|
+
| 5111 | 33411A | 0.5 | 0.18 |
|
78
|
+
| 5111 | 322230 | 1.4 | 0.2 |
|
79
|
+
| 5111 | 339940 | 1.1 | 0.2 |
|
100
80
|
| 5732 | 334300 | 1.2 | 0.25 |
|
101
|
-
| 5732 |
|
102
|
-
|
|
81
|
+
| 5732 | 33411A | 0.5 | 0.5 |
|
82
|
+
| 5732 | 334210 | 1.6 | 0.2 |
|
83
|
+
| 5812 | 722000 | 0.8 | 1.0 |
|
84
|
+
| 5172 | 324110 | 2.0 | 0.8 |
|
103
85
|
| 5172 | 324121 | 1.3 | 0.05 |
|
104
86
|
| 5172 | 324122 | 0.9 | 0.05 |
|
105
|
-
| 5172 | 324191 | 0.2 | 0.
|
87
|
+
| 5172 | 324191 | 0.2 | 0.05 |
|
106
88
|
| 5172 | 324199 | 1.2 | 0.05 |
|
107
|
-
| 8225 | 722000 | 0.8 | 0.15 |
|
108
89
|
|
109
|
-
Scenario Outline: Emission factor from
|
110
|
-
Given a purchase emitter
|
90
|
+
Scenario Outline: Emission factor from merchant category
|
91
|
+
Given a purchase emitter
|
111
92
|
And a characteristic "merchant_category.mcc" of "<mcc>"
|
112
93
|
When the "industry_shares" committee is calculated
|
113
94
|
And the "product_line_shares" committee is calculated
|
114
95
|
And the "sector_shares" committee is calculated
|
115
|
-
And the "
|
116
|
-
Then the conclusion of the committee should
|
96
|
+
And the "emission_factors" committee is calculated
|
97
|
+
Then the conclusion of the committee should have a record identified with "io_code" of "<io_code>" and having "factor" of "<emission_factor_share>"
|
117
98
|
Examples:
|
118
|
-
| mcc |
|
119
|
-
| 5111 |
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
99
|
+
| mcc | io_code | emission_factor_share |
|
100
|
+
| 5111 | 334111 | 0.312 |
|
101
|
+
| 5111 | 33411A | 0.09 |
|
102
|
+
| 5111 | 511200 | 0.18 |
|
103
|
+
| 5111 | 339940 | 0.22 |
|
104
|
+
| 5111 | 322230 | 0.28 |
|
105
|
+
| 5732 | 33411A | 0.25 |
|
106
|
+
| 5732 | 334300 | 0.3 |
|
107
|
+
| 5732 | 334210 | 0.32 |
|
108
|
+
| 5172 | 324110 | 1.6 |
|
109
|
+
| 5172 | 324121 | 0.065 |
|
110
|
+
| 5172 | 324122 | 0.045 |
|
111
|
+
| 5172 | 324191 | 0.01 |
|
112
|
+
| 5172 | 324199 | 0.06 |
|
113
|
+
| 5812 | 722000 | 0.8 |
|
123
114
|
|
124
|
-
Scenario
|
115
|
+
Scenario: Emission factor from default
|
116
|
+
Given a purchase emitter
|
117
|
+
When the "emission_factors" committee is calculated
|
118
|
+
Then the conclusion of the committee should have a record identified with "io_code" of "0" and having "factor" of "1"
|
119
|
+
|
120
|
+
Scenario Outline: Sector emissions from merchant id, cost, and date
|
125
121
|
Given a purchase emitter
|
126
|
-
And a characteristic "
|
127
|
-
|
128
|
-
|
122
|
+
And a characteristic "merchant.id" of "<merchant>"
|
123
|
+
And a characteristic "cost" of "<cost>"
|
124
|
+
And a characteristic "date" of "<date>"
|
125
|
+
When the "adjusted_cost" committee is calculated
|
126
|
+
And the "merchant_category" committee is calculated
|
127
|
+
And the "industry_shares" committee is calculated
|
128
|
+
And the "product_line_shares" committee is calculated
|
129
|
+
And the "sector_shares" committee is calculated
|
130
|
+
And the "emission_factors" committee is calculated
|
131
|
+
And the "sector_emissions" committee is calculated
|
132
|
+
Then the conclusion of the committee should include "<emission>"
|
133
|
+
Examples:
|
134
|
+
| merchant | io_code | cost | date | emission |
|
135
|
+
| 1 | 322230 | 100.00 | 2010-07-28 | 23.19801 |
|
136
|
+
| 1 | 334111 | 100.00 | 2010-07-28 | 25.84921 |
|
137
|
+
| 1 | 339940 | 100.00 | 2010-07-28 | 18.22701 |
|
138
|
+
| 1 | 511200 | 100.00 | 2010-07-28 | 14.91301 |
|
139
|
+
| 1 | 33411A | 100.00 | 2010-07-28 | 7.45650 |
|
140
|
+
| 2 | 334210 | 100.00 | 2010-07-28 | 26.51201 |
|
141
|
+
| 2 | 334300 | 100.00 | 2010-07-28 | 24.85501 |
|
142
|
+
| 2 | 33411A | 100.00 | 2010-07-28 | 20.71251 |
|
143
|
+
| 3 | 722000 | 100.00 | 2010-07-28 | 66.28003 |
|
144
|
+
| 4 | 7211A0 | 100.00 | 2010-07-28 | 82.85004 |
|
145
|
+
| 5 | 324110 | 100.00 | 2010-07-28 | 132.56007 |
|
146
|
+
| 5 | 324121 | 100.00 | 2010-07-28 | 5.38525 |
|
147
|
+
| 5 | 324122 | 100.00 | 2010-07-28 | 3.72825 |
|
148
|
+
| 5 | 324191 | 100.00 | 2010-07-28 | 0.82850 |
|
149
|
+
| 5 | 324199 | 100.00 | 2010-07-28 | 4.97100 |
|
@@ -1,7 +1,7 @@
|
|
1
1
|
Feature: Purchase Emissions Calculations
|
2
2
|
The purchase model should generate correct emission calculations
|
3
3
|
|
4
|
-
Scenario Outline: Calculations
|
4
|
+
Scenario Outline: Calculations starting from a merchant
|
5
5
|
Given a purchase has "merchant.id" of "<id>"
|
6
6
|
And it has "cost" of "<cost>"
|
7
7
|
And it has "date" of "<date>"
|
@@ -9,22 +9,51 @@ Feature: Purchase Emissions Calculations
|
|
9
9
|
Then the emission value should be within 1 kgs of <emission>
|
10
10
|
Examples:
|
11
11
|
| id | cost | date | emission |
|
12
|
-
| 1 | 100.00 | 2010-07-28 |
|
13
|
-
| 2 | 100.00 | 2010-07-28 |
|
14
|
-
| 3 | 100.00 | 2010-07-28 |
|
15
|
-
| 4 | 100.00 | 2010-07-28 |
|
16
|
-
| 5 | 100.00 | 2010-07-28 |
|
12
|
+
| 1 | 100.00 | 2010-07-28 | 89.64 |
|
13
|
+
| 2 | 100.00 | 2010-07-28 | 72.08 |
|
14
|
+
| 3 | 100.00 | 2010-07-28 | 66.28 |
|
15
|
+
| 4 | 100.00 | 2010-07-28 | 82.85 |
|
16
|
+
| 5 | 100.00 | 2010-07-28 | 147.47 |
|
17
17
|
|
18
|
-
Scenario Outline: Calculations
|
18
|
+
Scenario Outline: Calculations starting from a merchant category
|
19
19
|
Given a purchase has "merchant_category.mcc" of "<mcc>"
|
20
20
|
And it has "cost" of "<cost>"
|
21
21
|
And it has "date" of "<date>"
|
22
22
|
When emissions are calculated
|
23
|
-
Then the emission value should be within 1 kgs of <emission>
|
23
|
+
Then the emission value should be within 0.1 kgs of <emission>
|
24
24
|
Examples:
|
25
25
|
| mcc | cost | date | emission |
|
26
|
-
| 5111 | 100.00 | 2010-07-28 |
|
27
|
-
| 5732 | 100.00 | 2010-07-28 |
|
28
|
-
| 5812 | 100.00 | 2010-07-28 |
|
29
|
-
| 3504 | 100.00 | 2010-07-28 |
|
30
|
-
| 5172 | 100.00 | 2010-07-28 |
|
26
|
+
| 5111 | 100.00 | 2010-07-28 | 89.64 |
|
27
|
+
| 5732 | 100.00 | 2010-07-28 | 72.08 |
|
28
|
+
| 5812 | 100.00 | 2010-07-28 | 66.28 |
|
29
|
+
| 3504 | 100.00 | 2010-07-28 | 82.85 |
|
30
|
+
| 5172 | 100.00 | 2010-07-28 | 147.47 |
|
31
|
+
|
32
|
+
Scenario Outline: Calculations starting from industry
|
33
|
+
Given a purchase has "naics_code" of "<naics>"
|
34
|
+
And it has "cost" of "<cost>"
|
35
|
+
And it has "date" of "<date>"
|
36
|
+
When emissions are calculated
|
37
|
+
Then the emission value should be within 0.1 kgs of <emission>
|
38
|
+
Examples:
|
39
|
+
| naics | cost | date | emission |
|
40
|
+
| 45321 | 100.00 | 2010-07-28 | 89.64 |
|
41
|
+
| 443112 | 100.00 | 2010-07-28 | 72.08 |
|
42
|
+
| 72211 | 100.00 | 2010-07-28 | 66.28 |
|
43
|
+
| 72111 | 100.00 | 2010-07-28 | 82.85 |
|
44
|
+
| 32411 | 100.00 | 2010-07-28 | 132.56 |
|
45
|
+
| 324121 | 100.00 | 2010-07-28 | 5.39 |
|
46
|
+
| 324122 | 100.00 | 2010-07-28 | 3.73 |
|
47
|
+
| 324191 | 100.00 | 2010-07-28 | 0.83 |
|
48
|
+
| 324199 | 100.00 | 2010-07-28 | 4.97 |
|
49
|
+
|
50
|
+
Scenario Outline: Calculations without merchant, merchant category, or industry
|
51
|
+
Given a purchase has "cost" of "<cost>"
|
52
|
+
And it has "date" of "<date>"
|
53
|
+
When emissions are calculated
|
54
|
+
Then the emission value should be within 0.1 kgs of <emission>
|
55
|
+
Examples:
|
56
|
+
| cost | date | emission |
|
57
|
+
| 100.00 | 2010-07-28 | 82.85 |
|
58
|
+
| 100.00 | 2010-08-28 | 82.85 |
|
59
|
+
| 120.00 | 2010-08-28 | 99.42 |
|
data/features/support/env.rb
CHANGED
@@ -5,4 +5,4 @@ require 'cucumber'
|
|
5
5
|
require 'cucumber/formatter/unicode' # Remove this line if you don't want Cucumber Unicode support
|
6
6
|
|
7
7
|
require 'sniff'
|
8
|
-
Sniff.init File.join(File.dirname(__FILE__), '..', '..'), :earth => :industry
|
8
|
+
Sniff.init File.join(File.dirname(__FILE__), '..', '..'), :earth => :industry, :cucumber => true
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'leap'
|
2
2
|
require 'timeframe'
|
3
|
+
require 'date'
|
3
4
|
|
4
5
|
module BrighterPlanet
|
5
6
|
module Purchase
|
@@ -11,96 +12,92 @@ module BrighterPlanet
|
|
11
12
|
def self.included(base)
|
12
13
|
base.extend ::Leap::Subject
|
13
14
|
base.extend FastTimestamp
|
15
|
+
|
14
16
|
base.decide :emission, :with => :characteristics do
|
15
17
|
committee :emission do
|
16
|
-
quorum 'from emissions factor and adjusted cost', :needs =>
|
17
|
-
|
18
|
-
characteristics[:emission_factor] * characteristics[:adjusted_cost]
|
18
|
+
quorum 'from emissions factor and adjusted cost', :needs => :sector_emissions do |characteristics|
|
19
|
+
characteristics[:sector_emissions].inject(0) { |sum, emission| sum += emission }
|
19
20
|
end
|
20
|
-
|
21
|
-
|
22
|
-
|
21
|
+
end
|
22
|
+
|
23
|
+
committee :sector_emissions do
|
24
|
+
quorum 'from emissions factors and adjusted cost', :needs => [:emission_factors, :adjusted_cost] do |characteristics|
|
25
|
+
characteristics[:emission_factors].map do |emission_factor|
|
26
|
+
emission_factor.factor * characteristics[:adjusted_cost]
|
27
|
+
end
|
23
28
|
end
|
24
29
|
end
|
25
|
-
|
26
|
-
committee :
|
27
|
-
quorum 'from
|
28
|
-
characteristics[:sector_shares].inject(
|
29
|
-
if
|
30
|
+
|
31
|
+
committee :emission_factors do
|
32
|
+
quorum 'from sector shares', :needs => [:sector_shares] do |characteristics|
|
33
|
+
characteristics[:sector_shares].inject([]) do |list, sector_share|
|
34
|
+
if sector_share.emission_factor.nil?
|
30
35
|
raise MissingEmissionFactor,
|
31
|
-
"Missing emission factor for sector #{io_code}"
|
36
|
+
"Missing emission factor for sector #{sector_share.io_code}"
|
32
37
|
end
|
33
|
-
|
38
|
+
factor = sector_share.emission_factor * sector_share.share
|
39
|
+
list << EmissionFactor.new(sector_share.io_code, factor)
|
34
40
|
end
|
35
41
|
end
|
36
42
|
|
43
|
+
# FIXME TODO figure out if we really want a fallback and get a real one
|
37
44
|
quorum 'default' do
|
38
|
-
|
39
|
-
100
|
45
|
+
[EmissionFactor.new(0, 1)]
|
40
46
|
end
|
41
47
|
end
|
42
48
|
|
43
49
|
committee :sector_shares do
|
44
50
|
quorum 'from industry shares and product line shares', :needs => [:industry_shares, :product_line_shares] do |characteristics|
|
45
|
-
|
51
|
+
industry_shares = characteristics[:industry_shares]
|
52
|
+
industry_sector_shares = industry_shares.inject([]) do |list, industry_share|
|
53
|
+
industry_share.industries_sectors.each do |industry_sector|
|
54
|
+
io_code = industry_sector.io_code
|
55
|
+
unless ['420000','4A0000'].include?(io_code.to_s)
|
56
|
+
calculated_share = industry_share.ratio * industry_sector.ratio
|
57
|
+
sector = industry_sector.sector
|
58
|
+
list << SectorShare.new(sector, calculated_share)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
list
|
62
|
+
end
|
46
63
|
|
47
64
|
product_line_shares = characteristics[:product_line_shares]
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
product_line_share = product_line_shares[ps_code]
|
54
|
-
|
55
|
-
share = product_line_sector.ratio * product_line_share
|
56
|
-
sector = product_line_sector.sector
|
57
|
-
if sector.nil?
|
58
|
-
raise MissingSectorForProductLineSector,
|
59
|
-
"Missing a related sector for ProductLineSector #{product_line_sector.inspect}"
|
65
|
+
product_sector_shares = product_line_shares.inject([]) do |list, product_line_share|
|
66
|
+
product_line_share.product_lines_sectors.each do |product_line_sector|
|
67
|
+
calculated_share = product_line_sector.ratio * product_line_share.ratio
|
68
|
+
sector = product_line_sector.sector
|
69
|
+
list << SectorShare.new(sector, calculated_share)
|
60
70
|
end
|
61
|
-
|
62
|
-
:share => share,
|
63
|
-
:emission_factor => sector.emission_factor
|
64
|
-
}
|
65
|
-
hash
|
71
|
+
list
|
66
72
|
end
|
67
73
|
|
68
|
-
industry_sector_shares
|
69
|
-
end
|
70
|
-
|
71
|
-
# TODO Do we need this?
|
72
|
-
quorum 'from industry shares', :needs => [:industry_shares] do |characteristics|
|
73
|
-
sector_shares_from_industry_shares(characteristics[:industry_shares])
|
74
|
-
end
|
75
|
-
|
76
|
-
quorum 'default' do
|
77
|
-
raise "We need a merchant, merchant category, industry, or product_line"
|
74
|
+
industry_sector_shares + product_sector_shares
|
78
75
|
end
|
79
76
|
end
|
80
|
-
|
77
|
+
|
81
78
|
committee :product_line_shares do
|
82
|
-
quorum 'from industry shares', :needs =>
|
79
|
+
quorum 'from industry shares', :needs => :industry_shares do |characteristics|
|
83
80
|
industry_shares = characteristics[:industry_shares]
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
hash[ps_code] =
|
92
|
-
industry_product_line.ratio * industry_share.ratio
|
93
|
-
hash
|
81
|
+
industry_shares.inject([]) do |list, industry_share|
|
82
|
+
industry_share.industries_product_lines.each do |industry_product_line|
|
83
|
+
ratio = industry_product_line.ratio * industry_share.ratio
|
84
|
+
list << ProductLineShare.new(industry_product_line.ps_code,
|
85
|
+
ratio)
|
86
|
+
end
|
87
|
+
list
|
94
88
|
end
|
95
89
|
end
|
96
90
|
end
|
97
91
|
|
98
92
|
committee :industry_shares do
|
99
|
-
quorum 'from merchant category', :needs =>
|
100
|
-
characteristics[:merchant_category]
|
93
|
+
quorum 'from merchant category', :needs => :merchant_category do |characteristics|
|
94
|
+
IndustryShare.find_all_by_merchant_category characteristics[:merchant_category]
|
95
|
+
end
|
96
|
+
quorum 'from industry', :needs => :naics_code do |characteristics|
|
97
|
+
IndustryShare.find_all_by_naics_code characteristics[:naics_code]
|
101
98
|
end
|
102
99
|
end
|
103
|
-
|
100
|
+
|
104
101
|
committee :merchant_category do
|
105
102
|
quorum 'from merchant', :needs => [:merchant] do |characteristics|
|
106
103
|
characteristics[:merchant].merchant_category
|
@@ -109,42 +106,101 @@ module BrighterPlanet
|
|
109
106
|
|
110
107
|
committee :adjusted_cost do
|
111
108
|
quorum 'from cost and date', :needs => [:cost, :date] do |characteristics|
|
112
|
-
#
|
113
|
-
|
109
|
+
# TODO: Come up with a way to fetch real CPI conversions
|
110
|
+
@cpi_lookup ||= {
|
111
|
+
2009 => 1.189, 2010 => 1.207, 2011 => 1.225, 2012 => 1.245,
|
112
|
+
2013 => 1.265 }
|
113
|
+
|
114
|
+
date = parse_date characteristics[:date]
|
115
|
+
conversion_factor = @cpi_lookup[date.year] || 1.207
|
116
|
+
|
117
|
+
characteristics[:cost].to_f / conversion_factor
|
114
118
|
end
|
119
|
+
end
|
115
120
|
|
116
|
-
|
121
|
+
committee :cost do
|
122
|
+
quorum 'from purchase amount', :needs => :purchase_amount do |characteristics|
|
117
123
|
# FIXME TODO take out tax, then convert to 2002 US $ based on date and cost
|
118
124
|
characteristics[:purchase_amount] * 0.9
|
119
125
|
end
|
126
|
+
end
|
120
127
|
|
128
|
+
committee :date do
|
121
129
|
quorum 'default' do
|
122
|
-
|
130
|
+
Date.today
|
123
131
|
end
|
124
132
|
end
|
125
133
|
end
|
126
134
|
# FIXME TODO make other committees to report emissions by gas, by io sector, etc.
|
127
135
|
end
|
128
136
|
|
129
|
-
def self.
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
137
|
+
def self.parse_date(date)
|
138
|
+
date = date.is_a?(Date) ? date : Date.parse(date)
|
139
|
+
end
|
140
|
+
|
141
|
+
class IndustryShare
|
142
|
+
class << self
|
143
|
+
def find_all_by_naics_code(naics_code)
|
144
|
+
industry = Industry.find_by_naics_code naics_code
|
145
|
+
from_merchant_categories_industries industry.merchant_categories_industries
|
146
|
+
end
|
147
|
+
def find_all_by_merchant_category(merchant_category)
|
148
|
+
from_merchant_categories_industries merchant_category.merchant_categories_industries
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
def from_merchant_categories_industries(merchant_categories_industries)
|
153
|
+
merchant_categories_industries.map do |mci|
|
154
|
+
new mci.naics_code, mci.ratio
|
141
155
|
end
|
142
|
-
hash[io_code] = {
|
143
|
-
:share => calculated_share,
|
144
|
-
:emission_factor => sector.emission_factor
|
145
|
-
}
|
146
156
|
end
|
147
|
-
|
157
|
+
end
|
158
|
+
|
159
|
+
attr_accessor :naics_code, :ratio
|
160
|
+
|
161
|
+
def initialize(naics_code, ratio)
|
162
|
+
self.naics_code = naics_code
|
163
|
+
self.ratio = ratio
|
164
|
+
end
|
165
|
+
|
166
|
+
def industries_product_lines
|
167
|
+
IndustriesProductLines.find_all_by_naics_code naics_code
|
168
|
+
end
|
169
|
+
|
170
|
+
def industries_sectors
|
171
|
+
IndustriesSectors.find_all_by_naics_code naics_code
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class ProductLineShare
|
176
|
+
attr_accessor :ps_code, :ratio
|
177
|
+
|
178
|
+
def initialize(ps_code, ratio)
|
179
|
+
self.ps_code = ps_code
|
180
|
+
self.ratio = ratio
|
181
|
+
end
|
182
|
+
|
183
|
+
def product_lines_sectors
|
184
|
+
ProductLinesSectors.find_all_by_ps_code ps_code
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class SectorShare
|
189
|
+
attr_accessor :io_code, :share, :emission_factor
|
190
|
+
|
191
|
+
def initialize(sector, share)
|
192
|
+
self.io_code = sector.io_code
|
193
|
+
self.share = share
|
194
|
+
self.emission_factor = sector.emission_factor
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
class EmissionFactor
|
199
|
+
attr_accessor :io_code, :factor
|
200
|
+
|
201
|
+
def initialize(io_code, factor)
|
202
|
+
self.io_code = io_code
|
203
|
+
self.factor = factor
|
148
204
|
end
|
149
205
|
end
|
150
206
|
end
|
@@ -8,15 +8,10 @@ module BrighterPlanet
|
|
8
8
|
base.characterize do
|
9
9
|
has :merchant
|
10
10
|
has :merchant_category
|
11
|
-
has :
|
12
|
-
has :
|
13
|
-
has :sector_shares
|
14
|
-
has :purchase_amount # full purchase amount
|
11
|
+
has :naics_code
|
12
|
+
has :total # full purchase amount
|
15
13
|
has :tax # tax portion of purchase
|
16
14
|
has :cost # cost before tax
|
17
|
-
has :line_item # text describing what was purchased
|
18
|
-
has :customer_code
|
19
|
-
has :zip_code
|
20
15
|
has :date
|
21
16
|
end
|
22
17
|
end
|
@@ -9,7 +9,6 @@ class PurchaseRecord < ActiveRecord::Base
|
|
9
9
|
|
10
10
|
belongs_to :merchant, :foreign_key => 'merchant_id'
|
11
11
|
belongs_to :merchant_category, :foreign_key => 'mcc'
|
12
|
-
|
13
|
-
|
14
|
-
# belongs_to :sector, :foreign_key => 'io_code'
|
12
|
+
|
13
|
+
attr_accessor :naics_code
|
15
14
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: purchase
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 27
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
+
- 1
|
8
9
|
- 0
|
9
|
-
|
10
|
-
version: 0.0.4
|
10
|
+
version: 0.1.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Andy Rossmeissl
|
@@ -19,7 +19,7 @@ autorequire:
|
|
19
19
|
bindir: bin
|
20
20
|
cert_chain: []
|
21
21
|
|
22
|
-
date: 2010-08-
|
22
|
+
date: 2010-08-11 00:00:00 -04:00
|
23
23
|
default_executable:
|
24
24
|
dependencies:
|
25
25
|
- !ruby/object:Gem::Dependency
|
@@ -144,12 +144,12 @@ dependencies:
|
|
144
144
|
requirements:
|
145
145
|
- - "="
|
146
146
|
- !ruby/object:Gem::Version
|
147
|
-
hash:
|
147
|
+
hash: 23
|
148
148
|
segments:
|
149
149
|
- 0
|
150
|
-
-
|
151
|
-
-
|
152
|
-
version: 0.
|
150
|
+
- 1
|
151
|
+
- 6
|
152
|
+
version: 0.1.6
|
153
153
|
requirement: *id008
|
154
154
|
- !ruby/object:Gem::Dependency
|
155
155
|
type: :runtime
|
@@ -207,12 +207,12 @@ dependencies:
|
|
207
207
|
requirements:
|
208
208
|
- - ">="
|
209
209
|
- !ruby/object:Gem::Version
|
210
|
-
hash:
|
210
|
+
hash: 55
|
211
211
|
segments:
|
212
212
|
- 0
|
213
213
|
- 0
|
214
|
-
-
|
215
|
-
version: 0.0.
|
214
|
+
- 20
|
215
|
+
version: 0.0.20
|
216
216
|
requirement: *id012
|
217
217
|
- !ruby/object:Gem::Dependency
|
218
218
|
type: :runtime
|
@@ -255,12 +255,12 @@ dependencies:
|
|
255
255
|
requirements:
|
256
256
|
- - "="
|
257
257
|
- !ruby/object:Gem::Version
|
258
|
-
hash:
|
258
|
+
hash: 11
|
259
259
|
segments:
|
260
260
|
- 0
|
261
261
|
- 4
|
262
|
-
-
|
263
|
-
version: 0.4.
|
262
|
+
- 2
|
263
|
+
version: 0.4.2
|
264
264
|
requirement: *id015
|
265
265
|
- !ruby/object:Gem::Dependency
|
266
266
|
type: :runtime
|
@@ -302,7 +302,6 @@ extensions: []
|
|
302
302
|
|
303
303
|
extra_rdoc_files:
|
304
304
|
- LICENSE
|
305
|
-
- README.html
|
306
305
|
- README.markdown
|
307
306
|
files:
|
308
307
|
- LICENSE
|
@@ -314,8 +313,6 @@ files:
|
|
314
313
|
- lib/purchase/summarization.rb
|
315
314
|
- lib/test_support/db/schema.rb
|
316
315
|
- lib/test_support/purchase_record.rb
|
317
|
-
- README.html
|
318
|
-
- features/step_definitions/committee_steps.rb
|
319
316
|
- features/support/env.rb
|
320
317
|
- features/purchase_committees.feature
|
321
318
|
- features/purchase_emissions.feature
|
@@ -354,7 +351,6 @@ signing_key:
|
|
354
351
|
specification_version: 3
|
355
352
|
summary: A carbon model
|
356
353
|
test_files:
|
357
|
-
- features/step_definitions/committee_steps.rb
|
358
354
|
- features/support/env.rb
|
359
355
|
- features/purchase_committees.feature
|
360
356
|
- features/purchase_emissions.feature
|
data/README.html
DELETED
@@ -1,102 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="utf-8"?>
|
2
|
-
<!DOCTYPE html PUBLIC
|
3
|
-
"-//W3C//DTD XHTML 1.1 plus MathML 2.0 plus SVG 1.1//EN"
|
4
|
-
"http://www.w3.org/2002/04/xhtml-math-svg/xhtml-math-svg.dtd">
|
5
|
-
<html xmlns:svg='http://www.w3.org/2000/svg' xml:lang='en' xmlns='http://www.w3.org/1999/xhtml'>
|
6
|
-
<head><meta content='application/xhtml+xml;charset=utf-8' http-equiv='Content-type' /><title>Purchase</title></head>
|
7
|
-
<body>
|
8
|
-
<h1 id='purchase'>Purchase</h1>
|
9
|
-
|
10
|
-
<p>The purchase gem is used by Brighter Planet’s Carbon Middleware platform to define a mathematical emissions model for a purchase activity. In addition, this gem defines how the model’s calculation is displayed in a human readable format.</p>
|
11
|
-
|
12
|
-
<p>Contributions are welcome and encouraged, as we believe that help from the scientific and developer communities will help us provide correct and transparent models of greenhouse gas-emitting activities. See the section on the Collaboration Cycle below.</p>
|
13
|
-
|
14
|
-
<h2 id='usage'>Usage</h2>
|
15
|
-
|
16
|
-
<p>The purchase gem defines a module, BrighterPlanet::Purchase that should be included into an ActiveRecord model. Calling #emission on an instance of the model will determine the amount of emissions (CO2e) generated by the activity.</p>
|
17
|
-
|
18
|
-
<pre><code>class PurchaseRecord < ActiveRecord::Base
|
19
|
-
include BrighterPlanet::Purchase
|
20
|
-
...
|
21
|
-
end
|
22
|
-
|
23
|
-
p = PurchaseRecord.new :merchant_category_code => 2324, :amount => 873.45
|
24
|
-
p.emission
|
25
|
-
# => 2472</code></pre>
|
26
|
-
|
27
|
-
<h2 id='getting_ready_for_development'>Getting Ready for Development</h2>
|
28
|
-
|
29
|
-
<p>Purchase uses bundler to manage dependencies. To prepare your environment, perform the following: git clone git://github.com/brighterplanet/purchase.git cd purchase gem install bundler (note: prior to bundler 1.0 release, add –pre) bundle install</p>
|
30
|
-
|
31
|
-
<h3 id='a_note_to_rvm_users_and_nonusers'>A Note to RVM Users (and Non-Users)</h3>
|
32
|
-
|
33
|
-
<p><a href='http://rvm.beginrescueend.com/'>RVM</a> is a great tool for testing and development as it can create independent gem environments.</p>
|
34
|
-
|
35
|
-
<p>Included in the repository is a .rvmrc that will automatically create a ruby 1.8.7 gemset for you named purchase. If you already have a gemset with the same name, simply replace the `pwd` within the .rvmrc to whatever name you want.</p>
|
36
|
-
|
37
|
-
<h2 id='testing'>Testing</h2>
|
38
|
-
|
39
|
-
<p>Our goal is to have sufficient testing coverage for our emissions models. We have chosen to write our tests using <a href='http://cukes.info'>cucumber</a> as the table-formatted testing fits well with our calculations tests and the plain-english stories lower the barrier to entry for programmers of all skill levels.</p>
|
40
|
-
|
41
|
-
<p>There are two main features:</p>
|
42
|
-
|
43
|
-
<ul>
|
44
|
-
<li>purchase_emissions.feature, which tests emission values for various inputs</li>
|
45
|
-
|
46
|
-
<li>purchase_committees.feature, which tests sub-calculations used within the model</li>
|
47
|
-
</ul>
|
48
|
-
|
49
|
-
<p>To run all tests: <code>rake</code> or <code>cucumber</code></p>
|
50
|
-
|
51
|
-
<p>To run individual tests: <code>cucumber features/name_of_feature.feature</code></p>
|
52
|
-
|
53
|
-
<p>To run a specific scenario: <code>cucumber features/name_of_feature.feature:line_number_of_scenario</code></p>
|
54
|
-
|
55
|
-
<h3 id='sniff'>Sniff</h3>
|
56
|
-
|
57
|
-
<p>The sniff gem is used to provide a testing environment that includes dependent data models and a database.</p>
|
58
|
-
|
59
|
-
<p>See: <a href='http://github.com/brighterplanet/sniff'>http://github.com/brighterplanet/sniff</a></p>
|
60
|
-
|
61
|
-
<h3 id='earth'>Earth</h3>
|
62
|
-
|
63
|
-
<p>The earth gem contains the model definitions for dependent data models. For instance, this is zip code data or fuel prices used in the emissions calculations. Earth will look for any test fixtures defined in lib/test_support/db/fixtures. These sample records are in csv format and are imported into the testing database.</p>
|
64
|
-
|
65
|
-
<h2 id='implementation'>Implementation</h2>
|
66
|
-
|
67
|
-
<p>See the README for the sniff gem: <a href='http://github.com/brighterplanet/sniff/blob/master/README.markdown'>http://github.com/brighterplanet/sniff/blob/master/README.markdown</a></p>
|
68
|
-
|
69
|
-
<h2 id='collaboration_cycle'>Collaboration cycle</h2>
|
70
|
-
|
71
|
-
<p>Brighter Planet vigorously encourages collaborative improvement of its emitter libraries. Collaboration requires a (free) GitHub account.</p>
|
72
|
-
|
73
|
-
<h3 id='you'>You</h3>
|
74
|
-
|
75
|
-
<ol>
|
76
|
-
<li>Fork the emitter repository on GitHub.</li>
|
77
|
-
|
78
|
-
<li>Write a test proving the existing implementation’s inadequacy. Ensure that the test fails. Commit the test.</li>
|
79
|
-
|
80
|
-
<li>Improve the code until your new test passes and commit your changes.</li>
|
81
|
-
|
82
|
-
<li>Push your changes to your GitHub fork.</li>
|
83
|
-
|
84
|
-
<li>Submit a pull request to brighterplanet.</li>
|
85
|
-
</ol>
|
86
|
-
|
87
|
-
<h3 id='brighter_planet'>Brighter Planet</h3>
|
88
|
-
|
89
|
-
<ol>
|
90
|
-
<li>Receive a pull request.</li>
|
91
|
-
|
92
|
-
<li>Pull changes from forked repository.</li>
|
93
|
-
|
94
|
-
<li>Ensure tests pass.</li>
|
95
|
-
|
96
|
-
<li>Review changes for scientific accuracy.</li>
|
97
|
-
|
98
|
-
<li>Merge changes to master repository and publish.</li>
|
99
|
-
|
100
|
-
<li>Direct production environment to use new emitter version.</li>
|
101
|
-
</ol>
|
102
|
-
</body></html>
|
@@ -1,93 +0,0 @@
|
|
1
|
-
def coerce_value(value)
|
2
|
-
if value =~ /\d+\.\d+/
|
3
|
-
value.to_f
|
4
|
-
elsif value =~ /^\d+$/
|
5
|
-
value.to_i
|
6
|
-
else
|
7
|
-
value
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def compare_values(a, b)
|
12
|
-
if b =~ /\d+\.\d+/
|
13
|
-
b = b.to_f
|
14
|
-
a.should be_close(b, 0.00001)
|
15
|
-
elsif b =~ /^\d+$/
|
16
|
-
b = b.to_i
|
17
|
-
a.should == b
|
18
|
-
else
|
19
|
-
a.should == b
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
Given /^a purchase emitter$/ do
|
24
|
-
@activity = PurchaseRecord
|
25
|
-
end
|
26
|
-
|
27
|
-
Given /^(a )?characteristic "(.*)" of "(.*)"$/ do |_, name, value|
|
28
|
-
@characteristics ||= {}
|
29
|
-
|
30
|
-
if name =~ /\./
|
31
|
-
model_name, attribute = name.split /\./
|
32
|
-
model = model_name.singularize.camelize.constantize
|
33
|
-
value = model.send "find_by_#{attribute}", value
|
34
|
-
@characteristics[model_name.to_sym] = value
|
35
|
-
else
|
36
|
-
value = coerce_value(value)
|
37
|
-
@characteristics[name.to_sym] = value
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
When /^the "(.*)" committee is calculated$/ do |committee_name|
|
42
|
-
@decision ||= @activity.decisions[:emission]
|
43
|
-
@committee = @decision.committees.find { |c| c.name.to_s == committee_name }
|
44
|
-
@report = @committee.report(@characteristics, [])
|
45
|
-
@characteristics[committee_name.to_sym] = @report.conclusion
|
46
|
-
end
|
47
|
-
|
48
|
-
Then /^the committee should have used quorum "(.*)"$/ do |quorum|
|
49
|
-
@report.quorum.name.should == quorum
|
50
|
-
end
|
51
|
-
|
52
|
-
Then /^the conclusion of the committee should be "(.+)"$/ do |conclusion|
|
53
|
-
compare_values(@report.conclusion, conclusion)
|
54
|
-
end
|
55
|
-
|
56
|
-
Then /^the conclusion of the committee should include a key of (.*) and value (.*)$/ do |key, value|
|
57
|
-
if key.present?
|
58
|
-
@report.conclusion.keys.map(&:to_s).should include(key)
|
59
|
-
else
|
60
|
-
@report.conclusion.keys.map(&:to_s).should be_empty
|
61
|
-
end
|
62
|
-
|
63
|
-
if value.present?
|
64
|
-
@report.conclusion.each do |k, v|
|
65
|
-
@report.conclusion[k.to_s] == v
|
66
|
-
end
|
67
|
-
compare_values(@report.conclusion[key.to_s], value)
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
Then /^the conclusion of the committee should have "(.*)" of "(.*)"$/ do |attribute, value|
|
72
|
-
report_value = coerce_value @report.conclusion.send(attribute)
|
73
|
-
report_value.should == coerce_value(value)
|
74
|
-
end
|
75
|
-
|
76
|
-
Then /^the conclusion of the committee should have a record identified with "(.*)" of "(.*)" and having "(.*)" of "(.*)"$/ do |id_field, id, field, value|
|
77
|
-
id_field = id_field.to_sym
|
78
|
-
records = @report.conclusion
|
79
|
-
record = records.send("find_by_#{id_field}", id)
|
80
|
-
coerce_value(record.send(field)).should == coerce_value(value)
|
81
|
-
end
|
82
|
-
|
83
|
-
Then /^the conclusion of the committee should include a key of "(.*)" and subvalue "(.*)" of "(.*)" and subvalue "(.*)" of "(.*)"$/ do |key, subkey1, subvalue1, subkey2, subvalue2|
|
84
|
-
if key.present?
|
85
|
-
@report.conclusion.keys.map(&:to_s).should include(key)
|
86
|
-
actual_subvalue1 = coerce_value(@report.conclusion[key.to_s][subkey1.to_sym].to_s)
|
87
|
-
compare_values(actual_subvalue1, subvalue1)
|
88
|
-
actual_subvalue2 = coerce_value(@report.conclusion[key.to_s][subkey2.to_sym].to_s)
|
89
|
-
compare_values(actual_subvalue2, subvalue2)
|
90
|
-
else
|
91
|
-
@report.conclusion.keys.map(&:to_s).should be_empty
|
92
|
-
end
|
93
|
-
end
|