earth 0.5.4 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. data/Gemfile +1 -0
  2. data/TODO +2 -0
  3. data/bin/earth_tester.rb +62 -9
  4. data/earth.gemspec +7 -4
  5. data/lib/earth.rb +18 -17
  6. data/lib/earth/air.rb +2 -0
  7. data/lib/earth/air/aircraft.rb +76 -19
  8. data/lib/earth/air/aircraft/data_miner.rb +1 -63
  9. data/lib/earth/air/aircraft_class.rb +48 -12
  10. data/lib/earth/air/aircraft_class/data_miner.rb +3 -46
  11. data/lib/earth/air/aircraft_fuel_use_equation.rb +11 -13
  12. data/lib/earth/air/aircraft_instance.rb +9 -0
  13. data/lib/earth/air/aircraft_instance_seat_class.rb +12 -0
  14. data/lib/earth/air/airline.rb +6 -8
  15. data/lib/earth/air/airline/data_miner.rb +3 -3
  16. data/lib/earth/air/airport.rb +8 -10
  17. data/lib/earth/air/airport/data_miner.rb +1 -1
  18. data/lib/earth/air/bts_aircraft.rb +3 -5
  19. data/lib/earth/air/flight_distance_class.rb +4 -6
  20. data/lib/earth/air/flight_seat_class.rb +5 -7
  21. data/lib/earth/air/flight_segment.rb +58 -42
  22. data/lib/earth/air/flight_segment/data_miner.rb +11 -26
  23. data/lib/earth/automobile.rb +1 -0
  24. data/lib/earth/automobile/automobile_fuel.rb +78 -25
  25. data/lib/earth/automobile/automobile_fuel/data_miner.rb +5 -72
  26. data/lib/earth/automobile/automobile_make.rb +14 -6
  27. data/lib/earth/automobile/automobile_make/data_miner.rb +20 -35
  28. data/lib/earth/automobile/automobile_make_fleet_year.rb +39 -10
  29. data/lib/earth/automobile/automobile_make_fleet_year/data_miner.rb +0 -29
  30. data/lib/earth/automobile/automobile_make_model.rb +30 -9
  31. data/lib/earth/automobile/automobile_make_model/data_miner.rb +11 -26
  32. data/lib/earth/automobile/automobile_make_model_year.rb +41 -12
  33. data/lib/earth/automobile/automobile_make_model_year/data_miner.rb +14 -34
  34. data/lib/earth/automobile/automobile_make_model_year_variant.rb +56 -38
  35. data/lib/earth/automobile/automobile_make_model_year_variant/data_miner.rb +15 -61
  36. data/lib/earth/automobile/automobile_make_year.rb +15 -8
  37. data/lib/earth/automobile/automobile_make_year/data_miner.rb +17 -51
  38. data/lib/earth/automobile/automobile_size_class.rb +78 -14
  39. data/lib/earth/automobile/automobile_size_class/data_miner.rb +0 -66
  40. data/lib/earth/automobile/automobile_size_class_year.rb +38 -10
  41. data/lib/earth/automobile/automobile_size_class_year/data_miner.rb +0 -30
  42. data/lib/earth/automobile/automobile_type_fuel_age.rb +60 -11
  43. data/lib/earth/automobile/automobile_type_fuel_age/data_miner.rb +10 -65
  44. data/lib/earth/automobile/automobile_type_fuel_control.rb +41 -10
  45. data/lib/earth/automobile/automobile_type_fuel_control/data_miner.rb +0 -33
  46. data/lib/earth/automobile/automobile_type_fuel_year.rb +60 -15
  47. data/lib/earth/automobile/automobile_type_fuel_year/data_miner.rb +1 -52
  48. data/lib/earth/automobile/automobile_type_fuel_year_age.rb +64 -12
  49. data/lib/earth/automobile/automobile_type_fuel_year_age/data_miner.rb +1 -59
  50. data/lib/earth/automobile/automobile_type_fuel_year_control.rb +31 -10
  51. data/lib/earth/automobile/automobile_type_fuel_year_control/data_miner.rb +2 -32
  52. data/lib/earth/automobile/automobile_type_year.rb +51 -10
  53. data/lib/earth/automobile/automobile_type_year/data_miner.rb +1 -43
  54. data/lib/earth/automobile/data_miner.rb +1 -0
  55. data/lib/earth/bus/bus_class.rb +108 -28
  56. data/lib/earth/bus/bus_class/data_miner.rb +0 -88
  57. data/lib/earth/bus/bus_fuel.rb +83 -15
  58. data/lib/earth/bus/bus_fuel/data_miner.rb +0 -76
  59. data/lib/earth/bus/bus_fuel_control.rb +8 -10
  60. data/lib/earth/bus/bus_fuel_year_control.rb +7 -9
  61. data/lib/earth/bus/bus_fuel_year_control/data_miner.rb +1 -5
  62. data/lib/earth/computation/computation_carrier.rb +11 -5
  63. data/lib/earth/computation/computation_carrier/data_miner.rb +0 -7
  64. data/lib/earth/computation/computation_carrier_instance_class.rb +25 -8
  65. data/lib/earth/computation/computation_carrier_instance_class/data_miner.rb +0 -18
  66. data/lib/earth/computation/computation_carrier_region.rb +5 -7
  67. data/lib/earth/diet/diet_class.rb +14 -16
  68. data/lib/earth/diet/food_group.rb +7 -9
  69. data/lib/earth/fuel/fuel.rb +14 -16
  70. data/lib/earth/fuel/fuel/data_miner.rb +7 -3
  71. data/lib/earth/fuel/fuel_price.rb +4 -6
  72. data/lib/earth/fuel/fuel_type.rb +12 -14
  73. data/lib/earth/fuel/fuel_year.rb +57 -16
  74. data/lib/earth/fuel/fuel_year/data_miner.rb +0 -43
  75. data/lib/earth/fuel/greenhouse_gas.rb +46 -9
  76. data/lib/earth/fuel/greenhouse_gas/data_miner.rb +0 -38
  77. data/lib/earth/hospitality/lodging_class.rb +10 -12
  78. data/lib/earth/industry/industry.rb +3 -5
  79. data/lib/earth/industry/industry_product.rb +6 -8
  80. data/lib/earth/industry/industry_product_line.rb +5 -7
  81. data/lib/earth/industry/industry_sector.rb +5 -7
  82. data/lib/earth/industry/merchant.rb +4 -6
  83. data/lib/earth/industry/merchant_category.rb +3 -5
  84. data/lib/earth/industry/merchant_category_industry.rb +5 -7
  85. data/lib/earth/industry/product_line.rb +5 -7
  86. data/lib/earth/industry/product_line_industry_product.rb +5 -7
  87. data/lib/earth/industry/sector.rb +5 -7
  88. data/lib/earth/locality/census_division.rb +21 -23
  89. data/lib/earth/locality/census_region.rb +3 -5
  90. data/lib/earth/locality/climate_division.rb +5 -7
  91. data/lib/earth/locality/country.rb +13 -15
  92. data/lib/earth/locality/egrid_region.rb +14 -5
  93. data/lib/earth/locality/egrid_region/data_miner.rb +0 -10
  94. data/lib/earth/locality/egrid_subregion.rb +71 -18
  95. data/lib/earth/locality/egrid_subregion/data_miner.rb +0 -59
  96. data/lib/earth/locality/petroleum_administration_for_defense_district.rb +6 -8
  97. data/lib/earth/locality/state.rb +6 -8
  98. data/lib/earth/locality/urbanity.rb +2 -4
  99. data/lib/earth/locality/urbanity/data_miner.rb +7 -3
  100. data/lib/earth/locality/zip_code.rb +8 -10
  101. data/lib/earth/pet/breed.rb +5 -7
  102. data/lib/earth/pet/breed_gender.rb +6 -8
  103. data/lib/earth/pet/gender.rb +2 -4
  104. data/lib/earth/pet/species.rb +15 -17
  105. data/lib/earth/rail/rail_class.rb +64 -14
  106. data/lib/earth/rail/rail_class/data_miner.rb +0 -51
  107. data/lib/earth/residence/air_conditioner_use.rb +4 -6
  108. data/lib/earth/residence/air_conditioner_use/data_miner.rb +7 -3
  109. data/lib/earth/residence/clothes_machine_use.rb +4 -6
  110. data/lib/earth/residence/clothes_machine_use/data_miner.rb +11 -9
  111. data/lib/earth/residence/dishwasher_use.rb +4 -6
  112. data/lib/earth/residence/dishwasher_use/data_miner.rb +11 -9
  113. data/lib/earth/residence/residence_appliance.rb +4 -6
  114. data/lib/earth/residence/residence_class.rb +2 -4
  115. data/lib/earth/residence/residence_class/data_miner.rb +7 -3
  116. data/lib/earth/residence/residence_fuel_price.rb +12 -14
  117. data/lib/earth/residence/residence_fuel_price/data_miner.rb +7 -0
  118. data/lib/earth/residence/residence_fuel_type.rb +6 -8
  119. data/lib/earth/residence/residential_energy_consumption_survey_response.rb +91 -93
  120. data/lib/earth/residence/residential_energy_consumption_survey_response/data_miner.rb +9 -8
  121. data/lib/earth/shipping/carrier.rb +57 -10
  122. data/lib/earth/shipping/carrier/data_miner.rb +0 -48
  123. data/lib/earth/shipping/carrier_mode.rb +41 -10
  124. data/lib/earth/shipping/carrier_mode/data_miner.rb +0 -33
  125. data/lib/earth/shipping/shipment_mode.rb +30 -7
  126. data/lib/earth/shipping/shipment_mode/data_miner.rb +0 -23
  127. data/lib/earth/utils.rb +45 -0
  128. data/lib/earth/version.rb +1 -1
  129. data/spec/earth/air/aircraft_spec.rb +1 -1
  130. data/vendor/clean_find_in_batches/init.rb +35 -0
  131. metadata +74 -47
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
1
  gem 'data_miner', :path => ENV['LOCAL_DATA_MINER'] if ENV['LOCAL_DATA_MINER']
2
+ gem 'mini_record', :path => ENV['LOCAL_MINI_RECORD'] if ENV['LOCAL_MINI_RECORD']
2
3
  gem 'force_schema', :path => ENV['LOCAL_FORCE_SCHEMA'] if ENV['LOCAL_FORCE_SCHEMA']
3
4
 
4
5
  source :rubygems
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ * convert former/current verify blocks into table_warnings warn {...} blocks
2
+ * convert tests to minitest
@@ -14,19 +14,72 @@ end
14
14
  require 'active_support/all'
15
15
  require 'active_record'
16
16
 
17
- ActiveRecord::Base.establish_connection(
18
- 'adapter' => 'mysql',
19
- 'database' => 'test_earth',
20
- 'username' => 'root',
21
- 'password' => 'password',
22
- 'encoding' => 'utf8' # very, very important
23
- )
17
+ case ENV['EARTH_DB_ADAPTER']
18
+ when 'mysql'
19
+ adapter = 'mysql'
20
+ database = 'test_earth'
21
+ username = 'root'
22
+ password = 'password'
23
+
24
+ # system %{mysql -u #{username} -p#{password} -e "DROP DATABASE #{database}"}
25
+ # system %{mysql -u #{username} -p#{password} -e "CREATE DATABASE #{database}"}
26
+ when 'sqlite'
27
+ adapter = 'sqlite3'
28
+ database = ':memory:'
29
+ username = nil
30
+ password = nil
31
+ else
32
+ adapter = 'postgresql'
33
+ username = ENV['EARTH_POSTGRES_USERNAME'] || `whoami`.chomp
34
+ password = ENV['EARTH_POSTGRES_PASSWORD']
35
+ database = ENV['EARTH_POSTGRES_DATABASE'] || 'test_earth'
36
+
37
+ createdb_bin = ENV['EARTH_CREATEDB_BIN'] || 'createdb'
38
+ dropdb_bin = ENV['EARTH_DROPDB_BIN'] || 'dropdb'
39
+ # system %{#{dropdb_bin} #{database}}
40
+ # system %{#{createdb_bin} #{database}}
41
+ end
42
+
43
+ config = {
44
+ 'encoding' => 'utf8',
45
+ 'adapter' => adapter,
46
+ 'database' => database,
47
+ }
48
+ config['username'] = username if username
49
+ config['password'] = password if password
50
+
51
+ ActiveRecord::Base.establish_connection config
24
52
 
25
53
  require 'earth'
26
54
 
27
- Earth.init ARGV[0], :load_data_miner => true, :apply_schemas => true
55
+ domain = ARGV[0]
56
+
57
+ Earth.init domain, :load_data_miner => true, :apply_schemas => true
28
58
 
29
59
  ActiveRecord::Base.logger = Logger.new $stderr
30
60
  ActiveRecord::Base.logger.level = Logger::INFO
31
61
 
32
- ARGV[1].camelcase.constantize.run_data_miner!
62
+ def show_resource(resource)
63
+ resource_model = resource.constantize
64
+ if (warnings = resource_model.table_warnings).any?
65
+ $stderr.puts
66
+ $stderr.puts '#'*50
67
+ $stderr.puts "# #{resource}"
68
+ $stderr.puts '#'*50
69
+ $stderr.puts
70
+ warnings.each do |warning|
71
+ $stderr.puts "* #{warning}"
72
+ end
73
+ end
74
+ end
75
+
76
+ if (resource = ARGV[1].to_s.camelcase).present?
77
+ resource.constantize.run_data_miner!
78
+ show_resource resource
79
+ else
80
+ DataMiner.run
81
+ Earth.search(domain).each do |resource|
82
+ show_resource resource
83
+ end
84
+ end
85
+ # ARGV[1].split(/[^a-z_]/i).each { |underscore| underscore.camelcase.constantize.run_data_miner! }
@@ -27,23 +27,26 @@ Gem::Specification.new do |s|
27
27
  s.require_paths = ["lib"]
28
28
  s.rubygems_version = %q{1.3.7}
29
29
 
30
- s.add_runtime_dependency 'data_miner'
30
+ s.add_runtime_dependency 'data_miner', '>=1.3'
31
31
  s.add_runtime_dependency 'to_regexp'
32
32
  s.add_runtime_dependency 'cohort_scope'
33
+ s.add_runtime_dependency 'table_warnings', '>=0.0.6'
33
34
  s.add_runtime_dependency 'remote_table', '>=1.2.3'
34
35
  s.add_runtime_dependency 'falls_back_on'
35
36
  s.add_runtime_dependency 'fixed_width-multibyte'
36
37
  s.add_runtime_dependency 'geokit-rails'
37
38
  s.add_runtime_dependency 'loose_tight_dictionary', '>=0.2.3'
38
- s.add_runtime_dependency 'weighted_average'
39
- s.add_runtime_dependency 'force_schema', '>=0.0.2'
39
+ s.add_runtime_dependency 'weighted_average', '>=1'
40
+ # s.add_runtime_dependency 'mini_record' # need https://github.com/DAddYE/mini_record/pull/7 - for now install manually from https://github.com/seamusabshere/mini_record
41
+ s.add_runtime_dependency 'activesupport'
42
+ s.add_runtime_dependency 'activerecord'
40
43
  s.add_development_dependency 'bundler'
41
44
  s.add_development_dependency 'bueller'
42
45
  s.add_development_dependency 'cucumber'
43
46
  s.add_development_dependency 'rake'
44
47
  s.add_development_dependency 'rdoc'
45
- s.add_development_dependency 'rdoc'
46
48
  s.add_development_dependency 'rspec'
47
49
  s.add_development_dependency 'sqlite3-ruby'
48
50
  s.add_development_dependency 'mysql' # for bin/earth_tester.rb
51
+ s.add_development_dependency 'pg'
49
52
  end
@@ -1,3 +1,4 @@
1
+ require 'active_support/core_ext'
1
2
  require 'active_record'
2
3
  require 'cohort_scope'
3
4
  require 'earth/conversions_ext'
@@ -7,22 +8,15 @@ require 'falls_back_on'
7
8
  require 'weighted_average'
8
9
  require 'fixed_width'
9
10
  require 'errata'
10
- require 'force_schema'
11
+ require 'mini_record'
12
+ require 'table_warnings'
11
13
  require 'loose_tight_dictionary'
12
14
  require 'loose_tight_dictionary/cached_result'
13
15
 
14
- # hackety hack
15
- def INSERT_IGNORE(cmd)
16
- if ActiveRecord::Base.connection.adapter_name.downcase == 'sqlite'
17
- prefix = 'INSERT'
18
- else
19
- prefix = 'INSERT IGNORE'
20
- end
21
- ActiveRecord::Base.connection.execute "#{prefix} #{cmd}"
22
- end
23
-
24
16
  # The earth module is an interface for loading data models from various domains.
25
17
  module Earth
18
+ autoload :Utils, 'earth/utils'
19
+
26
20
  extend self
27
21
 
28
22
  # Takes argument like Earth.search(['air'])
@@ -78,6 +72,7 @@ module Earth
78
72
  options = args.last.is_a?(Hash) ? args.pop.symbolize_keys : {}
79
73
  domains = args.empty? ? [ :all ] : args.map(&:to_sym)
80
74
 
75
+ _configure_database_connection
81
76
  _load_plugins
82
77
  _load_domains domains, options
83
78
  _decorate_resources options
@@ -86,6 +81,12 @@ module Earth
86
81
 
87
82
  private
88
83
 
84
+ def _configure_database_connection
85
+ if ActiveRecord::Base.connection.adapter_name =~ /mysql/i
86
+ ActiveRecord::Base.connection.execute("SET SQL_MODE = 'PIPES_AS_CONCAT'")
87
+ end
88
+ end
89
+
89
90
  # TODO sabshere don't use directories to specify domains
90
91
  # * you have 20 million data_miner.rb files which are easy to confuse
91
92
  # * you have to go all over the filesystem to figure things out
@@ -119,13 +120,13 @@ module Earth
119
120
  resource_model.data_miner_config.steps.push pull_dependencies_step
120
121
  end
121
122
 
122
- def _prepend_force_schema_step_to_data_miner(resource)
123
+ def _prepend_auto_upgrade_step_to_data_miner(resource)
123
124
  resource_model = resource.constantize
124
- return if resource_model.data_miner_config.steps.any? { |step| step.description == :force_schema! }
125
+ return if resource_model.data_miner_config.steps.any? { |step| step.description == :auto_upgrade! }
125
126
 
126
- force_schema_step = DataMiner::Process.new resource_model.data_miner_config, :force_schema!
127
+ auto_upgrade_step = DataMiner::Process.new resource_model.data_miner_config, :auto_upgrade!
127
128
 
128
- resource_model.data_miner_config.steps.unshift force_schema_step
129
+ resource_model.data_miner_config.steps.unshift auto_upgrade_step
129
130
  end
130
131
 
131
132
  TAPS_STEP = 'Tap the Brighter Planet data server'
@@ -144,7 +145,7 @@ module Earth
144
145
  next unless ::Object.const_defined?(resource)
145
146
  _append_pull_dependencies_step_to_data_miner resource
146
147
  if options[:apply_schemas] or options[:load_data_miner]
147
- _prepend_force_schema_step_to_data_miner resource
148
+ _prepend_auto_upgrade_step_to_data_miner resource
148
149
  else
149
150
  _prepend_taps_step_to_data_miner resource
150
151
  end
@@ -154,7 +155,7 @@ module Earth
154
155
  def _load_schemas(selected_resources, options)
155
156
  return unless options[:apply_schemas]
156
157
  selected_resources.each do |resource|
157
- resource.constantize.force_schema!
158
+ resource.constantize.auto_upgrade!
158
159
  end
159
160
  end
160
161
  end
@@ -1,6 +1,8 @@
1
1
  require 'earth/air/aircraft'
2
2
  require 'earth/air/aircraft_class'
3
3
  require 'earth/air/aircraft_fuel_use_equation'
4
+ require 'earth/air/aircraft_instance'
5
+ require 'earth/air/aircraft_instance_seat_class'
4
6
  require 'earth/air/airline'
5
7
  require 'earth/air/airport'
6
8
  require 'earth/air/bts_aircraft'
@@ -4,8 +4,24 @@ class Aircraft < ActiveRecord::Base
4
4
  belongs_to :aircraft_class, :foreign_key => 'class_code', :primary_key => 'code'
5
5
  belongs_to :fuel_use_equation, :foreign_key => 'fuel_use_code', :primary_key => 'code', :class_name => 'AircraftFuelUseEquation'
6
6
 
7
- # set up a loose_tight_dictionary for matching Aircraft description with FlightSegment aircraft_description
7
+ col :icao_code
8
+ col :manufacturer_name
9
+ col :model_name
10
+ col :description
11
+ col :aircraft_type
12
+ col :engine_type
13
+ col :engines, :type => :integer
14
+ col :weight_class
15
+ col :class_code
16
+ col :fuel_use_code
17
+ col :seats, :type => :float
18
+ col :passengers, :type => :float
19
+
20
+ # Enable aircraft.flight_segments
21
+ cache_loose_tight_dictionary_matches_with :flight_segments, :primary_key => :description, :foreign_key => :aircraft_description
22
+
8
23
  class << self
24
+ # set up a loose_tight_dictionary for matching Aircraft description with FlightSegment aircraft_description
9
25
  def loose_tight_dictionary
10
26
  @loose_tight_dictionary ||= LooseTightDictionary.new(Aircraft.all,
11
27
  :haystack_reader => lambda { |record| record.description },
@@ -15,23 +31,64 @@ class Aircraft < ActiveRecord::Base
15
31
  :must_match_blocking => true,
16
32
  :first_blocking_decides => true)
17
33
  end
18
- end
19
34
 
20
- force_schema do
21
- string 'icao_code'
22
- string 'manufacturer_name'
23
- string 'model_name'
24
- string 'description'
25
- string 'aircraft_type'
26
- string 'engine_type'
27
- integer 'engines'
28
- string 'weight_class'
29
- string 'class_code'
30
- string 'fuel_use_code'
31
- float 'seats'
32
- float 'passengers'
33
- end
35
+ # FIXME TODO do we want to restrict this to certain years?
36
+ # Derive some average characteristics from flight segments
37
+ def update_averages!
38
+ manually_cache_flight_segments!
39
+ find_each do |aircraft|
40
+ aircraft.seats = aircraft.flight_segments.weighted_average :seats_per_flight, :weighted_by => :passengers
41
+ aircraft.passengers = aircraft.flight_segments.sum :passengers
42
+ aircraft.save!
43
+ end
44
+ end
34
45
 
35
- # Enable aircraft.flight_segments
36
- cache_loose_tight_dictionary_matches_with :flight_segments, :primary_key => :description, :foreign_key => :aircraft_description
37
- end
46
+ # Cache fuzzy matches between FlightSegment aircraft_description and Aircraft description
47
+ def manually_cache_flight_segments!
48
+ FlightSegment.run_data_miner!
49
+ LooseTightDictionary::CachedResult.setup
50
+ connection.select_values("SELECT DISTINCT(aircraft_description) FROM flight_segments WHERE aircraft_description IS NOT NULL").each do |original_description|
51
+ # If the flight segment's aircraft_description contains '/' then it describes multiple aircraft.
52
+ # We need to synthesize descriptions for those aircraft, find all Aircraft that fuzzily match the
53
+ # synthesized descriptions, and associate those Aircraft with the original aircraft_description.
54
+ # e.g. boeing 747-100/200
55
+ if original_description.include?('/')
56
+ # Pull out the complete first aircraft description
57
+ # e.g. 'boeing 747-100'
58
+ first_description = original_description.split('/')[0]
59
+
60
+ # Pull out the root of the description - the text up to and including the last ' ' or '-'
61
+ # e.g. 'boeing 747-'
62
+ root_length = first_description.rindex(/[ \-]/)
63
+ root = first_description.slice(0..root_length)
64
+
65
+ # Pull out the suffixes - the text separated by forward slashes
66
+ # e.g. ['100', '200']
67
+ suffixes = original_description.split(root)[1].split('/')
68
+
69
+ # Create an array of synthesized descriptions by appending each suffix to the root
70
+ # e.g. ['boeing 747-100', 'boeing 747-200']
71
+ suffixes.map{ |suffix| root + suffix }.each do |synthesized_description|
72
+ # Look up the Aircraft that match each synthesized description and associate
73
+ # them with the original flight segment aircraft_description
74
+ Aircraft.loose_tight_dictionary.find_all(synthesized_description).each do |aircraft|
75
+ attrs = {
76
+ :a_class => "Aircraft",
77
+ :a => aircraft.description,
78
+ :b_class => "FlightSegment",
79
+ :b => original_description
80
+ }
81
+ unless ::LooseTightDictionary::CachedResult.exists? attrs
82
+ ::LooseTightDictionary::CachedResult.create! attrs
83
+ end
84
+ end
85
+ end
86
+ # If the flight segment's aircraft_description doesn't contain '/' we can use
87
+ # a method provided by loose_tight_dictionary to associate it with Aircraft
88
+ else
89
+ FlightSegment.find_by_aircraft_description(original_description).cache_aircraft!
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -21,68 +21,6 @@ Aircraft.class_eval do
21
21
  @manufacturer_whitelist.any? { |manufacturer_regexp| manufacturer_regexp.match candidate }
22
22
  end
23
23
 
24
- # FIXME TODO do we want to restrict this to certain years?
25
- # Derive some average characteristics from flight segments
26
- def self.update_averages!
27
- FlightSegment.run_data_miner!
28
- manually_cache_flight_segments!
29
- find_each do |aircraft|
30
- aircraft.seats = aircraft.flight_segments.weighted_average :seats_per_flight, :weighted_by => :passengers
31
- aircraft.passengers = aircraft.flight_segments.sum :passengers
32
- aircraft.save
33
- end
34
- end
35
-
36
- # Cache fuzzy matches between FlightSegment aircraft_description and Aircraft description
37
- def self.manually_cache_flight_segments!
38
- FlightSegment.run_data_miner!
39
- LooseTightDictionary::CachedResult.setup
40
- FlightSegment.find_by_sql("SELECT * FROM flight_segments GROUP BY aircraft_description HAVING aircraft_description IS NOT NULL").each do |flight_segment|
41
- original_description = flight_segment.aircraft_description
42
-
43
- # If the flight segment's aircraft_description contains '/' then it describes multiple aircraft.
44
- # We need to synthesize descriptions for those aircraft, find all Aircraft that fuzzily match the
45
- # synthesized descriptions, and associate those Aircraft with the original aircraft_description.
46
- # e.g. boeing 747-100/200
47
- if original_description.include?("/")
48
- # Pull out the complete first aircraft description
49
- # e.g. 'boeing 747-100'
50
- first_description = original_description.split('/')[0]
51
-
52
- # Pull out the root of the description - the text up to and including the last ' ' or '-'
53
- # e.g. 'boeing 747-'
54
- root_length = first_description.rindex(/[ \-]/)
55
- root = first_description.slice(0..root_length)
56
-
57
- # Pull out the suffixes - the text separated by forward slashes
58
- # e.g. ['100', '200']
59
- suffixes = original_description.split(root)[1].split('/')
60
-
61
- # Create an array of synthesized descriptions by appending each suffix to the root
62
- # e.g. ['boeing 747-100', 'boeing 747-200']
63
- suffixes.map{ |suffix| root + suffix }.each do |synthesized_description|
64
- # Look up the Aircraft that match each synthesized description and associate
65
- # them with the original flight segment aircraft_description
66
- Aircraft.loose_tight_dictionary.find_all(synthesized_description).each do |aircraft|
67
- attrs = {
68
- :a_class => "Aircraft",
69
- :a => aircraft.description,
70
- :b_class => "FlightSegment",
71
- :b => original_description
72
- }
73
- unless ::LooseTightDictionary::CachedResult.exists? attrs
74
- ::LooseTightDictionary::CachedResult.create! attrs
75
- end
76
- end
77
- end
78
- # If the flight segment's aircraft_description doesn't contain '/' we can use
79
- # a method provided by loose_tight_dictionary to associate it with Aircraft
80
- else
81
- flight_segment.cache_aircraft!
82
- end
83
- end
84
- end
85
-
86
24
  data_miner do
87
25
  ('A'..'Z').each do |letter|
88
26
  import("aircraft made by whitelisted manufacturers whose ICAO code starts with '#{letter}' from the FAA",
@@ -140,7 +78,7 @@ Aircraft.class_eval do
140
78
  end
141
79
 
142
80
  process :update_averages!
143
-
81
+
144
82
  # FIXME TODO verify this
145
83
  end
146
84
  end
@@ -2,6 +2,17 @@ class AircraftClass < ActiveRecord::Base
2
2
  set_primary_key :code
3
3
 
4
4
  has_many :aircraft, :foreign_key => 'class_code', :primary_key => 'code'
5
+
6
+ col :code
7
+ col :m3, :type => :float
8
+ col :m3_units
9
+ col :m2, :type => :float
10
+ col :m2_units
11
+ col :m1, :type => :float
12
+ col :m1_units
13
+ col :b, :type => :float
14
+ col :b_units
15
+ col :seats, :type => :float
5
16
 
6
17
  def fuel_use_coefficients
7
18
  [m3, m2, m1, b]
@@ -10,17 +21,42 @@ class AircraftClass < ActiveRecord::Base
10
21
  def valid_fuel_use_equation?
11
22
  fuel_use_coefficients.all?(&:present?) and fuel_use_coefficients.any?(&:nonzero?)
12
23
  end
13
-
14
- force_schema do
15
- string 'code'
16
- float 'm3'
17
- string 'm3_units'
18
- float 'm2'
19
- string 'm2_units'
20
- float 'm1'
21
- string 'm1_units'
22
- float 'b'
23
- string 'b_units'
24
- float 'seats'
24
+
25
+ class << self
26
+ def update_averages!
27
+ Aircraft.run_data_miner!
28
+ AircraftFuelUseEquation.run_data_miner!
29
+ find_each do |aircraft_class|
30
+ cumulative_passengers = 0
31
+ aircraft_class.m3 = 0
32
+ aircraft_class.m2 = 0
33
+ aircraft_class.m1 = 0
34
+ aircraft_class.b = 0
35
+
36
+ aircraft_class.aircraft.where('passengers > 0 AND fuel_use_code IS NOT NULL').each do |a|
37
+ cumulative_passengers += a.passengers
38
+ aircraft_class.m3 += a.fuel_use_equation.m3 * a.passengers
39
+ aircraft_class.m2 += a.fuel_use_equation.m2 * a.passengers
40
+ aircraft_class.m1 += a.fuel_use_equation.m1 * a.passengers
41
+ aircraft_class.b += a.fuel_use_equation.b * a.passengers
42
+ end
43
+
44
+ if cumulative_passengers > 0
45
+ aircraft_class.m3 /= cumulative_passengers
46
+ aircraft_class.m2 /= cumulative_passengers
47
+ aircraft_class.m1 /= cumulative_passengers
48
+ aircraft_class.b /= cumulative_passengers
49
+ end
50
+
51
+ aircraft_class.seats = aircraft_class.aircraft.weighted_average(:seats, :weighted_by => :passengers)
52
+
53
+ aircraft_class.m3_units = 'kilograms_per_cubic_nautical_mile'
54
+ aircraft_class.m2_units = 'kilograms_per_square_nautical_mile'
55
+ aircraft_class.m1_units = 'kilograms_per_nautical_mile'
56
+ aircraft_class.b_units = 'kilograms'
57
+
58
+ aircraft_class.save!
59
+ end
60
+ end
25
61
  end
26
62
  end