worlddb 1.6.4 → 1.6.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gemtest ADDED
File without changes
data/Manifest.txt CHANGED
@@ -23,3 +23,5 @@ lib/worlddb/schema.rb
23
23
  lib/worlddb/stats.rb
24
24
  lib/worlddb/utils.rb
25
25
  lib/worlddb/version.rb
26
+ test/helper.rb
27
+ test/test_values.rb
@@ -3,6 +3,14 @@
3
3
  module WorldDb::Models
4
4
 
5
5
  class City < ActiveRecord::Base
6
+
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
6
14
  self.table_name = 'cities'
7
15
 
8
16
  belongs_to :country, :class_name => 'Country', :foreign_key => 'country_id'
@@ -109,6 +117,152 @@ class City < ActiveRecord::Base
109
117
  end # each city
110
118
  end
111
119
 
120
+
121
+ def self.create_or_update_from_titles( titles, more_attributes = {} ) # ary of titles e.g. ['Wien', 'Graz'] etc.
122
+
123
+ # fix: add/configure logger for ActiveRecord!!!
124
+ logger = LogKernel::Logger.root
125
+
126
+ titles.each do |title|
127
+
128
+ new_attributes = {}
129
+ key = TextUtils.title_to_key( title ) # auto generate key from title
130
+
131
+ # check if it exists
132
+ # todo/fix: add country_id for lookup?
133
+ city = City.find_by_key( key )
134
+ if city.present?
135
+ logger.debug "update city #{city.id}-#{city.key}:"
136
+ else
137
+ logger.debug "create city:"
138
+ city = City.new
139
+ new_attributes[ :key ] = key
140
+ end
141
+
142
+ new_attributes[ :title ] = title
143
+
144
+ ## merge in passed in attribes (e.g. country_id etc.)
145
+ new_attributes.merge!( more_attributes )
146
+
147
+ logger.debug new_attributes.to_json
148
+
149
+ city.update_attributes!( new_attributes )
150
+
151
+ ### todo/fix: add captial ref to country/region
152
+ end # each city
153
+ end # method create_or_update_from_titles
154
+
155
+
156
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
157
+
158
+ ## opts e.g. :skip_tags true|false
159
+
160
+ ## fix: add/configure logger for ActiveRecord!!!
161
+ logger = LogKernel::Logger.root
162
+
163
+ value_numbers = []
164
+ value_tag_keys = []
165
+
166
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
167
+ value_tag_keys += find_tags_in_hash!( new_attributes )
168
+
169
+ new_attributes[ :c ] = true # assume city type by default (use metro,district to change in fixture)
170
+
171
+ ## check for optional values
172
+ values.each_with_index do |value,index|
173
+ if value =~ /^region:/ ## region:
174
+ value_region_key = value[7..-1] ## cut off region: prefix
175
+ ## NB: requires country_id to make unique!
176
+ value_region = Region.find_by_key_and_country_id!( value_region_key, new_attributes[:country_id] )
177
+ new_attributes[ :region_id ] = value_region.id
178
+ elsif value =~ /^metro$/ ## metro(politan area)
179
+ new_attributes[ :c ] = false # turn off default c|city flag; make it m|metro only
180
+ new_attributes[ :m ] = true
181
+ elsif value =~ /^country:/ ## country:
182
+ value_country_key = value[8..-1] ## cut off country: prefix
183
+ value_country = Country.find_by_key!( value_country_key )
184
+ new_attributes[ :country_id ] = value_country.id
185
+ elsif value =~ /^metro:/ ## metro:
186
+ value_city_key = value[6..-1] ## cut off metro: prefix
187
+ value_city = City.find_by_key!( value_city_key )
188
+ new_attributes[ :city_id ] = value_city.id
189
+ elsif value =~ /^city:/ ## city:
190
+ value_city_key = value[5..-1] ## cut off city: prefix
191
+ value_city = City.find_by_key!( value_city_key )
192
+ new_attributes[ :city_id ] = value_city.id
193
+ new_attributes[ :c ] = false # turn off default c|city flag; make it d|district only
194
+ new_attributes[ :d ] = true
195
+ elsif value =~ /^m:/ ## m:
196
+ value_popm_str = value[2..-1] ## cut off m: prefix
197
+ value_popm = value_popm_str.gsub(/[ _]/, '').to_i
198
+ new_attributes[ :popm ] = value_popm
199
+ new_attributes[ :m ] = true # auto-mark city as m|metro too
200
+ elsif is_region?( value ) ## assume region code e.g. TX for city
201
+ value_region = Region.find_by_key_and_country_id!( value.downcase, new_attributes[:country_id] )
202
+ new_attributes[ :region_id ] = value_region.id
203
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
204
+ new_attributes[ :code ] = value
205
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
206
+ ## allow numbers like 453 km²
207
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
208
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
209
+ value_numbers << value.gsub(/[ _]/, '').to_i
210
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
211
+ logger.debug " found tags: >>#{value}<<"
212
+ value_tag_keys += find_tags( value )
213
+ else
214
+ # issue warning: unknown type for value
215
+ logger.warn "unknown type for value >#{value}<"
216
+ end
217
+ end # each value
218
+
219
+ if value_numbers.size > 0
220
+ new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
221
+ new_attributes[ :area ] = value_numbers[1]
222
+ end # if value_numbers.size > 0
223
+
224
+ rec = City.find_by_key( new_attributes[ :key ] )
225
+
226
+ if rec.present?
227
+ logger.debug "update City #{rec.id}-#{rec.key}:"
228
+ else
229
+ logger.debug "create City:"
230
+ rec = City.new
231
+ end
232
+
233
+ logger.debug new_attributes.to_json
234
+
235
+ rec.update_attributes!( new_attributes )
236
+
237
+ ##################
238
+ ## add taggings
239
+
240
+ ## todo/fix: reuse - move add taggings into method etc.
241
+
242
+ if value_tag_keys.size > 0
243
+
244
+ if opts[:skip_tags].present?
245
+ logger.debug " skipping add taggings (flag skip_tag)"
246
+ else
247
+ value_tag_keys.uniq! # remove duplicates
248
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
249
+
250
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
251
+
252
+ value_tag_keys.each do |key|
253
+ tag = Tag.find_by_key( key )
254
+ if tag.nil? # create tag if it doesn't exit
255
+ logger.debug " creating tag >#{key}<"
256
+ tag = Tag.create!( key: key )
257
+ end
258
+ rec.tags << tag
259
+ end
260
+ end
261
+ end
262
+
263
+ rec
264
+ end # method create_or_update_from_values
265
+
112
266
  end # class Cities
113
267
 
114
268
  end # module WorldDb::Models
@@ -3,6 +3,14 @@
3
3
  module WorldDb::Models
4
4
 
5
5
  class Country < ActiveRecord::Base
6
+
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
6
14
  self.table_name = 'countries'
7
15
 
8
16
  belongs_to :continent, :class_name => 'Continent', :foreign_key => 'continent_id'
@@ -95,6 +103,153 @@ class Country < ActiveRecord::Base
95
103
  end # each country
96
104
  end
97
105
 
106
+
107
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
108
+
109
+ ## opts e.g. :skip_tags true|false
110
+
111
+ ## fix: add/configure logger for ActiveRecord!!!
112
+ logger = LogKernel::Logger.root
113
+
114
+ value_numbers = []
115
+ value_tag_keys = []
116
+ value_cities = []
117
+
118
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
119
+ value_tag_keys += find_tags_in_hash!( new_attributes )
120
+
121
+
122
+ new_attributes[ :c ] = true # assume country type by default (use supra,depend to change)
123
+
124
+ ## check for optional values
125
+ values.each_with_index do |value,index|
126
+ if value =~ /^supra$/ ## supra(national)
127
+ new_attributes[ :c ] = false # turn off default c|country flag; make it s|supra only
128
+ new_attributes[ :s ] = true
129
+ ## auto-add tag supra
130
+ value_tag_keys << 'supra'
131
+ elsif value =~ /^supra:/ ## supra:
132
+ value_country_key = value[6..-1] ## cut off supra: prefix
133
+ value_country = Country.find_by_key!( value_country_key )
134
+ new_attributes[ :country_id ] = value_country.id
135
+ elsif value =~ /^country:/ ## country:
136
+ value_country_key = value[8..-1] ## cut off country: prefix
137
+ value_country = Country.find_by_key!( value_country_key )
138
+ new_attributes[ :country_id ] = value_country.id
139
+ new_attributes[ :c ] = false # turn off default c|country flag; make it d|depend only
140
+ new_attributes[ :d ] = true
141
+ ## auto-add tag supra
142
+ value_tag_keys << 'territory' # rename tag to dependency? why? why not?
143
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
144
+ new_attributes[ :code ] = value
145
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
146
+ ## allow numbers like 453 km²
147
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
148
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
149
+ value_numbers << value.gsub(/[ _]/, '').to_i
150
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
151
+ logger.debug " found tags: >>#{value}<<"
152
+ value_tag_keys += find_tags( value )
153
+ else
154
+
155
+ ### assume it is the capital city - mark it for auto add
156
+ value_cities << value
157
+ next
158
+
159
+ # issue warning: unknown type for value
160
+ # logger.warn "unknown type for value >#{value}<"
161
+ end
162
+ end # each value
163
+
164
+ if value_numbers.size > 0
165
+ new_attributes[ :area ] = value_numbers[0]
166
+ new_attributes[ :pop ] = value_numbers[1]
167
+ end
168
+
169
+ =begin
170
+ # auto-add tags
171
+ area = value_numbers[0]
172
+ pop = value_numbers[1]
173
+
174
+ # categorize into brackets
175
+ if area >= 1_000_000
176
+ value_tag_keys << 'area_1_000_000_n_up'
177
+ elsif area >= 100_000
178
+ value_tag_keys << 'area_100_000_to_1_000_000'
179
+ elsif area >= 1000
180
+ value_tag_keys << 'area_1_000_to_100_000'
181
+ else
182
+ value_tag_keys << 'area_1_000_n_less' # microstate
183
+ end
184
+
185
+ # include all
186
+ value_tag_keys << 'area_100_000_n_up' if area >= 100_000
187
+ value_tag_keys << 'area_1_000_n_up' if area >= 1_000
188
+
189
+
190
+ # categorize into brackets
191
+ if pop >= 100_000_000
192
+ value_tag_keys << 'pop_100m_n_up'
193
+ elsif pop >= 10_000_000
194
+ value_tag_keys << 'pop_10m_to_100m'
195
+ elsif pop >= 1_000_000
196
+ value_tag_keys << 'pop_1m_to_10m'
197
+ else
198
+ value_tag_keys << 'pop_1m_n_less'
199
+ end
200
+
201
+ # include all
202
+ value_tag_keys << 'pop_10m_n_up' if pop >= 10_000_000
203
+ value_tag_keys << 'pop_1m_n_up' if pop >= 1_000_000
204
+ =end
205
+
206
+ rec = Country.find_by_key( new_attributes[ :key ] )
207
+
208
+ if rec.present?
209
+ logger.debug "update Country #{rec.id}-#{rec.key}:"
210
+ else
211
+ logger.debug "create Country:"
212
+ rec = Country.new
213
+ end
214
+
215
+ logger.debug new_attributes.to_json
216
+
217
+ rec.update_attributes!( new_attributes )
218
+
219
+ #################
220
+ ## auto add capital cities
221
+
222
+ City.create_or_update_from_titles( value_cities, country_id: rec.id )
223
+
224
+ ##################
225
+ ## add taggings
226
+
227
+ if value_tag_keys.size > 0
228
+
229
+ if opts[:skip_tags].present?
230
+ logger.debug " skipping add taggings (flag skip_tag)"
231
+ else
232
+ value_tag_keys.uniq! # remove duplicates
233
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
234
+
235
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
236
+
237
+ value_tag_keys.each do |key|
238
+ tag = Tag.find_by_key( key )
239
+ if tag.nil? # create tag if it doesn't exit
240
+ logger.debug " creating tag >#{key}<"
241
+ tag = Tag.create!( key: key )
242
+ end
243
+ rec.tags << tag
244
+ end
245
+ end
246
+ end
247
+
248
+ rec
249
+
250
+ end # method create_or_update_from_values
251
+
252
+
98
253
  end # class Country
99
254
 
100
255
 
@@ -1,7 +1,16 @@
1
+ # encoding: UTF-8
2
+
1
3
  module WorldDb::Models
2
4
 
3
5
  class Region < ActiveRecord::Base
4
6
 
7
+ extend WorldDb::TagHelper # will add self.find_tags, self.find_tags_in_hash!, etc.
8
+
9
+ # NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
10
+ # self.create_or_update_from_values
11
+ extend TextUtils::ValueHelper # e.g. is_year?, is_region?, is_address?, is_taglist? etc.
12
+
13
+
5
14
  belongs_to :country, :class_name => 'Country', :foreign_key => 'country_id'
6
15
 
7
16
  has_many :cities, :class_name => 'City', :foreign_key => 'region_id'
@@ -9,7 +18,7 @@ class Region < ActiveRecord::Base
9
18
  has_many :taggings, :as => :taggable
10
19
  has_many :tags, :through => :taggings
11
20
 
12
- validates :key, :format => { :with => /^[a-z]{2,}$/, :message => 'expected two or more lowercase letters a-z' }
21
+ validates :key, :format => { :with => /^[a-z]+$/, :message => 'expected one or more lowercase letters a-z' }
13
22
  validates :code, :format => { :with => /^[A-Z_]{2,3}$/, :message => 'expected two or three uppercase letters A-Z (and _)' }, :allow_nil => true
14
23
 
15
24
 
@@ -48,6 +57,109 @@ class Region < ActiveRecord::Base
48
57
  end # each region
49
58
  end
50
59
 
60
+
61
+ def self.create_or_update_from_values( new_attributes, values, opts={} )
62
+
63
+ ## opts e.g. :skip_tags true|false
64
+
65
+ ## fix: add/configure logger for ActiveRecord!!!
66
+ logger = LogKernel::Logger.root
67
+
68
+ value_numbers = []
69
+ value_tag_keys = []
70
+ value_cities = []
71
+
72
+ ### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
73
+ value_tag_keys += find_tags_in_hash!( new_attributes )
74
+
75
+ ## check for optional values
76
+ values.each_with_index do |value,index|
77
+ if value =~ /^country:/ ## country:
78
+ value_country_key = value[8..-1] ## cut off country: prefix
79
+ value_country = Country.find_by_key!( value_country_key )
80
+ new_attributes[ :country_id ] = value_country.id
81
+ elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
82
+ new_attributes[ :code ] = value
83
+ elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
84
+ ## allow numbers like 453 km²
85
+ value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
86
+ elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
87
+ value_numbers << value.gsub(/[ _]/, '').to_i
88
+ elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
89
+ logger.debug " found tags: >>#{value}<<"
90
+ value_tag_keys += find_tags( value )
91
+ else
92
+
93
+ ### assume it is the capital city - mark it for auto add
94
+ value_cities << value
95
+ next
96
+
97
+ # issue warning: unknown type for value
98
+ # logger.warn "unknown type for value >#{value}<"
99
+ end
100
+ end # each value
101
+
102
+ if value_numbers.size > 0
103
+ new_attributes[ :area ] = value_numbers[0]
104
+ new_attributes[ :pop ] = value_numbers[1]
105
+ end # if value_numbers.size > 0
106
+
107
+ ## todo: assert that country_id is present/valid, that is, NOT null
108
+ rec = Region.find_by_key_and_country_id( new_attributes[ :key ], new_attributes[ :country_id] )
109
+
110
+ if rec.present?
111
+ logger.debug "update Region #{rec.id}-#{rec.key}:"
112
+ else
113
+ logger.debug "create Region:"
114
+ rec = Region.new
115
+ end
116
+
117
+ logger.debug new_attributes.to_json
118
+
119
+ rec.update_attributes!( new_attributes )
120
+
121
+ #################
122
+ # auto add capital cities
123
+
124
+ City.create_or_update_from_titles( value_cities,
125
+ region_id: rec.id,
126
+ country_id: rec.country_id )
127
+
128
+ ### todo/fix: add captial ref to country/region
129
+
130
+
131
+ ##################
132
+ # add taggings
133
+
134
+ ## todo/fix: reuse - move add taggings into method etc.
135
+
136
+ if value_tag_keys.size > 0
137
+
138
+ if opts[:skip_tags].present?
139
+ logger.debug " skipping add taggings (flag skip_tag)"
140
+ else
141
+ value_tag_keys.uniq! # remove duplicates
142
+ logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
143
+
144
+ ### fix/todo: check tag_ids and only update diff (add/remove ids)
145
+
146
+ value_tag_keys.each do |key|
147
+ tag = Tag.find_by_key( key )
148
+ if tag.nil? # create tag if it doesn't exit
149
+ logger.debug " creating tag >#{key}<"
150
+ tag = Tag.create!( key: key )
151
+ end
152
+ rec.tags << tag
153
+ end
154
+ end
155
+ end
156
+
157
+ rec
158
+
159
+ end # method create_or_update_from_values
160
+
161
+
162
+
51
163
  end # class Region
52
164
 
53
165
  end # module WorldDb::Models
@@ -2,6 +2,40 @@
2
2
 
3
3
  module WorldDb
4
4
 
5
+
6
+ ### fix: move to textutils
7
+ ## PlusReaderWrapper find a better name than Plus?
8
+ #
9
+ # todo: also add a ValuesReaderPlus and use it
10
+
11
+ class HashReaderPlus
12
+ include LogUtils::Logging
13
+
14
+ def initialize( name, include_path )
15
+ @name = name
16
+ @include_path = include_path
17
+ end
18
+
19
+ attr_reader :name
20
+ attr_reader :include_path
21
+
22
+ def each
23
+ path = "#{include_path}/#{name}.yml"
24
+ reader = HashReader.new( path )
25
+
26
+ logger.info "parsing data '#{name}' (#{path})..."
27
+
28
+ reader.each do |key, value|
29
+ yield( key, value )
30
+ end
31
+ Prop.create_from_fixture!( name, path )
32
+ end
33
+
34
+ end # class HashReaderPlus
35
+
36
+
37
+
38
+
5
39
  class Reader
6
40
 
7
41
  include LogUtils::Logging
@@ -114,22 +148,16 @@ class Reader
114
148
 
115
149
 
116
150
  def load_regions_xxx( country_key, xxx, name )
117
- path = "#{include_path}/#{name}.yml"
118
-
119
- logger.info "parsing data '#{name}' (#{path})..."
120
-
121
151
  country = Country.find_by_key!( country_key )
122
152
  logger.debug "Country #{country.key} >#{country.title} (#{country.code})<"
123
153
 
124
- reader = HashReader.new( path )
154
+ reader = HashReaderPlus.new( name, include_path )
125
155
 
126
156
  reader.each do |key, value|
127
157
  region = Region.find_by_country_id_and_key!( country.id, key )
128
158
  region.send( "#{xxx}=", value )
129
159
  region.save!
130
160
  end
131
-
132
- Prop.create_from_fixture!( name, path )
133
161
  end
134
162
 
135
163
 
@@ -141,27 +169,8 @@ class Reader
141
169
  end
142
170
 
143
171
 
144
-
145
- def with_path_for( name )
146
- ## todo: find a better name?
147
- # e.g. find_path_for or open_fixture_for ??
148
-
149
- path = "#{include_path}/#{name}.yml"
150
-
151
- logger.info "parsing data '#{name}' (#{path})..."
152
-
153
- yield( path )
154
-
155
- Prop.create_from_fixture!( name, path )
156
- end
157
-
158
-
159
172
  def load_continent_refs( name )
160
- path = "#{include_path}/#{name}.yml"
161
-
162
- logger.info "parsing data '#{name}' (#{path})..."
163
-
164
- reader = HashReader.new( path )
173
+ reader = HashReaderPlus.new( name, include_path )
165
174
 
166
175
  reader.each do |key, value|
167
176
  country = Country.find_by_key!( key )
@@ -169,10 +178,9 @@ class Reader
169
178
  country.continent_id = continent.id
170
179
  country.save!
171
180
  end
172
-
173
- Prop.create_from_fixture!( name, path )
174
181
  end
175
182
 
183
+
176
184
  def load_continent_defs( name, more_values={} )
177
185
  path = "#{include_path}/#{name}.txt"
178
186
 
@@ -208,11 +216,9 @@ class Reader
208
216
 
209
217
  def load_langs( name )
210
218
 
211
- with_path_for( name ) do |path|
212
-
213
- reader = HashReader.new( path )
219
+ reader = HashReaderPlus.new( name, include_path )
214
220
 
215
- reader.each do |key, value|
221
+ reader.each do |key, value|
216
222
 
217
223
  logger.debug "adding lang >>#{key}<< >>#{value}<<..."
218
224
 
@@ -236,17 +242,14 @@ class Reader
236
242
  logger.debug lang_attribs.to_json
237
243
 
238
244
  lang.update_attributes!( lang_attribs )
239
- end # each key,value
240
- end # with_path_for
245
+ end # each key,value
241
246
 
242
247
  end # method load_langs
243
248
 
244
249
 
245
250
  def load_tags( name, more_values={} )
246
251
 
247
- with_path_for( name ) do |path|
248
-
249
- reader = HashReader.new( path )
252
+ reader = HashReaderPlus.new( name, include_path )
250
253
 
251
254
  grade = 1
252
255
 
@@ -291,19 +294,13 @@ class Reader
291
294
 
292
295
  tag.update_attributes!( tag_attribs )
293
296
  end
294
- end # each key,value
295
-
296
- end # with_path_for
297
-
297
+ end # each key,value
298
+
298
299
  end # method load_tags
299
300
 
300
301
 
301
302
  def load_usages( name )
302
- path = "#{include_path}/#{name}.yml"
303
-
304
- logger.info "parsing data '#{name}' (#{path})..."
305
-
306
- reader = HashReader.new( path )
303
+ reader = HashReaderPlus.new( name, include_path )
307
304
 
308
305
  reader.each do |key, value|
309
306
  logger.debug " adding langs >>#{value}<<to country >>#{key}<<"
@@ -322,28 +319,19 @@ class Reader
322
319
  Usage.create!( country_id: country.id, lang_id: lang.id, official: true, minor: false )
323
320
  end
324
321
  end
325
-
326
- Prop.create_from_fixture!( name, path )
327
322
  end
328
323
 
329
324
 
330
325
  def load_xxx( xxx, name )
331
- path = "#{include_path}/#{name}.yml"
332
-
333
- logger.info "parsing data '#{name}' (#{path})..."
334
-
335
- reader = HashReader.new( path )
326
+ reader = HashReaderPlus.new( name, include_path )
336
327
 
337
328
  reader.each do |key, value|
338
329
  country = Country.find_by_key!( key )
339
330
  country.send( "#{xxx}=", value )
340
331
  country.save!
341
332
  end
342
-
343
- Prop.create_from_fixture!( name, path )
344
333
  end
345
334
 
346
-
347
335
  private
348
336
  def load_fixtures_for( clazz, name, more_values={} ) # load from file system
349
337
  path = "#{include_path}/#{name}.txt"
@@ -352,267 +340,13 @@ private
352
340
 
353
341
  reader = ValuesReader.new( path, more_values )
354
342
 
355
- load_fixtures_worker_for( clazz, reader )
343
+ reader.each_line do |new_attributes, values|
344
+ opts = { skip_tags: skip_tags? }
345
+ clazz.create_or_update_from_values( new_attributes, values, opts )
346
+ end
356
347
 
357
348
  Prop.create_from_fixture!( name, path )
358
349
  end
359
-
360
-
361
- def load_fixtures_worker_for( clazz, reader )
362
-
363
- ## NB: assumes active activerecord db connection
364
- ##
365
-
366
- reader.each_line do |attribs, values|
367
-
368
- value_numbers = []
369
- value_tag_keys = []
370
- value_cities = []
371
-
372
- ### check for "default" tags - that is, if present attribs[:tags] remove from hash
373
-
374
- if attribs[:tags].present?
375
- more_tag_keys = attribs[:tags].split('|')
376
- attribs.delete(:tags)
377
-
378
- ## unify; replace _w/ space; remove leading n trailing whitespace
379
- more_tag_keys = more_tag_keys.map do |key|
380
- key = key.gsub( '_', ' ' )
381
- key = key.strip
382
- key
383
- end
384
-
385
- value_tag_keys += more_tag_keys
386
- end
387
-
388
-
389
- if clazz == City
390
- attribs[ :c ] = true # assume city type by default (use metro,district to change in fixture)
391
- elsif clazz == Country
392
- attribs[ :c ] = true # assume country type by default (use supra,depend to change)
393
- end
394
-
395
- ## check for optional values
396
- values.each_with_index do |value,index|
397
- if value =~ /^region:/ ## region:
398
- value_region_key = value[7..-1] ## cut off region: prefix
399
- ## NB: requires country_id to make unique!
400
- value_region = Region.find_by_key_and_country_id!( value_region_key, attribs[:country_id] )
401
- attribs[ :region_id ] = value_region.id
402
- elsif value =~ /^metro$/ ## metro(politan area)
403
- attribs[ :c ] = false # turn off default c|city flag; make it m|metro only
404
- attribs[ :m ] = true
405
- elsif value =~ /^supra$/ ## supra(national)
406
- attribs[ :c ] = false # turn off default c|country flag; make it s|supra only
407
- attribs[ :s ] = true
408
- ## auto-add tag supra
409
- value_tag_keys << 'supra'
410
- elsif value =~ /^supra:/ ## supra:
411
- value_country_key = value[6..-1] ## cut off supra: prefix
412
- value_country = Country.find_by_key!( value_country_key )
413
- attribs[ :country_id ] = value_country.id
414
- elsif value =~ /^country:/ ## country:
415
- value_country_key = value[8..-1] ## cut off country: prefix
416
- value_country = Country.find_by_key!( value_country_key )
417
- attribs[ :country_id ] = value_country.id
418
- attribs[ :c ] = false # turn off default c|country flag; make it d|depend only
419
- attribs[ :d ] = true
420
- ## auto-add tag supra
421
- value_tag_keys << 'territory' # rename tag to dependency? why? why not?
422
- elsif value =~ /^metro:/ ## metro:
423
- value_city_key = value[6..-1] ## cut off metro: prefix
424
- value_city = City.find_by_key!( value_city_key )
425
- attribs[ :city_id ] = value_city.id
426
- elsif value =~ /^city:/ ## city:
427
- value_city_key = value[5..-1] ## cut off city: prefix
428
- value_city = City.find_by_key!( value_city_key )
429
- attribs[ :city_id ] = value_city.id
430
- attribs[ :c ] = false # turn off default c|city flag; make it d|district only
431
- attribs[ :d ] = true
432
- elsif value =~ /^m:/ ## m:
433
- value_popm_str = value[2..-1] ## cut off m: prefix
434
- value_popm = value_popm_str.gsub(/[ _]/, '').to_i
435
- attribs[ :popm ] = value_popm
436
- attribs[ :m ] = true # auto-mark city as m|metro too
437
- elsif is_region?( value ) && clazz == City ## assume region code e.g. TX for city
438
- value_region = Region.find_by_key_and_country_id!( value.downcase, attribs[:country_id] )
439
- attribs[ :region_id ] = value_region.id
440
- elsif value =~ /^[A-Z]{2,3}$/ ## assume two or three-letter code
441
- attribs[ :code ] = value
442
- elsif value =~ /^([0-9][0-9 _]+[0-9]|[0-9]{1,2})(?:\s*(?:km2|km²)\s*)$/
443
- ## allow numbers like 453 km²
444
- value_numbers << value.gsub( 'km2', '').gsub( 'km²', '' ).gsub(/[ _]/, '').to_i
445
- elsif value =~ /^([0-9][0-9 _]+[0-9])|([0-9]{1,2})$/ ## numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
446
- value_numbers << value.gsub(/[ _]/, '').to_i
447
- elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
448
-
449
- logger.debug " found tags: >>#{value}<<"
450
-
451
- tag_keys = value.split('|')
452
-
453
- ## unify; replace _w/ space; remove leading n trailing whitespace
454
- tag_keys = tag_keys.map do |key|
455
- key = key.gsub( '_', ' ' )
456
- key = key.strip
457
- key
458
- end
459
-
460
- value_tag_keys += tag_keys
461
- else
462
-
463
- if clazz == Country || clazz == Region
464
- ### assume it is the capital city - mark it for auto add
465
- value_cities << value
466
- next
467
- end
468
-
469
- # issue warning: unknown type for value
470
- logger.warn "unknown type for value >#{value}<"
471
- end
472
- end # each value
473
-
474
-
475
- if value_numbers.size > 0
476
- if clazz == City
477
- attribs[ :pop ] = value_numbers[0] # assume first number is pop for cities
478
- attribs[ :area ] = value_numbers[1]
479
- else # countries,regions
480
- attribs[ :area ] = value_numbers[0]
481
- attribs[ :pop ] = value_numbers[1]
482
-
483
-
484
- =begin
485
- if clazz == Country
486
- # auto-add tags
487
- area = value_numbers[0]
488
- pop = value_numbers[1]
489
-
490
- # categorize into brackets
491
- if area >= 1_000_000
492
- value_tag_keys << 'area_1_000_000_n_up'
493
- elsif area >= 100_000
494
- value_tag_keys << 'area_100_000_to_1_000_000'
495
- elsif area >= 1000
496
- value_tag_keys << 'area_1_000_to_100_000'
497
- else
498
- value_tag_keys << 'area_1_000_n_less' # microstate
499
- end
500
-
501
- # include all
502
- value_tag_keys << 'area_100_000_n_up' if area >= 100_000
503
- value_tag_keys << 'area_1_000_n_up' if area >= 1_000
504
-
505
-
506
- # categorize into brackets
507
- if pop >= 100_000_000
508
- value_tag_keys << 'pop_100m_n_up'
509
- elsif pop >= 10_000_000
510
- value_tag_keys << 'pop_10m_to_100m'
511
- elsif pop >= 1_000_000
512
- value_tag_keys << 'pop_1m_to_10m'
513
- else
514
- value_tag_keys << 'pop_1m_n_less'
515
- end
516
-
517
- # include all
518
- value_tag_keys << 'pop_10m_n_up' if pop >= 10_000_000
519
- value_tag_keys << 'pop_1m_n_up' if pop >= 1_000_000
520
- end
521
- =end
522
-
523
-
524
- end
525
- end # if value_numbers.size > 0
526
-
527
- rec = nil
528
-
529
- if clazz == Region ## requires country_id
530
- ## todo: assert that country_id is present/valid, that is, NOT null
531
- rec = clazz.find_by_key_and_country_id( attribs[ :key ], attribs[ :country_id] )
532
- else
533
- rec = clazz.find_by_key( attribs[ :key ] )
534
- end
535
-
536
- if rec.present?
537
- ## nb: [17..-1] cut off WorldDB::Models:: in name
538
- logger.debug "update #{clazz.name[17..-1].downcase} #{rec.id}-#{rec.key}:"
539
- else
540
- logger.debug "create #{clazz.name[17..-1].downcase}:"
541
- rec = clazz.new
542
- end
543
-
544
- logger.debug attribs.to_json
545
-
546
- rec.update_attributes!( attribs )
547
-
548
- #################
549
- ## auto add capital cities
550
-
551
- value_cities.each do |city_title|
552
-
553
- city_attribs = {}
554
- city_key = TextUtils.title_to_key( city_title )
555
-
556
- ## check if it exists
557
- ## todo/fix: add country_id for lookup?
558
- city = City.find_by_key( city_key )
559
- if city.present?
560
- logger.debug "update city #{city.id}-#{city.key}:"
561
- else
562
- logger.debug "create city:"
563
- city = City.new
564
- city_attribs[ :key ] = city_key
565
- end
566
-
567
- city_attribs[ :title ] = city_title
568
-
569
- if clazz == Country
570
- city_attribs[ :country_id ] = rec.id
571
- elsif clazz == Region
572
- city_attribs[ :region_id ] = rec.id
573
- city_attribs[ :country_id ] = rec.country_id
574
- else
575
- ## issue warning: unknown type for city!!!
576
- end
577
-
578
- logger.debug city_attribs.to_json
579
-
580
- city.update_attributes!( city_attribs )
581
-
582
- ### todo/fix: add captial ref to country/region
583
-
584
- end # each city
585
-
586
-
587
- ##################
588
- ## add taggings
589
-
590
- if value_tag_keys.size > 0
591
-
592
- if skip_tags?
593
- logger.debug " skipping add taggings (flag skip_tag)"
594
- else
595
- value_tag_keys.uniq! # remove duplicates
596
- logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
597
-
598
- ### fix/todo: check tag_ids and only update diff (add/remove ids)
599
-
600
- value_tag_keys.each do |key|
601
- tag = Tag.find_by_key( key )
602
- if tag.nil? # create tag if it doesn't exit
603
- logger.debug " creating tag >#{key}<"
604
- tag = Tag.create!( key: key )
605
- end
606
- rec.tags << tag
607
- end
608
- end
609
- end
610
-
611
-
612
- end # each_line
613
-
614
- end # method load_fixture_worker_for
615
-
616
350
 
617
351
  end # class Reader
618
352
  end # module WorldDb
data/lib/worlddb/utils.rb CHANGED
@@ -16,3 +16,39 @@ class Time
16
16
  end
17
17
 
18
18
  end # class Time
19
+
20
+
21
+ ##### fix/todo: move to helper folder - use one file per module/helper
22
+
23
+ module WorldDb
24
+ module TagHelper
25
+
26
+ def find_tags( value )
27
+ # logger.debug " found tags: >>#{value}<<"
28
+
29
+ tag_keys = value.split('|')
30
+
31
+ ## unify; replace _w/ space; remove leading n trailing whitespace
32
+ tag_keys = tag_keys.map do |key|
33
+ key = key.gsub( '_', ' ' )
34
+ key = key.strip
35
+ key
36
+ end
37
+
38
+ tag_keys # return tag keys as ary
39
+ end
40
+
41
+ def find_tags_in_hash!( h )
42
+ # NB: will remove :tags from hash
43
+
44
+ if h[:tags].present?
45
+ tag_keys = find_tags( h[:tags] )
46
+ h.delete(:tags)
47
+ tag_keys # return tag keys as ary
48
+ else
49
+ [] # nothing found; return empty ary
50
+ end
51
+ end
52
+
53
+ end # module
54
+ end # module WorldDb
@@ -1,6 +1,6 @@
1
1
 
2
2
  module WorldDb
3
- VERSION = '1.6.4' # sync version w/ sport.db - why? why not?
3
+ VERSION = '1.6.5' # sync version w/ sport.db - why? why not?
4
4
  end
5
5
 
6
6
  ###########################################
data/lib/worlddb.rb CHANGED
@@ -26,6 +26,7 @@ require 'textutils'
26
26
 
27
27
  require 'worlddb/version'
28
28
 
29
+ require 'worlddb/utils'
29
30
  require 'worlddb/models/prop'
30
31
  require 'worlddb/models/continent'
31
32
  require 'worlddb/models/country'
@@ -36,7 +37,6 @@ require 'worlddb/models/tagging'
36
37
  require 'worlddb/models/lang'
37
38
  require 'worlddb/models/usage'
38
39
  require 'worlddb/schema' # NB: requires worlddb/models (include WorldDB::Models)
39
- require 'worlddb/utils'
40
40
  require 'worlddb/reader'
41
41
  require 'worlddb/deleter'
42
42
  require 'worlddb/stats'
data/test/helper.rb ADDED
@@ -0,0 +1,56 @@
1
+
2
+ ## $:.unshift(File.dirname(__FILE__))
3
+
4
+ ## minitest setup
5
+
6
+ # require 'minitest/unit'
7
+ require 'minitest/autorun'
8
+
9
+ # include MiniTest::Unit # lets us use TestCase instead of MiniTest::Unit::TestCase
10
+
11
+
12
+ # ruby stdlibs
13
+
14
+ require 'json'
15
+ require 'uri'
16
+ require 'pp'
17
+
18
+ # ruby gems
19
+
20
+ require 'active_record'
21
+
22
+ # our own code
23
+
24
+ require 'worlddb'
25
+ require 'logutils/db' # NB: explict require required for LogDb (not automatic)
26
+
27
+ Country = WorldDb::Models::Country
28
+ Region = WorldDb::Models::Region
29
+ City = WorldDb::Models::City
30
+
31
+
32
+ def setup_in_memory_db
33
+ # Database Setup & Config
34
+
35
+ db_config = {
36
+ adapter: 'sqlite3',
37
+ database: ':memory:'
38
+ }
39
+
40
+ pp db_config
41
+
42
+ ActiveRecord::Base.logger = Logger.new( STDOUT )
43
+ ## ActiveRecord::Base.colorize_logging = false - no longer exists - check new api/config setting?
44
+
45
+ ## NB: every connect will create a new empty in memory db
46
+ ActiveRecord::Base.establish_connection( db_config )
47
+
48
+
49
+ ## build schema
50
+
51
+ LogDb.create
52
+ WorldDb.create
53
+ end
54
+
55
+
56
+ setup_in_memory_db()
@@ -0,0 +1,114 @@
1
+ # encoding: utf-8
2
+
3
+ ###
4
+ # to run use
5
+ # ruby -I ./lib -I ./test test/test_helper.rb
6
+ # or better
7
+ # rake test
8
+
9
+ require 'helper'
10
+
11
+ class TestValues < MiniTest::Unit::TestCase
12
+
13
+ def setup
14
+ # delete all countries, regions, cities in in-memory only db
15
+ WorldDb.delete!
16
+ end
17
+
18
+ def test_load_country_values
19
+
20
+ new_attributes = {
21
+ key: 'at',
22
+ title: 'Austria',
23
+ synonyms: ''
24
+ }
25
+
26
+ values = [
27
+ 'AUT',
28
+ '83_871',
29
+ '8_414_638',
30
+ 'un|fifa|uefa|eu|euro|schengen|central_europe|western_europe'
31
+ ]
32
+
33
+ c = Country.create_or_update_from_values( new_attributes, values )
34
+
35
+ c2 = Country.find_by_key!( new_attributes[:key] )
36
+ assert( c.id == c2.id )
37
+
38
+ assert( c.title == new_attributes[:title] )
39
+ assert( c.pop == 8_414_638 )
40
+ assert( c.area == 83_871 )
41
+ ## todo: assert tag count; add supra:eu etc.
42
+ end
43
+
44
+ def test_load_region_values
45
+
46
+ at = Country.create!( key: 'at',
47
+ title: 'Austria',
48
+ code: 'AUT',
49
+ pop: 8_414_638,
50
+ area: 83_871 )
51
+
52
+ new_attributes = {
53
+ key: 'w',
54
+ title: 'Wien',
55
+ synonyms: '',
56
+ country_id: at.id
57
+ }
58
+
59
+ values = [
60
+ '415 km²',
61
+ 'eastern austria'
62
+ ]
63
+
64
+ r = Region.create_or_update_from_values( new_attributes, values )
65
+
66
+ r2 = Region.find_by_key!( new_attributes[:key] )
67
+ assert( r.id == r2.id )
68
+
69
+ assert( r.title == new_attributes[:title] )
70
+ assert( r.area == 415 )
71
+ ## todo: assert country_id & country.title for assoc
72
+ end
73
+
74
+ def test_load_city_values
75
+
76
+ at = Country.create!( key: 'at',
77
+ title: 'Austria',
78
+ code: 'AUT',
79
+ pop: 8_414_638,
80
+ area: 83_871 )
81
+
82
+ w = Region.create!( key: 'w',
83
+ title: 'Wien',
84
+ country_id: at.id )
85
+
86
+ new_attributes = {
87
+ key: 'wien',
88
+ title: 'Wien',
89
+ synonyms: '',
90
+ country_id: at.id
91
+ }
92
+
93
+ values = [
94
+ 'W',
95
+ '1_731_236',
96
+ 'm:1_724_000'
97
+ ]
98
+
99
+ c = City.create_or_update_from_values( new_attributes, values )
100
+
101
+ c2 = City.find_by_key!( new_attributes[:key] )
102
+ assert( c.id == c2.id )
103
+
104
+ assert( c.title == new_attributes[:title] )
105
+ assert( c.pop == 1_731_236 )
106
+ assert( c.popm == 1_724_000 )
107
+ assert( c.m == true )
108
+ assert( c.region_id == w.id )
109
+ assert( c.country_id == at.id )
110
+ end
111
+
112
+
113
+ end # class TestValues
114
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: worlddb
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.4
4
+ version: 1.6.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-09 00:00:00.000000000 Z
12
+ date: 2013-05-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: textutils
16
- requirement: &83827050 !ruby/object:Gem::Requirement
16
+ requirement: &83860400 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0.5'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *83827050
24
+ version_requirements: *83860400
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: commander
27
- requirement: &83826830 !ruby/object:Gem::Requirement
27
+ requirement: &83860180 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 4.1.3
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *83826830
35
+ version_requirements: *83860180
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: activerecord
38
- requirement: &83826620 !ruby/object:Gem::Requirement
38
+ requirement: &83876350 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '3.2'
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *83826620
46
+ version_requirements: *83876350
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rdoc
49
- requirement: &83826400 !ruby/object:Gem::Requirement
49
+ requirement: &83876130 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '3.10'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *83826400
57
+ version_requirements: *83876130
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: hoe
60
- requirement: &83826180 !ruby/object:Gem::Requirement
60
+ requirement: &83875910 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: '3.3'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *83826180
68
+ version_requirements: *83875910
69
69
  description: worlddb - world.db command line tool
70
70
  email: opensport@googlegroups.com
71
71
  executables:
@@ -99,6 +99,9 @@ files:
99
99
  - lib/worlddb/stats.rb
100
100
  - lib/worlddb/utils.rb
101
101
  - lib/worlddb/version.rb
102
+ - test/helper.rb
103
+ - test/test_values.rb
104
+ - .gemtest
102
105
  homepage: https://github.com/geraldb/world.db.ruby
103
106
  licenses:
104
107
  - Public Domain
@@ -126,4 +129,5 @@ rubygems_version: 1.8.17
126
129
  signing_key:
127
130
  specification_version: 3
128
131
  summary: worlddb - world.db command line tool
129
- test_files: []
132
+ test_files:
133
+ - test/test_values.rb