fias 0.0.2 → 1.0.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 +7 -0
- data/.gitignore +15 -22
- data/.rubocop.yml +7 -0
- data/.travis.yml +10 -0
- data/Gemfile +1 -1
- data/LICENSE.txt +2 -2
- data/README.md +259 -155
- data/Rakefile +6 -1
- data/config/names.txt +0 -0
- data/config/synonyms.yml +50 -0
- data/examples/create.rb +106 -0
- data/examples/generate_index.rb +63 -0
- data/fias.gemspec +33 -21
- data/lib/fias.rb +197 -10
- data/lib/fias/config.rb +74 -0
- data/lib/fias/import/copy.rb +62 -0
- data/lib/fias/import/dbf.rb +81 -0
- data/lib/fias/import/download_service.rb +37 -0
- data/lib/fias/import/restore_parent_id.rb +51 -0
- data/lib/fias/import/tables.rb +74 -0
- data/lib/fias/name/append.rb +30 -0
- data/lib/fias/name/canonical.rb +42 -0
- data/lib/fias/name/extract.rb +85 -0
- data/lib/fias/name/house_number.rb +71 -0
- data/lib/fias/name/split.rb +60 -0
- data/lib/fias/name/synonyms.rb +93 -0
- data/lib/fias/query.rb +43 -0
- data/lib/fias/query/estimate.rb +67 -0
- data/lib/fias/query/finder.rb +75 -0
- data/lib/fias/query/params.rb +101 -0
- data/lib/fias/railtie.rb +3 -17
- data/lib/fias/version.rb +1 -1
- data/spec/fixtures/ACTSTAT.DBF +0 -0
- data/spec/fixtures/NORDOC99.DBF +0 -0
- data/spec/fixtures/STRSTAT.DBF +0 -0
- data/spec/fixtures/addressing.yml +93 -0
- data/spec/fixtures/query.yml +79 -0
- data/spec/fixtures/query_sanitization.yml +75 -0
- data/spec/fixtures/status_append.yml +60 -0
- data/spec/lib/import/copy_spec.rb +44 -0
- data/spec/lib/import/dbf_spec.rb +28 -0
- data/spec/lib/import/download_service_spec.rb +15 -0
- data/spec/lib/import/restore_parent_id_spec.rb +34 -0
- data/spec/lib/import/tables_spec.rb +26 -0
- data/spec/lib/name/append_spec.rb +14 -0
- data/spec/lib/name/canonical_spec.rb +20 -0
- data/spec/lib/name/extract_spec.rb +67 -0
- data/spec/lib/name/house_number_spec.rb +45 -0
- data/spec/lib/name/query_spec.rb +21 -0
- data/spec/lib/name/split_spec.rb +15 -0
- data/spec/lib/name/synonyms_spec.rb +51 -0
- data/spec/lib/query/params_spec.rb +15 -0
- data/spec/lib/query_spec.rb +27 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/db.rb +30 -0
- data/spec/support/query.rb +13 -0
- data/tasks/db.rake +52 -0
- data/tasks/download.rake +15 -0
- metadata +246 -64
- data/lib/fias/active_record/address_object.rb +0 -231
- data/lib/fias/active_record/address_object_type.rb +0 -15
- data/lib/fias/dbf_wrapper.rb +0 -90
- data/lib/fias/importer.rb +0 -30
- data/lib/fias/importer/base.rb +0 -59
- data/lib/fias/importer/pg.rb +0 -81
- data/lib/fias/importer/sqlite.rb +0 -38
- data/lib/generators/fias/migration.rb +0 -34
- data/lib/generators/fias/templates/create_fias_tables.rb +0 -5
- data/tasks/fias.rake +0 -68
@@ -1,231 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
# TODO: Поделить на файлы
|
3
|
-
module Fias
|
4
|
-
class AddressObject < ActiveRecord::Base
|
5
|
-
# TODO: Тут надо понять как префикс передать
|
6
|
-
if defined?(Rails)
|
7
|
-
self.table_name = "#{Rails.application.config.fias.prefix}_address_objects"
|
8
|
-
else
|
9
|
-
self.table_name = "fias_address_objects"
|
10
|
-
end
|
11
|
-
|
12
|
-
self.primary_key = 'aoid'
|
13
|
-
|
14
|
-
alias_attribute :name, :formalname
|
15
|
-
alias_attribute :id, :aoid
|
16
|
-
|
17
|
-
# Родительские объекты (Ленобласть для Лодейнопольского района)
|
18
|
-
# Для "проезд 1-й Конной Лахты 2-й" - Санкт-Петербург и Ленинград.
|
19
|
-
# Блядь, 1-й проезд 2-й Конной Лахты, ебануться!
|
20
|
-
# http://maps.yandex.ru/?text=%D0%A0%D0%BE%D1%81%D1%81%D0%B8%D1%8F%2C%20%D0%A1%D0%B0%D0%BD%D0%BA%D1%82-%D0%9F%D0%B5%D1%82%D0%B5%D1%80%D0%B1%D1%83%D1%80%D0%B3%2C%202-%D0%B9%20%D0%BF%D1%80%D0%BE%D0%B5%D0%B7%D0%B4%201-%D0%B9%20%D0%9A%D0%BE%D0%BD%D0%BD%D0%BE%D0%B9%20%D0%9B%D0%B0%D1%85%D1%82%D1%8B&sll=30.123296%2C60.007056&ll=30.123848%2C60.007475&spn=0.010085%2C0.006017&z=17&l=map
|
21
|
-
has_many :parents,
|
22
|
-
class_name: 'AddressObject',
|
23
|
-
foreign_key: 'aoguid',
|
24
|
-
primary_key: 'parentguid'
|
25
|
-
|
26
|
-
# Дочерние объекты (например, улицы для города)
|
27
|
-
has_many :children,
|
28
|
-
class_name: 'AddressObject',
|
29
|
-
primary_key: 'aoguid',
|
30
|
-
foreign_key: 'parentguid'
|
31
|
-
|
32
|
-
# Предыдущие исторические версии названия (Ленинград для Питера)
|
33
|
-
# Может быть несколько, если произошло слияние
|
34
|
-
has_many :previous_versions,
|
35
|
-
class_name: 'AddressObject',
|
36
|
-
foreign_key: 'aoid',
|
37
|
-
primary_key: 'previd'
|
38
|
-
|
39
|
-
# Следующая исторические версии названия (Питер для Ленинграда)
|
40
|
-
# Может быть несколько, если произошло разделение
|
41
|
-
has_many :next_versions,
|
42
|
-
class_name: 'AddressObject',
|
43
|
-
foreign_key: 'aoid',
|
44
|
-
primary_key: 'nextid'
|
45
|
-
|
46
|
-
# Полное наименование типа объекта (город, улица)
|
47
|
-
belongs_to :address_object_type,
|
48
|
-
class_name: '::Fias::AddressObjectType',
|
49
|
-
foreign_key: 'shortname',
|
50
|
-
primary_key: 'scname'
|
51
|
-
|
52
|
-
# Актуальные записи (активные в настоящий момент)
|
53
|
-
# Проверено, что livestatus уже достаточен для идентификации
|
54
|
-
# актуальных объектов, вопреки показаниям вики.
|
55
|
-
scope :actual, where(livestatus: 1)
|
56
|
-
|
57
|
-
# Выбирает объекты определенного уровня, аргументы - символы из хеша
|
58
|
-
# AOLEVELS
|
59
|
-
scope :leveled, ->(*levels) {
|
60
|
-
levels = Array.wrap(levels).map { |level| AOLEVELS[level] }
|
61
|
-
where(aolevel: levels)
|
62
|
-
}
|
63
|
-
|
64
|
-
scope :sorted, order('formalname ASC')
|
65
|
-
scope :with_types, includes(:address_object_type)
|
66
|
-
scope :matching, ->(name) {
|
67
|
-
scope = if self.connection.adapter_name
|
68
|
-
where(%{ "formalname" @@ ? OR ? @@ "formalname"}, name, name)
|
69
|
-
else
|
70
|
-
where('formalname LIKE ?', "%#{name}%")
|
71
|
-
end
|
72
|
-
|
73
|
-
# Первыми идут центральные и крупные поселения
|
74
|
-
scope.order('aolevel ASC, centstatus DESC')
|
75
|
-
}
|
76
|
-
|
77
|
-
scope :same_region, ->(address_object) {
|
78
|
-
where(regioncode: address_object.regioncode)
|
79
|
-
}
|
80
|
-
|
81
|
-
# Значимые поселения
|
82
|
-
scope :central, where('centstatus > 0')
|
83
|
-
|
84
|
-
def has_history?
|
85
|
-
previd.present?
|
86
|
-
end
|
87
|
-
|
88
|
-
def actual?
|
89
|
-
livestatus == 1
|
90
|
-
end
|
91
|
-
|
92
|
-
# Актуальный родитель. Для 1-го проезда 2-й Конной Лахты - только Питер.
|
93
|
-
def parent
|
94
|
-
parents.actual.first
|
95
|
-
end
|
96
|
-
|
97
|
-
def aolevel_sym
|
98
|
-
AOLEVELS.key(aolevel)
|
99
|
-
end
|
100
|
-
|
101
|
-
# Название с сокращением / полным наименованием города
|
102
|
-
# "г. Санкт-Петербург" / "город Санкт-Петербург"
|
103
|
-
#
|
104
|
-
# Для работы метода требуется загрузить таблицу :address_object_types
|
105
|
-
#
|
106
|
-
# Параметр - режим:
|
107
|
-
# :obviously - очевидный режим". В этом режиме "Дагестан" вместо
|
108
|
-
# "Республика Дагестан", "АО" вместо "Автономная область",
|
109
|
-
# но всегда "Краснодарский край"
|
110
|
-
# :short - дописываются "г." и "респ."
|
111
|
-
# :long - дописываются "город" и "Республика"
|
112
|
-
#
|
113
|
-
# Пример:
|
114
|
-
# .abbrevated(:obviously) # Тульская область, Хакасия, Еврейская Аобл.
|
115
|
-
# .abbrevated(:short) # Тульская область, Респ. Хакасия, Еврейская Аобл.
|
116
|
-
# .abbrevated(:long) # Тульская область, Республика Хакасия, Еврейская автономная область
|
117
|
-
def abbrevated(mode = :obviously)
|
118
|
-
return name if address_object_type.blank? || shortname.blank?
|
119
|
-
|
120
|
-
case aolevel_sym
|
121
|
-
when :region
|
122
|
-
# "Ханты-Мансийский Автономный округ - Югра"
|
123
|
-
return name if regioncode == '86'
|
124
|
-
|
125
|
-
ending = name[-2..-1]
|
126
|
-
|
127
|
-
# Если название кончается на -ая -ий - по правилам русского языка
|
128
|
-
# нужно дописать в конец "область", "край"
|
129
|
-
must_append = SHN_MUST_APPEND_TO_ENDINGS.include?(ending) ||
|
130
|
-
shortname == 'Чувашия'
|
131
|
-
|
132
|
-
must_abbrevate = must_append ||
|
133
|
-
shortname == 'Чувашия' ||
|
134
|
-
mode != :obviously
|
135
|
-
|
136
|
-
return name unless must_abbrevate
|
137
|
-
|
138
|
-
abbr = case mode
|
139
|
-
when :short
|
140
|
-
shortname
|
141
|
-
when :long
|
142
|
-
address_object_type.name
|
143
|
-
when :obviously
|
144
|
-
if SHN_LEAVE_SHORTS_INTACT.include?(shortname)
|
145
|
-
shortname
|
146
|
-
else
|
147
|
-
address_object_type.name
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Точка не ставится для АО, края, Чувашии и длинных названий
|
152
|
-
abbr = "#{abbr}." if mode == :short && not(shortname.in?(SHN_MUST_NOT_APPEND_DOT))
|
153
|
-
|
154
|
-
if not(must_append) && must_abbrevate
|
155
|
-
"#{abbr} #{name}"
|
156
|
-
else
|
157
|
-
# "Республика" => "республика", но "АО" остается
|
158
|
-
abbr = abbr.mb_chars.downcase if not(shortname.in?(SHN_LEAVE_SHORTS_INTACT))
|
159
|
-
"#{name} #{abbr}"
|
160
|
-
end
|
161
|
-
else
|
162
|
-
abbr = if mode == :long
|
163
|
-
address_object_type.try(:name)
|
164
|
-
else
|
165
|
-
shortname
|
166
|
-
end
|
167
|
-
"#{abbr} #{name}"
|
168
|
-
end
|
169
|
-
end
|
170
|
-
|
171
|
-
class << self
|
172
|
-
# Подробное описание см. в README
|
173
|
-
# TODO: OPERSTATUS
|
174
|
-
def match_existing(scope, fias_key_accessor, title_field_accessor, &block)
|
175
|
-
scope.find_each do |record|
|
176
|
-
aoid = record.send(fias_key_accessor)
|
177
|
-
title = record.send(title_field_accessor)
|
178
|
-
|
179
|
-
if aoid.present?
|
180
|
-
match = scoped.find_by_aoid(aoid)
|
181
|
-
|
182
|
-
if match.present?
|
183
|
-
unless match.actual?
|
184
|
-
next_versions = match.next_versions
|
185
|
-
|
186
|
-
if next_versions.empty?
|
187
|
-
yield(:deleted, record, match)
|
188
|
-
elsif next_versions.count == 1
|
189
|
-
next_version = next_versions.first
|
190
|
-
previous_versions_of_current = next_version.previous_versions
|
191
|
-
|
192
|
-
if previous_versions_of_current.count == 1
|
193
|
-
yield(:updated, record, next_version)
|
194
|
-
else
|
195
|
-
yield(:joined, record, next_version, previous_versions_of_current)
|
196
|
-
end
|
197
|
-
elsif next_versions.count > 1
|
198
|
-
yield(:split, record, next_versions)
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
202
|
-
else
|
203
|
-
matches = scoped.matching(title)
|
204
|
-
yield(:match, record, *matches)
|
205
|
-
end
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
def match_missing(scope, address_object_key_field, &block)
|
210
|
-
scoped.each do |address_object|
|
211
|
-
unless scope.where(address_object_key_field => address_object.aoid).exists?
|
212
|
-
yield(:created, nil, address_object)
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Коды уровня адресного объекта
|
219
|
-
AOLEVELS = {
|
220
|
-
region: 1, autonomy: 2, district: 3, city: 4,
|
221
|
-
territory: 5, settlement: 6, street: 7,
|
222
|
-
additional_territory: 90, additional_territory_slave: 91
|
223
|
-
}
|
224
|
-
|
225
|
-
# Дописывать сокращения обязательно, "Самарская" выглядит странно,
|
226
|
-
# всегда должно быть "Самарская область", а "Дагестан" понятно и так.
|
227
|
-
SHN_MUST_APPEND_TO_ENDINGS = %w(ая ий)
|
228
|
-
SHN_MUST_NOT_APPEND_DOT = %w(край АО Чувашия) # Не дописывать точку к сокращению
|
229
|
-
SHN_LEAVE_SHORTS_INTACT = %w(АО Аобл Чувашия) # В очевидном режиме не разворачивать "АО" в "Автономная область"
|
230
|
-
end
|
231
|
-
end
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module Fias
|
3
|
-
class AddressObjectType < ActiveRecord::Base
|
4
|
-
# TODO: Тут надо понять как префикс передать
|
5
|
-
if defined?(Rails)
|
6
|
-
self.table_name = "#{Rails.application.config.fias.prefix}_address_object_types"
|
7
|
-
else
|
8
|
-
self.table_name = "fias_address_object_types"
|
9
|
-
end
|
10
|
-
self.primary_key = 'scname'
|
11
|
-
|
12
|
-
alias_attribute :name, :socrname
|
13
|
-
alias_attribute :abbrevation, :scname
|
14
|
-
end
|
15
|
-
end
|
data/lib/fias/dbf_wrapper.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
module Fias
|
2
|
-
# Класс для доступа к DBF-файлам ФИАС.
|
3
|
-
#
|
4
|
-
# Пример:
|
5
|
-
# wrapper = Fias::DbfWrapper.new('tmp/fias')
|
6
|
-
# wrapper.address_objects.record_count
|
7
|
-
# wrapper.address_objects.each { |record| record.attributes }
|
8
|
-
#
|
9
|
-
# TODO: Добавить в инишилайзер tables, чтобы при создании проверять
|
10
|
-
# их наличие на диске
|
11
|
-
class DbfWrapper
|
12
|
-
# Открывает DBF-файлы ФИАСа
|
13
|
-
def initialize(pathspec)
|
14
|
-
unless Dir.exists?(pathspec)
|
15
|
-
raise ArgumentError, 'FIAS database path does not exists'
|
16
|
-
end
|
17
|
-
self.pathspec = pathspec
|
18
|
-
|
19
|
-
DBF_ACCESSORS.each do |accessor, dbf_name|
|
20
|
-
filename = File.join(pathspec, dbf_name)
|
21
|
-
|
22
|
-
if File.exists?(filename)
|
23
|
-
dbf = DBF::Table.new(filename, nil, DEFAULT_ENCODING)
|
24
|
-
send("#{accessor}=", dbf)
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Возвращает хеш {аксессор dbf-таблицы => таблица}
|
30
|
-
# На входе массив названий таблиц (строки), без параметров - все
|
31
|
-
# Макрос houses вернет все таблицы домов
|
32
|
-
#
|
33
|
-
# Пример:
|
34
|
-
# wrapper = Fias::DbfWrapper.new('tmp/fias')
|
35
|
-
# wrapper.tables(%w(address_objects address_object_types houses))
|
36
|
-
# ... {
|
37
|
-
# ... address_objects: Dbf::Table(...),
|
38
|
-
# ... address_object_types: Dbf::Table(...),
|
39
|
-
# ... house1: Dbf::Table(...),
|
40
|
-
# ... ...
|
41
|
-
# ... house99: Dbf::Table(...)
|
42
|
-
# ... }
|
43
|
-
def tables(*only)
|
44
|
-
only = only.first if only.first.is_a?(Array)
|
45
|
-
only = only.map(&:to_s)
|
46
|
-
|
47
|
-
hash = DBF_ACCESSORS.keys.map do |accessor|
|
48
|
-
accessor_s = accessor.to_s
|
49
|
-
is_houses = only.include?('houses') && accessor_s.starts_with?('house')
|
50
|
-
|
51
|
-
if only.include?(accessor_s) || is_houses
|
52
|
-
[accessor, send(accessor)]
|
53
|
-
end
|
54
|
-
end
|
55
|
-
Hash[*hash.compact.flatten]
|
56
|
-
end
|
57
|
-
|
58
|
-
# { house01: "HOUSE01"..house99: "HOUSE99" }
|
59
|
-
HOUSES_ACCESSORS = Hash[*(1..99).map { |n|
|
60
|
-
[("house%0.2d" % n).to_sym, "HOUSE%0.2d.dbf" % n]
|
61
|
-
}.flatten]
|
62
|
-
|
63
|
-
# Таблица соответствий аттрибутов класса DBF-файлам
|
64
|
-
DBF_ACCESSORS = {
|
65
|
-
address_object_types: 'SOCRBASE.DBF',
|
66
|
-
current_statuses: 'CURENTST.DBF',
|
67
|
-
actual_statuses: 'ACTSTAT.DBF',
|
68
|
-
operation_statuses: 'OPERSTAT.DBF',
|
69
|
-
center_statuses: 'CENTERST.DBF',
|
70
|
-
interval_statuses: 'INTVSTAT.DBF',
|
71
|
-
estate_statues: 'ESTSTAT.DBF',
|
72
|
-
structure_statuses: 'STRSTAT.DBF',
|
73
|
-
address_objects: 'ADDROBJ.DBF',
|
74
|
-
house_intervals: 'HOUSEINT.DBF',
|
75
|
-
landmarks: 'LANDMARK.DBF'
|
76
|
-
}.merge(
|
77
|
-
HOUSES_ACCESSORS
|
78
|
-
)
|
79
|
-
|
80
|
-
DEFAULT_ENCODING = Encoding::CP866
|
81
|
-
|
82
|
-
attr_reader *DBF_ACCESSORS.keys
|
83
|
-
attr_reader :houses
|
84
|
-
|
85
|
-
private
|
86
|
-
attr_accessor :pathspec
|
87
|
-
attr_writer *DBF_ACCESSORS.keys
|
88
|
-
attr_writer :houses
|
89
|
-
end
|
90
|
-
end
|
data/lib/fias/importer.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
module Fias
|
2
|
-
module Importer
|
3
|
-
# Фактори-метод, возвращает ссылку на объект импортера
|
4
|
-
# Принимает параметры:
|
5
|
-
# :adapter - название адаптера
|
6
|
-
# :connection - прямое соединение с базой данных (connection.raw_connection)
|
7
|
-
def self.build(options = {})
|
8
|
-
adapter = options.delete(:adapter) ||
|
9
|
-
ActiveRecord::Base.connection_config[:adapter]
|
10
|
-
|
11
|
-
connection = options.delete(:connection) ||
|
12
|
-
ActiveRecord::Base.connection.raw_connection
|
13
|
-
|
14
|
-
case adapter
|
15
|
-
when 'postgresql'
|
16
|
-
Pg.new(
|
17
|
-
connection,
|
18
|
-
options
|
19
|
-
)
|
20
|
-
when 'sqlite3'
|
21
|
-
Sqlite.new(
|
22
|
-
connection,
|
23
|
-
options
|
24
|
-
)
|
25
|
-
else
|
26
|
-
raise 'Only postgres & sqlite supported now, fork'
|
27
|
-
end
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
data/lib/fias/importer/base.rb
DELETED
@@ -1,59 +0,0 @@
|
|
1
|
-
module Fias
|
2
|
-
module Importer
|
3
|
-
class Base
|
4
|
-
def initialize(connection, options = {})
|
5
|
-
self.prefix = options.delete(:prefix) || 'fias'
|
6
|
-
self.connection = connection
|
7
|
-
end
|
8
|
-
|
9
|
-
# На входе - хеш (имя таблицы => dbf)
|
10
|
-
def schema(tables)
|
11
|
-
"".tap do |s|
|
12
|
-
tables.each do |name, dbf|
|
13
|
-
if dbf.present?
|
14
|
-
s << schema_for(name, table_name(name), dbf)
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def schema_for(name, table_name, dbf)
|
21
|
-
"".tap do |s|
|
22
|
-
s << %{create_table "#{table_name}", id: false do |t|\n}
|
23
|
-
s << schema_columns(name, dbf)
|
24
|
-
s << "end\n"
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def import(tables, &block)
|
29
|
-
tables.each do |name, dbf|
|
30
|
-
if dbf
|
31
|
-
import_table(name, table_name(name), dbf, &block)
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def import_table(name, table_name, dbf, &block)
|
37
|
-
raise NotImplementedError, 'Implement this in concrete class'
|
38
|
-
end
|
39
|
-
|
40
|
-
protected
|
41
|
-
attr_accessor :prefix, :connection
|
42
|
-
|
43
|
-
private
|
44
|
-
def table_name(name)
|
45
|
-
"#{prefix}_#{name}"
|
46
|
-
end
|
47
|
-
|
48
|
-
def schema_columns(accessor, table)
|
49
|
-
"".tap do |s|
|
50
|
-
table.columns.each do |column|
|
51
|
-
column_name = column.name.downcase
|
52
|
-
column_def = column.schema_definition
|
53
|
-
s << " t.column #{column_def}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
data/lib/fias/importer/pg.rb
DELETED
@@ -1,81 +0,0 @@
|
|
1
|
-
module Fias
|
2
|
-
module Importer
|
3
|
-
# Класс для импорта данных из ФИАС в PostgreSQL.
|
4
|
-
# Используется COPY FROM STDIN.
|
5
|
-
class Pg < Base
|
6
|
-
def schema_for(name, table_name, dbf)
|
7
|
-
super + alter_table_to_pg_uuids(name, table_name)
|
8
|
-
end
|
9
|
-
|
10
|
-
private
|
11
|
-
def alter_table_to_pg_uuids(name, table_name)
|
12
|
-
"".tap do |s|
|
13
|
-
columns = CONVERT_TO_UUID[name]
|
14
|
-
if columns.present?
|
15
|
-
columns.each do |column|
|
16
|
-
s << %{ActiveRecord::Base.connection.execute("ALTER TABLE #{table_name} ALTER COLUMN #{column} TYPE UUID USING CAST (#{column} AS UUID);")\n}
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def import_table(name, table_name, dbf, &block)
|
23
|
-
fields = table_fields(dbf)
|
24
|
-
columns = table_columns(fields)
|
25
|
-
|
26
|
-
truncate_table(table_name)
|
27
|
-
copy_from_stdin(table_name, columns)
|
28
|
-
|
29
|
-
dbf.each_with_index do |record, index|
|
30
|
-
should_import = yield(name, record.attributes, index) if block_given?
|
31
|
-
|
32
|
-
unless should_import === false
|
33
|
-
data = record.to_a
|
34
|
-
put_data(data)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
put_copy_end
|
39
|
-
end
|
40
|
-
|
41
|
-
def truncate_table(table_name)
|
42
|
-
connection.exec "TRUNCATE TABLE #{table_name};"
|
43
|
-
end
|
44
|
-
|
45
|
-
def table_fields(table)
|
46
|
-
table.columns.map(&:name).map(&:downcase)
|
47
|
-
end
|
48
|
-
|
49
|
-
def table_columns(fields)
|
50
|
-
fields.join(', ')
|
51
|
-
end
|
52
|
-
|
53
|
-
def copy_from_stdin(table_name, columns)
|
54
|
-
sql = "COPY #{table_name} (#{columns}) FROM STDIN NULL AS '-nil-'\n"
|
55
|
-
connection.exec(sql)
|
56
|
-
end
|
57
|
-
|
58
|
-
def put_data(data)
|
59
|
-
data.map! { |item| item == "" ? '-nil-' : item }
|
60
|
-
line = data.join("\t") + "\n"
|
61
|
-
connection.put_copy_data(line)
|
62
|
-
end
|
63
|
-
|
64
|
-
def put_copy_end
|
65
|
-
connection.put_copy_end
|
66
|
-
|
67
|
-
while res = connection.get_result
|
68
|
-
result_status = res.res_status(res.result_status)
|
69
|
-
unless result_status == 'PGRES_COMMAND_OK'
|
70
|
-
raise "Import failure: #{result_status}"
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
# Эти поля нужно отконвертировать в тип UUID после создания
|
77
|
-
CONVERT_TO_UUID = {
|
78
|
-
address_objects: %w(aoguid aoid previd nextid parentguid)
|
79
|
-
}
|
80
|
-
end
|
81
|
-
end
|