worlddb-models 2.2.2 → 2.3.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.
- checksums.yaml +4 -4
- data/Manifest.txt +31 -13
- data/README.md +7 -7
- data/Rakefile +1 -1
- data/lib/worlddb/deleter.rb +6 -1
- data/lib/worlddb/helpers/value_helper.rb +117 -0
- data/lib/worlddb/matcher.rb +99 -135
- data/lib/worlddb/matcher_adm.rb +82 -0
- data/lib/worlddb/models/city.rb +30 -208
- data/lib/worlddb/models/city_base.rb +220 -0
- data/lib/worlddb/models/continent.rb +9 -0
- data/lib/worlddb/models/country.rb +21 -4
- data/lib/worlddb/models/forward.rb +25 -9
- data/lib/worlddb/models/lang.rb +6 -0
- data/lib/worlddb/models/place.rb +1 -1
- data/lib/worlddb/models/state.rb +83 -0
- data/lib/worlddb/models/{region.rb → state_base.rb} +52 -36
- data/lib/worlddb/models/tagdb/tag.rb +1 -1
- data/lib/worlddb/models.rb +11 -8
- data/lib/worlddb/patterns.rb +4 -4
- data/lib/worlddb/reader.rb +68 -39
- data/lib/worlddb/reader_file.rb +36 -3
- data/lib/worlddb/reader_zip.rb +33 -3
- data/lib/worlddb/readers/base.rb +149 -0
- data/lib/worlddb/readers/city.rb +2 -65
- data/lib/worlddb/readers/country.rb +2 -63
- data/lib/worlddb/readers/lang.rb +3 -68
- data/lib/worlddb/readers/state.rb +61 -0
- data/lib/worlddb/readers/state_tree.rb +118 -0
- data/lib/worlddb/readers/usage.rb +2 -65
- data/lib/worlddb/schema.rb +142 -43
- data/lib/worlddb/stats.rb +7 -4
- data/lib/worlddb/tree_reader.rb +97 -0
- data/lib/worlddb/version.rb +2 -2
- data/test/adm/test_fixture_matcher_adm2.rb +73 -0
- data/test/{test_fixture_matcher_adm3.rb → adm/test_fixture_matcher_adm3.rb} +6 -6
- data/test/adm/test_fixture_matcher_tree.rb +52 -0
- data/test/{test_read_adm.rb → adm/test_read_adm.rb} +13 -20
- data/test/adm/test_read_tree.rb +63 -0
- data/test/data/at-austria/2--n-niederoesterreich/counties.txt +6 -4
- data/test/data/at-austria/orte.txt +23 -0
- data/test/data/at-austria/setups/tree.txt +9 -0
- data/test/data/de-deutschland/3--by-bayern/4--oberfranken/counties.txt +14 -13
- data/test/data/de-deutschland/3--by-bayern/4--oberfranken/orte.txt +104 -0
- data/test/data/de-deutschland/3--by-bayern/4--oberfranken/orte_ii.txt +17 -0
- data/test/data/de-deutschland/3--by-bayern/{districts.txt → parts.txt} +1 -1
- data/test/data/de-deutschland/orte.txt +12 -0
- data/test/data/de-deutschland/setups/adm.txt +1 -1
- data/test/data/de-deutschland/setups/tree.txt +9 -0
- data/test/helper.rb +8 -1
- data/test/test_fixture_matchers.rb +9 -10
- data/test/test_fixture_matchers_ii.rb +20 -19
- data/test/test_model_city.rb +26 -9
- data/test/{test_model_comp.rb → test_model_compat.rb} +15 -13
- data/test/test_model_country.rb +1 -1
- data/test/test_model_state.rb +54 -0
- data/test/test_model_states_at.rb +111 -0
- data/test/test_model_states_de.rb +147 -0
- data/test/test_models.rb +10 -3
- data/test/test_parse_city.rb +70 -0
- data/test/test_parse_country.rb +56 -0
- data/test/test_parse_state.rb +46 -0
- data/test/test_state_tree_reader_at.rb +54 -0
- data/test/test_state_tree_reader_de.rb +71 -0
- data/test/test_tree_reader.rb +39 -0
- metadata +50 -22
- data/lib/worlddb/models/city_compat.rb +0 -27
- data/lib/worlddb/models/continent_compat.rb +0 -24
- data/lib/worlddb/models/country_compat.rb +0 -35
- data/lib/worlddb/models/lang_compat.rb +0 -23
- data/lib/worlddb/models/region_compat.rb +0 -26
- data/lib/worlddb/readers/region.rb +0 -79
- data/test/test_fixture_matcher_adm2.rb +0 -62
- data/test/test_model_region.rb +0 -50
data/lib/worlddb/models/city.rb
CHANGED
@@ -3,238 +3,60 @@
|
|
3
3
|
module WorldDb
|
4
4
|
module Model
|
5
5
|
|
6
|
-
###
|
7
|
-
## Todo:
|
8
|
-
## use four classes instead of one ?
|
9
|
-
# e.g. Use class class Metro n class City n class District n class CityBase ?? - why? why not?
|
10
|
-
#
|
11
|
-
# find a better name for CityBase ??
|
12
|
-
# Locality ??
|
13
|
-
# or CityCore or CityStd or CityAll or CityGeneric
|
14
|
-
# or CityLike or CityTable or CityTbl or ???
|
15
|
-
|
16
|
-
class City < ActiveRecord::Base
|
17
|
-
|
18
|
-
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
19
|
-
|
20
|
-
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
21
|
-
# self.create_or_update_from_values
|
22
|
-
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_region?, self.is_address?, self.is_taglist? etc.
|
23
6
|
|
7
|
+
class City < CityBase
|
24
8
|
|
25
9
|
self.table_name = 'cities'
|
26
10
|
|
27
|
-
belongs_to :
|
28
|
-
belongs_to :
|
29
|
-
belongs_to :
|
30
|
-
|
31
|
-
## self referencing hierachy within cities e.g. m|metro > c|city > d|district
|
32
|
-
|
33
|
-
## fix: use condition check for m|d|c flag?? why? why not? (NB: flags are NOT exclusive e.g. possible metro|city)
|
34
|
-
|
35
|
-
## (1) metro - level up
|
36
|
-
has_many :cities, class_name: 'City', foreign_key: 'city_id'
|
37
|
-
|
38
|
-
## (2) city
|
39
|
-
belongs_to :metro, class_name: 'City', foreign_key: 'city_id' ## for now alias for parent - use parent?
|
40
|
-
has_many :districts, class_name: 'City', foreign_key: 'city_id' ## for now alias for cities - use cities?
|
41
|
-
|
42
|
-
## (3) district - level down
|
43
|
-
belongs_to :city, class_name: 'City', foreign_key: 'city_id' ## for now alias for parent - use parent?
|
44
|
-
|
45
|
-
has_many_tags
|
46
|
-
|
47
|
-
###
|
48
|
-
# NB: use is_ for flags to avoid conflict w/ assocs (e.g. metro?, city? etc.)
|
49
|
-
|
50
|
-
def is_metro?() m? == true; end
|
51
|
-
def is_city?() c? == true; end
|
52
|
-
def is_district?() d? == true; end
|
11
|
+
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
|
12
|
+
belongs_to :state, class_name: 'State', foreign_key: 'state_id'
|
13
|
+
belongs_to :muni, class_name: 'Muni', foreign_key: 'muni_id'
|
53
14
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def on_create
|
58
|
-
place_rec = Place.create!( name: name, kind: place_kind )
|
59
|
-
self.place_id = place_rec.id
|
60
|
-
end
|
61
|
-
|
62
|
-
def on_update
|
63
|
-
## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
|
64
|
-
place.update_attributes!( name: name, kind: place_kind )
|
65
|
-
end
|
15
|
+
belongs_to :metro, class_name: 'Metro', foreign_key: 'metro_id'
|
16
|
+
has_many :districts, class_name: 'District', foreign_key: 'city_id'
|
66
17
|
|
67
18
|
def place_kind # use place_kind_of_code ??
|
68
|
-
|
69
|
-
#//////////////////////////////////
|
70
|
-
#// fix: add nested record syntax e.g. city w/ metro population
|
71
|
-
#// use (metro: 4444) e.g. must start with (<nested_type>: props) !!! or similar
|
72
|
-
#//
|
73
|
-
if is_metro?
|
74
|
-
'MTRO'
|
75
|
-
elsif is_district?
|
76
|
-
'DIST'
|
77
|
-
else
|
78
|
-
'CITY'
|
79
|
-
end
|
19
|
+
'CITY'
|
80
20
|
end
|
81
21
|
|
22
|
+
def self.create_or_update_from_titles( titles, more_attribs = {} )
|
23
|
+
# ary of titles e.g. ['Wien', 'Graz'] etc.
|
82
24
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
scope :by_name, ->{ order( 'name asc' ) } # order by title (a-z)
|
89
|
-
scope :by_pop, ->{ order( 'pop desc' ) } # order by pop(ulation)
|
90
|
-
scope :by_popm, ->{ order( 'popm desc' ) } # order by pop(ulation) metropolitan area
|
91
|
-
scope :by_area, ->{ order( 'area desc' ) } # order by area (in square km)
|
25
|
+
titles.each do |title|
|
26
|
+
values = [title]
|
27
|
+
City.create_or_update_from_values( values, more_attribs )
|
28
|
+
end # each city
|
29
|
+
end # method create_or_update_from_titles
|
92
30
|
|
93
31
|
|
94
|
-
|
95
|
-
### fix:
|
96
|
-
## allow to passing in sep or separator e.g. | or other
|
32
|
+
end # class City
|
97
33
|
|
98
|
-
return name if alt_names.blank?
|
99
|
-
|
100
|
-
buf = ''
|
101
|
-
buf << name
|
102
|
-
buf << ' | '
|
103
|
-
buf << alt_names.split('|').join(' | ')
|
104
|
-
buf
|
105
|
-
end
|
106
34
|
|
35
|
+
class Metro < CityBase
|
107
36
|
|
108
|
-
|
109
|
-
|
37
|
+
belongs_to :country, class_name: 'Country', foreign_key: 'country_id'
|
38
|
+
## note: metro might be part of more than one (two,three) states (thus, cannot belong_to state)
|
39
|
+
## collect (all) states from cities??
|
110
40
|
|
111
|
-
|
112
|
-
attribs = attribs.merge( more_attribs )
|
41
|
+
has_many :cities, class_name: 'City', foreign_key: 'metro_id'
|
113
42
|
|
114
|
-
|
115
|
-
|
43
|
+
def place_kind # use place_kind_of_code ??
|
44
|
+
'MTRO'
|
116
45
|
end
|
117
46
|
|
47
|
+
end # class Metro
|
118
48
|
|
119
|
-
def self.create_or_update_from_titles( titles, more_attribs = {} )
|
120
|
-
# ary of titles e.g. ['Wien', 'Graz'] etc.
|
121
49
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
end # each city
|
126
|
-
end # method create_or_update_from_titles
|
50
|
+
class District < CityBase
|
51
|
+
|
52
|
+
belongs_to :city, class_name: 'City', foreign_key: 'city_id'
|
127
53
|
|
54
|
+
def place_kind # use place_kind_of_code ??
|
55
|
+
'DIST'
|
56
|
+
end
|
128
57
|
|
58
|
+
end # class District
|
129
59
|
|
130
|
-
def self.create_or_update_from_attribs( new_attributes, values, opts={} )
|
131
|
-
# attribs -> key/value pairs e.g. hash
|
132
|
-
# values -> ary of string values/strings (key not yet known; might be starting of value e.g. city:wien)
|
133
|
-
|
134
|
-
## opts e.g. :skip_tags true|false
|
135
|
-
|
136
|
-
## fix: add/configure logger for ActiveRecord!!!
|
137
|
-
logger = LogKernel::Logger.root
|
138
|
-
|
139
|
-
value_numbers = []
|
140
|
-
value_tag_keys = []
|
141
|
-
|
142
|
-
### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
|
143
|
-
value_tag_keys += find_tags_in_attribs!( new_attributes )
|
144
|
-
|
145
|
-
new_attributes[ :c ] = true # assume city type by default (use metro,district to change in fixture)
|
146
|
-
|
147
|
-
## check for optional values
|
148
|
-
|
149
|
-
values.each_with_index do |value,index|
|
150
|
-
if match_region_for_country( value, new_attributes[:country_id] ) do |region|
|
151
|
-
new_attributes[ :region_id ] = region.id
|
152
|
-
end
|
153
|
-
elsif match_country( value ) do |country|
|
154
|
-
new_attributes[ :country_id ] = country.id
|
155
|
-
end
|
156
|
-
elsif match_metro( value ) do |city|
|
157
|
-
new_attributes[ :city_id ] = city.id
|
158
|
-
end
|
159
|
-
elsif match_metro_pop( value ) do |num| # m:
|
160
|
-
new_attributes[ :popm ] = num
|
161
|
-
new_attributes[ :m ] = true # auto-mark city as m|metro too
|
162
|
-
end
|
163
|
-
elsif match_metro_flag( value ) do |_| # metro(politan area)
|
164
|
-
new_attributes[ :c ] = false # turn off default c|city flag; make it m|metro only
|
165
|
-
new_attributes[ :m ] = true
|
166
|
-
end
|
167
|
-
elsif match_city( value ) do |city| # parent city for district
|
168
|
-
new_attributes[ :city_id ] = city.id
|
169
|
-
new_attributes[ :c ] = false # turn off default c|city flag; make it d|district only
|
170
|
-
new_attributes[ :d ] = true
|
171
|
-
end
|
172
|
-
elsif match_km_squared( value ) do |num| # allow numbers like 453 km²
|
173
|
-
value_numbers << num
|
174
|
-
end
|
175
|
-
elsif match_number( value ) do |num| # numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
|
176
|
-
value_numbers << num
|
177
|
-
end
|
178
|
-
elsif value =~ /#{CITY_CODE_PATTERN}/ ## assume three-letter code
|
179
|
-
new_attributes[ :code ] = value
|
180
|
-
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
181
|
-
logger.debug " found tags: >>#{value}<<"
|
182
|
-
value_tag_keys += find_tags( value )
|
183
|
-
else
|
184
|
-
# issue warning: unknown type for value
|
185
|
-
logger.warn "unknown type for value >#{value}<"
|
186
|
-
end
|
187
|
-
end # each value
|
188
|
-
|
189
|
-
if value_numbers.size > 0
|
190
|
-
new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
|
191
|
-
new_attributes[ :area ] = value_numbers[1]
|
192
|
-
end
|
193
|
-
|
194
|
-
rec = City.find_by_key( new_attributes[ :key ] )
|
195
|
-
|
196
|
-
if rec.present?
|
197
|
-
logger.debug "update City #{rec.id}-#{rec.key}:"
|
198
|
-
else
|
199
|
-
logger.debug "create City:"
|
200
|
-
rec = City.new
|
201
|
-
end
|
202
|
-
|
203
|
-
logger.debug new_attributes.to_json
|
204
|
-
|
205
|
-
rec.update_attributes!( new_attributes )
|
206
|
-
|
207
|
-
##################
|
208
|
-
## add taggings
|
209
|
-
|
210
|
-
## todo/fix: reuse - move add taggings into method etc.
|
211
|
-
|
212
|
-
if value_tag_keys.size > 0
|
213
|
-
|
214
|
-
if opts[:skip_tags].present?
|
215
|
-
logger.debug " skipping add taggings (flag skip_tag)"
|
216
|
-
else
|
217
|
-
value_tag_keys.uniq! # remove duplicates
|
218
|
-
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
219
|
-
|
220
|
-
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
221
|
-
|
222
|
-
value_tag_keys.each do |key|
|
223
|
-
tag = Tag.find_by_key( key )
|
224
|
-
if tag.nil? # create tag if it doesn't exit
|
225
|
-
logger.debug " creating tag >#{key}<"
|
226
|
-
tag = Tag.create!( key: key )
|
227
|
-
end
|
228
|
-
rec.tags << tag
|
229
|
-
end
|
230
|
-
end
|
231
|
-
end
|
232
|
-
|
233
|
-
rec
|
234
|
-
end # method create_or_update_from_values
|
235
|
-
|
236
|
-
|
237
|
-
end # class Cities
|
238
60
|
|
239
61
|
end # module Model
|
240
62
|
end # module WorldDb
|
@@ -0,0 +1,220 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module WorldDb
|
4
|
+
module Model
|
5
|
+
|
6
|
+
|
7
|
+
class CityBase < ActiveRecord::Base
|
8
|
+
|
9
|
+
self.abstract_class = true
|
10
|
+
|
11
|
+
extend TextUtils::TagHelper # will add self.find_tags, self.find_tags_in_attribs!, etc.
|
12
|
+
|
13
|
+
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
14
|
+
# self.create_or_update_from_values
|
15
|
+
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_address?, self.is_taglist? etc.
|
16
|
+
|
17
|
+
belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
|
18
|
+
|
19
|
+
has_many_tags
|
20
|
+
|
21
|
+
## begin compat
|
22
|
+
def title() name; end
|
23
|
+
def title=(value) self.name = value; end
|
24
|
+
|
25
|
+
def synonyms() alt_names; end
|
26
|
+
def synonyms=(value) self.alt_names = value; end
|
27
|
+
## end
|
28
|
+
|
29
|
+
before_create :on_create
|
30
|
+
before_update :on_update
|
31
|
+
|
32
|
+
def on_create
|
33
|
+
place_rec = Place.create!( name: name, kind: place_kind )
|
34
|
+
self.place_id = place_rec.id
|
35
|
+
end
|
36
|
+
|
37
|
+
def on_update
|
38
|
+
## fix/todo: check - if name or kind changed - only update if changed ?? why? why not??
|
39
|
+
place.update_attributes!( name: name, kind: place_kind )
|
40
|
+
end
|
41
|
+
|
42
|
+
validates :key, format: { with: /#{CITY_KEY_PATTERN}/, message: CITY_KEY_PATTERN_MESSAGE }
|
43
|
+
validates :code, format: { with: /#{CITY_CODE_PATTERN}/, message: CITY_CODE_PATTERN_MESSAGE }, allow_nil: true
|
44
|
+
|
45
|
+
|
46
|
+
scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
|
47
|
+
scope :by_name, ->{ order( 'name asc' ) } # order by title (a-z)
|
48
|
+
scope :by_pop, ->{ order( 'pop desc' ) } # order by pop(ulation)
|
49
|
+
scope :by_area, ->{ order( 'area desc' ) } # order by area (in square km)
|
50
|
+
|
51
|
+
|
52
|
+
def all_names( opts={} )
|
53
|
+
### fix:
|
54
|
+
## allow to passing in sep or separator e.g. | or other
|
55
|
+
|
56
|
+
return name if alt_names.blank?
|
57
|
+
|
58
|
+
buf = ''
|
59
|
+
buf << name
|
60
|
+
buf << ' | '
|
61
|
+
buf << alt_names.split('|').join(' | ')
|
62
|
+
buf
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
def self.parse( *args )
|
68
|
+
## remove (extract) attribs hash (if last arg is a hash n present)
|
69
|
+
more_attribs = args.last.is_a?(Hash) ? args.pop : {} ## extract_options!
|
70
|
+
values = args
|
71
|
+
|
72
|
+
self.create_or_update_from_values( values, more_attribs )
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
def self.create_or_update_from_values( values, more_attribs={} )
|
77
|
+
## key & title & country required
|
78
|
+
|
79
|
+
attribs, more_values = find_key_n_title( values )
|
80
|
+
attribs = attribs.merge( more_attribs )
|
81
|
+
|
82
|
+
## check for optional values
|
83
|
+
self.create_or_update_from_attribs( attribs, more_values )
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.create_or_update_from_attribs( new_attributes, values, opts={} )
|
87
|
+
# attribs -> key/value pairs e.g. hash
|
88
|
+
# values -> ary of string values/strings (key not yet known; might be starting of value e.g. city:wien)
|
89
|
+
|
90
|
+
## opts e.g. :skip_tags true|false
|
91
|
+
|
92
|
+
## fix: add/configure logger for ActiveRecord!!!
|
93
|
+
logger = LogKernel::Logger.root
|
94
|
+
|
95
|
+
value_numbers = []
|
96
|
+
value_tag_keys = []
|
97
|
+
|
98
|
+
### check for "default" tags - that is, if present new_attributes[:tags] remove from hash
|
99
|
+
value_tag_keys += find_tags_in_attribs!( new_attributes )
|
100
|
+
|
101
|
+
city_class = City # assume city type by default (use metro,district to change in fixture)
|
102
|
+
new_attributes_metro = {}
|
103
|
+
|
104
|
+
## check for optional values
|
105
|
+
|
106
|
+
values.each_with_index do |value,index|
|
107
|
+
if match_state_for_country( value, new_attributes[:country_id] ) do |state|
|
108
|
+
new_attributes[ :state_id ] = state.id
|
109
|
+
end
|
110
|
+
elsif match_country( value ) do |country|
|
111
|
+
new_attributes[ :country_id ] = country.id
|
112
|
+
end
|
113
|
+
elsif match_metro( value ) do |metro|
|
114
|
+
new_attributes[ :metro_id ] = metro.id
|
115
|
+
end
|
116
|
+
elsif match_metro_pop( value ) do |num| # e.g m: 1 444 444
|
117
|
+
new_attributes_metro[ :pop ] = num # note: gets added to **metro** attributes; triggers auto-added metro record
|
118
|
+
end
|
119
|
+
elsif match_metro_flag( value ) do |_| # metro(politan area)
|
120
|
+
city_class = Metro # turn off default c|city flag; make it m|metro only
|
121
|
+
end
|
122
|
+
elsif match_city( value ) do |city| # parent city for district
|
123
|
+
new_attributes[ :city_id ] = city.id
|
124
|
+
city_class = District # turn off default c|city flag; make it d|district only
|
125
|
+
end
|
126
|
+
elsif match_km_squared( value ) do |num| # allow numbers like 453 km²
|
127
|
+
value_numbers << num
|
128
|
+
end
|
129
|
+
elsif match_number( value ) do |num| # numeric (nb: can use any _ or spaces inside digits e.g. 1_000_000 or 1 000 000)
|
130
|
+
value_numbers << num
|
131
|
+
end
|
132
|
+
elsif value =~ /#{CITY_CODE_PATTERN}/ ## assume three-letter code
|
133
|
+
new_attributes[ :code ] = value
|
134
|
+
elsif (values.size==(index+1)) && is_taglist?( value ) # tags must be last entry
|
135
|
+
logger.debug " found tags: >>#{value}<<"
|
136
|
+
value_tag_keys += find_tags( value )
|
137
|
+
else
|
138
|
+
# issue warning: unknown type for value
|
139
|
+
logger.warn "unknown type for value >#{value}<"
|
140
|
+
end
|
141
|
+
end # each value
|
142
|
+
|
143
|
+
if value_numbers.size > 0
|
144
|
+
new_attributes[ :pop ] = value_numbers[0] # assume first number is pop for cities
|
145
|
+
new_attributes[ :area ] = value_numbers[1]
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
#######
|
150
|
+
## auto add metro if used M:/m: shortcut
|
151
|
+
if new_attributes_metro.empty? == false
|
152
|
+
new_attributes_metro[:key] = new_attributes[:key]
|
153
|
+
## todo/fix: use title or name?
|
154
|
+
new_attributes_metro[:name] = new_attributes[:name] || new_attributes[:title]
|
155
|
+
new_attributes_metro[:country_id] = new_attributes[:country_id]
|
156
|
+
|
157
|
+
metro_rec = Metro.find_by_key( new_attributes_metro[ :key ] )
|
158
|
+
|
159
|
+
if metro_rec.present?
|
160
|
+
logger.debug "(auto-)update Metro #{metro_rec.id}-#{metro_rec.key}:"
|
161
|
+
else
|
162
|
+
logger.debug "(auto-)create Metro:"
|
163
|
+
metro_rec = Metro.new
|
164
|
+
end
|
165
|
+
|
166
|
+
logger.debug new_attributes_metro.to_json
|
167
|
+
|
168
|
+
metro_rec.update_attributes!( new_attributes_metro )
|
169
|
+
new_attributes[:metro_id] = metro_rec.id
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
rec = city_class.find_by_key( new_attributes[ :key ] )
|
174
|
+
|
175
|
+
if rec.present?
|
176
|
+
logger.debug "update #{city_class.name} #{rec.id}-#{rec.key}:"
|
177
|
+
else
|
178
|
+
logger.debug "create #{city_class.name}:"
|
179
|
+
rec = city_class.new
|
180
|
+
end
|
181
|
+
|
182
|
+
logger.debug new_attributes.to_json
|
183
|
+
|
184
|
+
rec.update_attributes!( new_attributes )
|
185
|
+
|
186
|
+
|
187
|
+
##################
|
188
|
+
## add taggings
|
189
|
+
|
190
|
+
## todo/fix: reuse - move add taggings into method etc.
|
191
|
+
|
192
|
+
if value_tag_keys.size > 0
|
193
|
+
|
194
|
+
if opts[:skip_tags].present?
|
195
|
+
logger.debug " skipping add taggings (flag skip_tag)"
|
196
|
+
else
|
197
|
+
value_tag_keys.uniq! # remove duplicates
|
198
|
+
logger.debug " adding #{value_tag_keys.size} taggings: >>#{value_tag_keys.join('|')}<<..."
|
199
|
+
|
200
|
+
### fix/todo: check tag_ids and only update diff (add/remove ids)
|
201
|
+
|
202
|
+
value_tag_keys.each do |key|
|
203
|
+
tag = Tag.find_by_key( key )
|
204
|
+
if tag.nil? # create tag if it doesn't exit
|
205
|
+
logger.debug " creating tag >#{key}<"
|
206
|
+
tag = Tag.create!( key: key )
|
207
|
+
end
|
208
|
+
rec.tags << tag
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
rec
|
214
|
+
end # method create_or_update_from_values
|
215
|
+
|
216
|
+
|
217
|
+
end # class Cities
|
218
|
+
|
219
|
+
end # module Model
|
220
|
+
end # module WorldDb
|
@@ -8,6 +8,15 @@ class Continent < ActiveRecord::Base
|
|
8
8
|
belongs_to :place, class_name: 'Place', foreign_key: 'place_id'
|
9
9
|
has_many :countries
|
10
10
|
|
11
|
+
## begin compat
|
12
|
+
def title() name; end
|
13
|
+
def title=(value) self.name = value; end
|
14
|
+
|
15
|
+
def synonyms() alt_names; end
|
16
|
+
def synonyms=(value) self.alt_names = value; end
|
17
|
+
## end
|
18
|
+
|
19
|
+
|
11
20
|
# NB: allow dots in keys e.g. concacaf.naf etc.
|
12
21
|
|
13
22
|
before_create :on_create
|
@@ -12,7 +12,7 @@ class Country < ActiveRecord::Base
|
|
12
12
|
|
13
13
|
# NB: use extend - is_<type>? become class methods e.g. self.is_<type>? for use in
|
14
14
|
# self.create_or_update_from_values
|
15
|
-
extend TextUtils::ValueHelper # e.g. self.is_year?, self.
|
15
|
+
extend TextUtils::ValueHelper # e.g. self.is_year?, self.is_address?, is_taglist? etc.
|
16
16
|
|
17
17
|
|
18
18
|
self.table_name = 'countries'
|
@@ -23,14 +23,14 @@ class Country < ActiveRecord::Base
|
|
23
23
|
has_many :usages
|
24
24
|
has_many :langs, :through => :usages # lang(uage)s through usages (that is, countries_langs) join table
|
25
25
|
|
26
|
-
has_many :
|
26
|
+
has_many :states, class_name: 'State', foreign_key: 'country_id'
|
27
27
|
has_many :cities, class_name: 'City', foreign_key: 'country_id'
|
28
|
-
|
28
|
+
|
29
29
|
## self referencing hierachy within countries e.g. EU > GB > EN
|
30
30
|
belongs_to :parent, class_name: 'Country', foreign_key: 'country_id'
|
31
31
|
has_many :countries, class_name: 'Country', foreign_key: 'country_id'
|
32
32
|
### recursive self-reference - use node??
|
33
|
-
## has_many :nodes, class_name: '
|
33
|
+
## has_many :nodes, class_name: 'State', foregin_key: 'state_id'
|
34
34
|
|
35
35
|
|
36
36
|
has_many_tags
|
@@ -38,6 +38,14 @@ class Country < ActiveRecord::Base
|
|
38
38
|
validates :key, format: { with: /#{COUNTRY_KEY_PATTERN}/, message: COUNTRY_KEY_PATTERN_MESSAGE }
|
39
39
|
validates :code, format: { with: /#{COUNTRY_CODE_PATTERN}/, message: COUNTRY_CODE_PATTERN_MESSAGE }
|
40
40
|
|
41
|
+
## begin compat
|
42
|
+
def title() name; end
|
43
|
+
def title=(value) self.name = value; end
|
44
|
+
|
45
|
+
def synonyms() alt_names; end
|
46
|
+
def synonyms=(value) self.alt_names = value; end
|
47
|
+
## end
|
48
|
+
|
41
49
|
|
42
50
|
scope :by_key, ->{ order( 'key asc' ) } # order by key (a-z)
|
43
51
|
scope :by_name, ->{ order( 'name asc' ) } # order by name (a-z)
|
@@ -167,6 +175,15 @@ class Country < ActiveRecord::Base
|
|
167
175
|
end
|
168
176
|
|
169
177
|
|
178
|
+
|
179
|
+
def self.parse( *args )
|
180
|
+
## remove (extract) attribs hash (if last arg is a hash n present)
|
181
|
+
more_attribs = args.last.is_a?(Hash) ? args.pop : {} ## extract_options!
|
182
|
+
values = args
|
183
|
+
|
184
|
+
self.create_or_update_from_values( values, more_attribs )
|
185
|
+
end
|
186
|
+
|
170
187
|
def self.create_or_update_from_values( values, more_attribs={} )
|
171
188
|
|
172
189
|
## key & title
|
@@ -15,15 +15,24 @@ Tagging = TagDb::Model::Tagging
|
|
15
15
|
Tag = TagDb::Model::Tag
|
16
16
|
|
17
17
|
|
18
|
-
class Name
|
19
|
-
class Place
|
20
|
-
class Continent
|
21
|
-
class Country
|
22
|
-
class Region < ActiveRecord::Base ; end
|
23
|
-
class City < ActiveRecord::Base ; end
|
18
|
+
class Name < ActiveRecord::Base ; end
|
19
|
+
class Place < ActiveRecord::Base ; end
|
20
|
+
class Continent < ActiveRecord::Base ; end
|
21
|
+
class Country < ActiveRecord::Base ; end
|
24
22
|
|
25
|
-
class
|
26
|
-
class
|
23
|
+
class CityBase < ActiveRecord::Base ; end
|
24
|
+
class Metro < CityBase ; end
|
25
|
+
class City < CityBase ; end
|
26
|
+
class District < CityBase ; end
|
27
|
+
|
28
|
+
class StateBase < ActiveRecord::Base ; end
|
29
|
+
class State < StateBase ; end ## ADM1
|
30
|
+
class Part < StateBase ; end ## x /ADM2
|
31
|
+
class County < StateBase ; end ## ADM2/ADM3
|
32
|
+
class Muni < StateBase ; end ## ADM3/ADM4
|
33
|
+
|
34
|
+
class Lang < ActiveRecord::Base ; end
|
35
|
+
class Usage < ActiveRecord::Base ; end
|
27
36
|
|
28
37
|
class CountryCode < ActiveRecord::Base ; end
|
29
38
|
|
@@ -45,8 +54,15 @@ Name = WorldDb::Model::Name
|
|
45
54
|
Place = WorldDb::Model::Place
|
46
55
|
Continent = WorldDb::Model::Continent
|
47
56
|
Country = WorldDb::Model::Country
|
48
|
-
|
57
|
+
|
58
|
+
State = WorldDb::Model::State ## ADM1
|
59
|
+
Part = WorldDb::Model::Part ## x /ADM2
|
60
|
+
County = WorldDb::Model::County ## ADM2/ADM3
|
61
|
+
Muni = WorldDb::Model::Muni ## ADM3/ADM4
|
62
|
+
|
63
|
+
Metro = WorldDb::Model::Metro
|
49
64
|
City = WorldDb::Model::City
|
65
|
+
District = WorldDb::Model::District
|
50
66
|
|
51
67
|
Lang = WorldDb::Model::Lang
|
52
68
|
Usage = WorldDb::Model::Usage
|
data/lib/worlddb/models/lang.rb
CHANGED
@@ -11,6 +11,12 @@ class Lang < ActiveRecord::Base
|
|
11
11
|
|
12
12
|
validates :key, format: { with: /#{LANG_KEY_PATTERN}/, message: LANG_KEY_PATTERN_MESSAGE }
|
13
13
|
|
14
|
+
## begin compat
|
15
|
+
def title() name; end
|
16
|
+
def title=(value) self.name = value; end
|
17
|
+
## end
|
18
|
+
|
19
|
+
|
14
20
|
end # class Lang
|
15
21
|
|
16
22
|
end # module Model
|