flight 0.0.2 → 0.0.4

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,270 @@
1
+ require 'weighted_average'
2
+
3
+ module BrighterPlanet
4
+ module Flight
5
+ module CarbonModel
6
+ def self.included(base)
7
+ base.send :include, ::Leap::Subject
8
+ base.decide :emission, :with => :characteristics do
9
+ committee :emission do
10
+ quorum 'from fuel and passengers with coefficients',
11
+ :needs => [:fuel, :passengers, :seat_class_multiplier, :emission_factor,
12
+ :radiative_forcing_index, :freight_share, :date] do |characteristics, timeframe|
13
+ if timeframe.include? characteristics[:date]
14
+ #( kg fuel ) * ( kg CO2 / kg fuel ) = kg CO2
15
+ (characteristics[:fuel] / characteristics[:passengers] * characteristics[:seat_class_multiplier]) * characteristics[:emission_factor] * characteristics[:radiative_forcing_index] * (1 - characteristics[:freight_share])
16
+ else
17
+ 0
18
+ end
19
+ end
20
+
21
+ quorum 'default' do
22
+ raise "The emission committee's default quorum should never be called"
23
+ end
24
+ end
25
+
26
+ committee :fuel do # returns kg fuel
27
+ quorum 'from fuel per segment and emplanements and trips', :needs => [:fuel_per_segment, :emplanements_per_trip, :trips] do |characteristics|
28
+ characteristics[:fuel_per_segment] * characteristics[:emplanements_per_trip].to_f * characteristics[:trips].to_f
29
+ end
30
+ end
31
+
32
+ committee :fuel_per_segment do # returns kg fuel
33
+ quorum 'from adjusted distance and fuel use formula and emplanements and trips', :needs => [:adjusted_distance_per_segment, :fuel_use_coefficients, :endpoint_fuel] do |characteristics|
34
+ characteristics[:fuel_use_coefficients][:m3].to_f * characteristics[:adjusted_distance_per_segment].to_f ** 3 +
35
+ characteristics[:fuel_use_coefficients][:m2].to_f * characteristics[:adjusted_distance_per_segment].to_f ** 2 +
36
+ characteristics[:fuel_use_coefficients][:m1].to_f * characteristics[:adjusted_distance_per_segment].to_f +
37
+ characteristics[:endpoint_fuel].to_f
38
+ end
39
+ end
40
+
41
+ committee :adjusted_distance_per_segment do
42
+ quorum 'from adjusted distance and emplanements', :needs => [:adjusted_distance, :emplanements_per_trip] do |characteristics|
43
+ characteristics[:adjusted_distance] / characteristics[:emplanements_per_trip]
44
+ end
45
+ end
46
+
47
+ committee :endpoint_fuel do
48
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
49
+ characteristics[:aircraft].endpoint_fuel
50
+ end
51
+
52
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
53
+ characteristics[:aircraft_class].endpoint_fuel
54
+ end
55
+
56
+ quorum 'default' do
57
+ Aircraft.fallback.andand.endpoint_fuel
58
+ end
59
+ end
60
+
61
+ committee :fuel_use_coefficients do
62
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
63
+ characteristics[:aircraft].attributes.symbolize_keys.slice(:m1, :m2, :m3)
64
+ end
65
+
66
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
67
+ characteristics[:aircraft_class].attributes.symbolize_keys.slice(:m1, :m2, :m3)
68
+ end
69
+
70
+ quorum 'default' do
71
+ fallback = Aircraft.fallback
72
+ if fallback
73
+ fallback.attributes.symbolize_keys.slice(:m1, :m2, :m3)
74
+ end
75
+ end
76
+ end
77
+
78
+ committee :passengers do
79
+ quorum 'from seats and load factor', :needs => [:seats, :load_factor] do |characteristics|
80
+ (characteristics[:seats] * characteristics[:load_factor]).round
81
+ end
82
+ end
83
+
84
+ committee :seats do
85
+ # leaving this here to explain how someday we might lookup seat count based on both airline AND aircraft
86
+ #SE quorum 'from_airline_and_aircraft', :needs => [:airline, :aircraft] do |characteristics, timeframe|
87
+ #SE if aircraft = AirlineAircraft.memoized_find_by_airline_id_and_aircraft_id(characteristics[:airline].id, characteristics[:aircraft].id)
88
+ #SE aircraft.seats
89
+ #SE end
90
+ #SE end
91
+
92
+ quorum 'from aircraft', :needs => :aircraft do |characteristics|
93
+ characteristics[:aircraft].seats
94
+ end
95
+
96
+ quorum 'from seats estimate', :needs => :seats_estimate do |characteristics|
97
+ characteristics[:seats_estimate]
98
+ end
99
+
100
+ quorum 'from cohort', :needs => :cohort do |characteristics|
101
+ seats = characteristics[:cohort].weighted_average :seats, :weighted_by => :passengers
102
+ if seats.nil? or seats.zero?
103
+ nil
104
+ else
105
+ seats
106
+ end
107
+ end
108
+
109
+ quorum 'from aircraft class', :needs => :aircraft_class do |characteristics|
110
+ characteristics[:aircraft_class].seats
111
+ end
112
+
113
+ quorum 'default' do
114
+ FlightSegment.fallback.andand.seats
115
+ end
116
+ end
117
+
118
+ committee :load_factor do
119
+ quorum 'from cohort', :needs => :cohort do |characteristics|
120
+ characteristics[:cohort].weighted_average(:load_factor, :weighted_by => :passengers)
121
+ end
122
+
123
+ quorum 'default' do
124
+ BrighterPlanet::Flight.flight_model.fallback.andand.load_factor
125
+ end
126
+ end
127
+
128
+ committee :adjusted_distance do # returns nautical miles
129
+ quorum 'from distance', :needs => [:distance, :emplanements_per_trip] do |characteristics|
130
+ route_inefficiency_factor = BrighterPlanet::Flight.flight_model.research(:route_inefficiency_factor)
131
+ dogleg_factor = BrighterPlanet::Flight.flight_model.research(:dogleg_factor)
132
+ characteristics[:distance] * route_inefficiency_factor * ( dogleg_factor ** (characteristics[:emplanements_per_trip] - 1) )
133
+ end
134
+ end
135
+
136
+ committee :distance do # returns nautical miles
137
+ quorum 'from airports', :needs => [:origin_airport, :destination_airport] do |characteristics|
138
+ if characteristics[:origin_airport].latitude and
139
+ characteristics[:origin_airport].longitude and
140
+ characteristics[:destination_airport].latitude and
141
+ characteristics[:destination_airport].longitude
142
+ characteristics[:origin_airport].distance_to(characteristics[:destination_airport], :units => :kms).kilometres.to :nautical_miles
143
+ end
144
+ end
145
+
146
+ quorum 'from distance estimate', :needs => :distance_estimate do |characteristics|
147
+ characteristics[:distance_estimate].kilometres.to :nautical_miles
148
+ end
149
+
150
+ quorum 'from distance class', :needs => :distance_class do |characteristics|
151
+ characteristics[:distance_class].distance.kilometres.to :nautical_miles
152
+ end
153
+
154
+ quorum 'from cohort', :needs => :cohort do |characteristics|
155
+ distance = characteristics[:cohort].weighted_average(:distance, :weighted_by => :passengers).to_f.kilometres.to(:nautical_miles)
156
+ distance > 0 ? distance : nil
157
+ end
158
+
159
+ quorum 'default' do
160
+ BrighterPlanet::Flight.flight_model.fallback.distance_estimate.kilometres.to :nautical_miles
161
+ end
162
+ end
163
+
164
+ committee :emplanements_per_trip do # per trip
165
+ quorum 'default' do
166
+ BrighterPlanet::Flight.flight_model.fallback.emplanements_per_trip_before_type_cast
167
+ end
168
+ end
169
+
170
+ committee :radiative_forcing_index do
171
+ quorum 'from fuel type', :needs => :fuel_type do |characteristics|
172
+ characteristics[:fuel_type].radiative_forcing_index
173
+ end
174
+ end
175
+
176
+ committee :emission_factor do # returns kg CO2 / kg fuel
177
+ quorum 'from fuel type', :needs => :fuel_type do |characteristics|
178
+ #( kg CO2 / litres fuel ) * ( litres fuel / kg fuel )
179
+ characteristics[:fuel_type].emission_factor * ( 1 / characteristics[:fuel_type].density).gallons.to(:litres)
180
+ end
181
+ end
182
+
183
+ committee :fuel_type do
184
+ quorum 'default' do
185
+ FlightFuelType.fallback
186
+ end
187
+ end
188
+
189
+ committee :freight_share do
190
+ quorum 'from cohort', :needs => :cohort do |characteristics|
191
+ characteristics[:cohort].weighted_average(:freight_share, :weighted_by => :passengers)
192
+ end
193
+
194
+ quorum 'default' do
195
+ FlightSegment.fallback.andand.freight_share
196
+ end
197
+ end
198
+
199
+ committee :trips do
200
+ quorum 'default' do
201
+ BrighterPlanet::Flight.flight_model.fallback.andand.trips_before_type_cast
202
+ end
203
+ end
204
+
205
+ committee :domesticity do
206
+ quorum 'from airports', :needs => [:origin_airport, :destination_airport] do |characteristics|
207
+ if [characteristics[:origin_airport], characteristics[:destination_airport]].all?(&:united_states?)
208
+ FlightDomesticity.find_by_name('domestic')
209
+ elsif [characteristics[:origin_airport], characteristics[:destination_airport]].any?(&:united_states?)
210
+ FlightDomesticity.find_by_name('international')
211
+ end
212
+ end
213
+
214
+ quorum 'from origin', :needs => :origin_airport do |characteristics|
215
+ if characteristics[:origin_airport].all_flights_from_here_domestic?
216
+ FlightDomesticity.find_by_name('domestic')
217
+ end
218
+ end
219
+
220
+ quorum 'from destination', :needs => :destination_airport do |characteristics|
221
+ if characteristics[:destination_airport].all_flights_to_here_domestic?
222
+ FlightDomesticity.find_by_name('domestic')
223
+ end
224
+ end
225
+
226
+ quorum 'from airline', :needs => :airline do |characteristics|
227
+ if characteristics[:airline].all_flights_domestic?
228
+ FlightDomesticity.find_by_name('domestic')
229
+ end
230
+ end
231
+ end
232
+
233
+ committee :seat_class_multiplier do
234
+ quorum 'from seat class', :needs => :seat_class do |characteristics|
235
+ characteristics[:seat_class].multiplier
236
+ end
237
+
238
+ quorum 'default' do
239
+ FlightSeatClass.fallback.andand.multiplier
240
+ end
241
+ end
242
+
243
+ committee :date do
244
+ quorum 'from creation date', :needs => :creation_date do |characteristics|
245
+ characteristics[:creation_date]
246
+ end
247
+
248
+ quorum 'from timeframe' do |characteristics, timeframe|
249
+ timeframe.andand.from
250
+ end
251
+ end
252
+
253
+ committee :cohort do
254
+ quorum 'from t100', :appreciates => FlightSegment::INPUT_CHARACTERISTICS do |characteristics|
255
+ needed_characteristics = characteristics
256
+ needed_characteristics.
257
+ reject! { |k,v| !FlightSegment::INPUT_CHARACTERISTICS.include?(k) }
258
+ cohort = FlightSegment.big_cohort needed_characteristics
259
+ if cohort.any?
260
+ cohort
261
+ else
262
+ nil
263
+ end
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,31 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Characterization
4
+ def self.included(base)
5
+ base.characterize do
6
+ has :date, :trumps => :year
7
+ has :year
8
+ has :time_of_day
9
+ has :origin_airport do |origin_airport|
10
+ origin_airport.reveals :destination_airport,
11
+ :trumps => [:distance_class, :domesticity, :distance_estimate]
12
+ end
13
+ has :distance_class
14
+ has :distance_estimate, :trumps => :distance_class, :measures => :length, :precision => 0
15
+ has :domesticity
16
+ has :airline
17
+ has :trips
18
+ has :emplanements_per_trip
19
+ has :seat_class
20
+ has :load_factor, :measures => :percentage
21
+ has :seats_estimate, :range => 1..500
22
+ has :aircraft_class, :trumps => [:propulsion, :fuel_type]
23
+ has :aircraft, :trumps => [:propulsion, :aircraft_class, :seats_estimate, :fuel_type]
24
+ has :propulsion, :trumps => :fuel_type
25
+
26
+ has :creation_date, :hidden => true
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,34 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Data
4
+ def self.included(base)
5
+ base.data_miner do
6
+ schema do
7
+ string 'origin_airport_id'
8
+ string 'destination_airport_id'
9
+ integer 'trips'
10
+ integer 'emplanements_per_trip'
11
+ float 'distance_estimate'
12
+ string 'distance_class_id'
13
+ string 'aircraft_id'
14
+ string 'aircraft_class_id'
15
+ string 'propulsion_id'
16
+ string 'fuel_type_id'
17
+ string 'airline_id'
18
+ string 'seat_class_id'
19
+ integer 'seats_estimate'
20
+ float 'load_factor'
21
+ string 'domesticity_id'
22
+ date 'date'
23
+ integer 'year'
24
+ time 'time_of_day'
25
+ end
26
+
27
+ process "pull dependencies" do
28
+ run_data_miner_on_belongs_to_associations
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,20 @@
1
+ module BrighterPlanet
2
+ module Flight
3
+ module Summarization
4
+ def self.included(base)
5
+ base.summarize do |has|
6
+ has.adjective 'one-way', :if => lambda { |flight| flight.trips == 1 }
7
+ has.adjective 'round-trip', :if => lambda { |flight| flight.trips == 1 }
8
+ has.adjective 'nonstop', :if => lambda { |flight| flight.emplanements_per_trip == 1 }
9
+ has.identity 'flight'
10
+ has.modifier lambda { |flight| "from #{flight.origin_airport.name}" }, :if => :origin_airport
11
+ has.modifier lambda { |flight| "to #{flight.destination_airport.name}" }, :if => :destination_airport
12
+ has.modifier lambda { |flight| "on a #{flight.vehicle}" }, :if => :vehicle
13
+ has.modifier lambda { |flight| "on #{flight.date.to_formatted_s(:archive)}"}, :if => :date
14
+ has.verb :take
15
+ has.aspect :perfect
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,25 @@
1
+ class Aircraft < ActiveRecord::Base
2
+ set_table_name 'aircraft'
3
+ set_primary_key :icao_code
4
+
5
+ belongs_to :aircraft_class, :foreign_key => 'brighter_planet_aircraft_class_code'
6
+ belongs_to :manufacturer, :foreign_key => 'manufacturer_name', :class_name => 'AircraftManufacturer'
7
+ has_many :segments, :foreign_key => 'bts_aircraft_type_code', :class_name => "FlightSegment", :primary_key => 'bts_aircraft_type_code'
8
+
9
+ falls_back_on :m3 => lambda { weighted_average(:m3, :weighted_by => [:segments, :passengers]) }, # 9.73423082858437e-08 r7110: 8.6540464368905e-8 r6972: 8.37e-8
10
+ :m2 => lambda { weighted_average(:m2, :weighted_by => [:segments, :passengers]) }, # -0.000134350543484608 r7110: -0.00015337661447817 r6972: -4.09e-5
11
+ :m1 => lambda { weighted_average(:m1, :weighted_by => [:segments, :passengers]) }, # 6.7728101555467 r7110: 4.7781966869412 r6972: 7.85
12
+ :endpoint_fuel => lambda { weighted_average(:endpoint_fuel, :weighted_by => [:segments, :passengers]) }, # 1527.81790006167 r7110: 1065.3476555284 r6972: 1.72e3
13
+ :seats => lambda { weighted_average(:seats, :weighted_by => [:segments, :passengers]) } # 62.1741
14
+
15
+ class << self
16
+ def loose_search_columns
17
+ @_loose_search_columns ||= [primary_key, :manufacturer_name, :name]
18
+ end
19
+
20
+ # search by name
21
+ def loose_right_reader
22
+ @_loose_right_reader ||= lambda { |record| record[1,2].join ' ' }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,6 @@
1
+ class AircraftClass < ActiveRecord::Base
2
+ set_primary_key :brighter_planet_aircraft_class_code
3
+
4
+ has_many :aircraft, :foreign_key => 'brighter_planet_aircraft_class_code'
5
+ # has_many :airline_aircraft_seat_classes, :through => :aircraft
6
+ end
@@ -0,0 +1,5 @@
1
+ class AircraftManufacturer < ActiveRecord::Base
2
+ set_primary_key :name
3
+
4
+ has_many :aircraft, :foreign_key => 'manufacturer_name'
5
+ end
@@ -0,0 +1,24 @@
1
+ class Airline < ActiveRecord::Base
2
+ set_primary_key :iata_code
3
+
4
+ # has_many :airline_aircraft, :class_name => 'AirlineAircraft'
5
+ # has_many :seat_classes, :class_name => 'AirlineSeatClass'
6
+ has_many :segments, :class_name => "FlightSegment", :foreign_key => 'airline_iata_code'
7
+ # has_many :airline_aircraft_seat_classes, :class_name => 'AirlineAircraftSeatClass'
8
+
9
+
10
+ class << self
11
+ def loose_search_columns
12
+ @_loose_search_columns ||= [primary_key, :name]
13
+ end
14
+
15
+ # search by name
16
+ def loose_right_reader
17
+ @_loose_right_reader ||= lambda { |record| record[1] }
18
+ end
19
+ end
20
+
21
+ def all_flights_domestic?
22
+ !international?
23
+ end
24
+ end
@@ -0,0 +1,52 @@
1
+ class Airport < ActiveRecord::Base
2
+ set_primary_key :iata_code
3
+
4
+ acts_as_mappable :default_units => :nms,
5
+ :lat_column_name => :latitude,
6
+ :lng_column_name => :longitude
7
+
8
+ class << self
9
+ def loose_search_columns
10
+ @_loose_search_columns ||= [primary_key, :city]
11
+ end
12
+
13
+ # search by name
14
+ def loose_right_reader
15
+ @_loose_right_reader ||= lambda { |record| record[1] }
16
+ end
17
+ end
18
+
19
+ def name_and_location
20
+ [ name.to_s.titleize, city, country.andand.name ].select(&:present?).join ', '
21
+ end
22
+
23
+ # --------------------------------
24
+ # virtual has_many association
25
+ # has_many :segments won't work because there's no general way to specify the correct conditions
26
+ # even if you get clever with it, like
27
+ # has_many :segments,
28
+ # :class_name => 'FlightSegment',
29
+ # :foreign_key => 'origin_airport_id',
30
+ # :conditions => 'flight_segments.destination_airport_id = #{id}'
31
+ # you get queries like "`flight_segments`.origin_airport_id = 3654 AND (flight_segments.destination_airport_id = 3654))"
32
+ # in which you notice the AND which must be an OR
33
+ # and you can't just do finder_sql, because that breaks any other :select
34
+ def segments
35
+ FlightSegment.scoped :conditions => ['origin_airport_id = ? OR destination_airport_id = ?', id, id]
36
+ end
37
+ # --------------------------------
38
+
39
+ belongs_to :country, :foreign_key => 'country_iso_3166_code'
40
+
41
+ def all_flights_from_here_domestic?
42
+ !international_origin?
43
+ end
44
+
45
+ def all_flights_to_here_domestic?
46
+ !international_destination?
47
+ end
48
+
49
+ def united_states?
50
+ country == Country.united_states
51
+ end
52
+ end