earth 0.5.4 → 0.6.0

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.
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