fias_reader 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0487bfee5053fa367df10d23d433effe5c1cbf9b
4
+ data.tar.gz: c221923b51e134a479fad411d742111196bfbc30
5
+ SHA512:
6
+ metadata.gz: 1c760dc5c617ac874ba55a2e0e4d64307dbb1eb6afd73ee2c3fd1046eb3abf8b504a098eed0fb8dccf9666a917d5fd44479bc15cbdfbd9a735d29de8b126bcf3
7
+ data.tar.gz: c1dcd52754c78df2c6f1c7aa7674831a16cff3b3d84afe21faff3bd47d315b38cef3ddd52b12830bbedb00f0f1ff6b49aee74bf44ffb8782541feba32e5743c0
@@ -0,0 +1,11 @@
1
+ .bundle/
2
+ .yardoc
3
+ Gemfile.lock
4
+ *.gem
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /spec/db/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.4
4
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'dm-types',
4
+ git: 'git://github.com/julienma/dm-types.git',
5
+ branch: 'gem-v1.2.2-with-frozen-nilclass-fix'
6
+
7
+ gemspec
@@ -0,0 +1,65 @@
1
+ # FiasReader
2
+
3
+ Библиотека для работы с XML базой ФИАС. Библиотека позволяет провести обход по всем активным записям домов.
4
+
5
+ ## Installation
6
+
7
+ Добваить строку в Gemfile:
8
+
9
+ ```ruby
10
+ gem 'fias_reader', path: '/path_to_dir/fias_reader'
11
+ ```
12
+
13
+ И выполнить после:
14
+
15
+ ```
16
+ $ bundle
17
+ ```
18
+
19
+ ## Usage
20
+
21
+ Перед запуском лучше исполнить rake для построения индекса:
22
+
23
+ ```
24
+ rake fias_reader:build DB_PATH=/path/to/dir
25
+ ```
26
+
27
+ если запускать без него то индекс построится в процессе выполнения.
28
+
29
+ Для просотого обхода по всем записям домов нужно вызвать итератор следующим способом:
30
+
31
+ ``` ruby
32
+ FiasReader.open('/path/to/unpacked/fais/xml/dir/').each do |row|
33
+ # somecode here
34
+ end
35
+ ```
36
+
37
+ Итератор передает аргумент типа `FiasReader::Reader::Row`, который имеет методы:
38
+ * `postal_code` - почтовый индекс
39
+ * `house_number` - номер дома
40
+ * `house_building_number` - номер корпуса, опционально
41
+ * `house_structure_number` - номер строения, опционально
42
+ * `levels` - хеш всех уровни частей адреса
43
+ * Части адреса возращаются либо масивом из двух объектов(сокращенное название типа части адреса, название части адреса). В случае если часть адреса не указана возвращаем nil.
44
+ * `street` - улица
45
+ * `settlement` - населенный пункт
46
+ * `city_district` - внутригородская территория
47
+ * `city` - город
48
+ * `district` - район
49
+ * `autonomy` - автономный округ
50
+ * `state` - регион
51
+
52
+
53
+ После выполнения работы нужно удалить индекс:
54
+
55
+ ```
56
+ rake fias_reader:clear
57
+ ```
58
+
59
+ ## Development
60
+
61
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
62
+
63
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
64
+
65
+ ## Contributing
@@ -0,0 +1,9 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ spec = Gem::Specification.find_by_name 'fias'
6
+ load "#{spec.gem_dir}/tasks/download.rake"
7
+ load "#{spec.gem_dir}/tasks/db.rake"
8
+
9
+ task default: :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'fias_reader'
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require 'irb'
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'fias_reader/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'fias_reader'
8
+ spec.version = FiasReader::VERSION
9
+ spec.authors = ["Андрей Большов", "uno4ki"]
10
+ spec.email = ['asnow.dev@gmail.ru', 'i.have@no.mail']
11
+
12
+ spec.summary = 'FIAS reader'
13
+ spec.description = 'FIAS reader'
14
+ spec.homepage = 'http://www.thefurnish.ru/'
15
+ spec.license = 'MIT'
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = 'exe'
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_dependency 'ox', '~> 2.4'
23
+ spec.add_dependency 'activesupport', '~> 4.2'
24
+ spec.add_dependency 'data_mapper', '~>1.2'
25
+ spec.add_dependency 'dm-sqlite-adapter', '~>1.2'
26
+ spec.add_dependency 'sqlite3', '~>1.3'
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.11'
29
+ spec.add_development_dependency 'rake', '~> 10.0'
30
+ spec.add_development_dependency 'rspec', '~> 3.0'
31
+ spec.add_development_dependency 'pry'
32
+ spec.add_development_dependency 'ruby-prof'
33
+ spec.add_development_dependency 'pry-byebug'
34
+ spec.add_development_dependency 'pry-stack_explorer'
35
+ end
@@ -0,0 +1,41 @@
1
+ require 'fias_reader/version'
2
+
3
+ require 'date'
4
+ require 'sqlite3'
5
+ require 'ox'
6
+ require 'data_mapper'
7
+ require 'dm-migrations'
8
+ require 'dm-types'
9
+ begin
10
+ require 'pry'
11
+ rescue
12
+ nil
13
+ end
14
+ require 'active_support/all'
15
+ require 'fias_reader/parse_logic/abstract'
16
+ require 'fias_reader/parse_logic/attributes'
17
+ require 'fias_reader/parse_logic/attributes_select'
18
+ require 'fias_reader/parse_logic/depth'
19
+ require 'fias_reader/parse_logic/logger'
20
+ require 'fias_reader/parse_logic/rows'
21
+ require 'fias_reader/parse_logic/stub'
22
+ require 'fias_reader/parse_logic'
23
+ require 'fias_reader/parser'
24
+ require 'fias_reader/scope'
25
+ require 'fias_reader/cache'
26
+ require 'fias_reader/cache/guid'
27
+ require 'fias_reader/cache/address_part/level_adapter'
28
+ require 'fias_reader/cache/address_part'
29
+ require 'fias_reader/table'
30
+ require 'fias_reader/table/house'
31
+ require 'fias_reader/table/address_part'
32
+
33
+ require 'fias_reader/reader/row'
34
+ require 'fias_reader/reader'
35
+
36
+ module FiasReader
37
+ # Создаем инстанс ФИАС базы. Параметр путь до файлов базы
38
+ def self.open(path)
39
+ FiasReader::Reader.new path
40
+ end
41
+ end
@@ -0,0 +1,38 @@
1
+ module FiasReader
2
+ module Cache
3
+ FILE = '/tmp/fias_reader_cache.db'.freeze
4
+
5
+ def init(reader)
6
+ reindex = true unless File.exist?(FILE)
7
+
8
+ DataMapper.setup(:default, "sqlite://#{FILE}")
9
+ begin
10
+ DataMapper.auto_upgrade!
11
+ rescue
12
+ nil
13
+ end
14
+ adapter = DataMapper.repository(:default).adapter
15
+ adapter.execute('PRAGMA journal_mode=OFF; PRAGMA synchronous=OFF; PRAGMA locking_mode = EXCLUSIVE; PRAGMA count_changes = OFF; PRAGMA cache_size=500000; PRAGMA temp_store = MEMORY; PRAGMA auto_vacuum = NONE;')
16
+ index_build(reader) if reindex
17
+ end
18
+
19
+ def clear
20
+ File.delete(FILE)
21
+ end
22
+
23
+ def index_build(reader)
24
+ adapter = DataMapper.repository(:default).adapter
25
+ adapter.execute('BEGIN;')
26
+ puts "Индексируем части адресов..."
27
+ FiasReader::Cache::AddressPart.index reader
28
+ puts "Индексируем уровни адресов..."
29
+ adapter.execute('CREATE INDEX `aolevel_index` ON `fias_reader_cache_address_parts` (`aolevel` );')
30
+ puts "Индексируем структуру частеи адресов..."
31
+ FiasReader::Cache::AddressPart.index_levels
32
+ puts "Индексирование закончено..."
33
+ adapter.execute('COMMIT;')
34
+ end
35
+
36
+ module_function :init, :clear, :index_build
37
+ end
38
+ end
@@ -0,0 +1,118 @@
1
+ module FiasReader
2
+ module Cache
3
+ class AddressPart
4
+ include DataMapper::Resource
5
+ ZERO_STR = '0'.freeze
6
+
7
+ # property :AOGUID, String
8
+ property :id0, Integer, key: true
9
+ property :id1, Integer, key: true
10
+ property :id2, Integer, key: true
11
+ property :id3, Integer, key: true
12
+ property :SHORTNAME, String, length: 10
13
+ property :OFFNAME, String, length: 120
14
+ property :levels_filled, Boolean
15
+ property :shortname_l1, String, length: 10
16
+ property :offname_l1, String, length: 120
17
+ property :shortname_l2, String, length: 10
18
+ property :offname_l2, String, length: 120
19
+ property :shortname_l3, String, length: 10
20
+ property :offname_l3, String, length: 120
21
+ property :shortname_l4, String, length: 10
22
+ property :offname_l4, String, length: 120
23
+ property :shortname_l5, String, length: 10
24
+ property :offname_l5, String, length: 120
25
+ property :shortname_l6, String, length: 10
26
+ property :offname_l6, String, length: 120
27
+ property :shortname_l7, String, length: 10
28
+ property :offname_l7, String, length: 120
29
+ property :shortname_l8, String, length: 10
30
+ property :offname_l8, String, length: 120
31
+ property :shortname_l9, String, length: 10
32
+ property :offname_l9, String, length: 120
33
+ property :shortname_l65, String, length: 10
34
+ property :offname_l65, String, length: 120
35
+ property :shortname_l75, String, length: 10
36
+ property :offname_l75, String, length: 120
37
+ property :shortname_l90, String, length: 10
38
+ property :offname_l90, String, length: 120
39
+ property :shortname_l91, String, length: 10
40
+ property :offname_l91, String, length: 120
41
+ property :AOLEVEL, Integer
42
+ # property :PARENTGUID, String
43
+ property :parent0, Integer
44
+ property :parent1, Integer
45
+ property :parent2, Integer
46
+ property :parent3, Integer
47
+
48
+ require 'ruby-prof'
49
+
50
+ def self.index(reader)
51
+ attrs = properties.map(&:name)
52
+ FiasReader::Table::AddressPart.new(reader).query.all do |row|
53
+ next if row[:LIVESTATUS] == ZERO_STR
54
+ begin
55
+ params = FiasReader::Cache::Guid.new(row[:AOGUID]).to_attr(:id)
56
+ params.merge! FiasReader::Cache::Guid.new(row[:PARENTGUID]).to_attr(:parent)
57
+ params[:SHORTNAME] = row[:SHORTNAME]
58
+ params[:OFFNAME] = row[:OFFNAME]
59
+ params[:AOLEVEL] = row[:AOLEVEL]
60
+ create params
61
+ rescue DataObjects::IntegrityError => e
62
+ end
63
+ end
64
+ end
65
+
66
+ def self.index_levels
67
+ puts "Части 4..."
68
+ index_levels_batch '4'
69
+ puts "Части 6..."
70
+ index_levels_batch '6'
71
+ puts "Части 7..."
72
+ index_levels_batch '7'
73
+ end
74
+
75
+ def self.index_levels_batch(level)
76
+ start = -1
77
+ i = 0
78
+
79
+ while start != i
80
+ start = i
81
+ all(offset: i, limit: 1000, AOLEVEL: level).each do |item|
82
+ i += 1
83
+ puts Time.new if (i % 10_000).zero?
84
+ item.parent_string
85
+ end
86
+ end
87
+ end
88
+
89
+ def levels
90
+ @levels ||= FiasReader::Cache::AddressPart::LevelAdapter.new(self)
91
+ end
92
+
93
+ def parent_string
94
+ return levels if levels_filled
95
+
96
+ if parent0.nil?
97
+ levels.reset_own
98
+ else
99
+ # some bug with parent DataObjects::SyntaxError, so using first
100
+ parent_item = FiasReader::Cache::AddressPart.first(id0: parent0, id1: parent1, id2: parent2, id3: parent3)
101
+
102
+ if parent_item
103
+ levels.copy(parent_item.parent_string)
104
+ else
105
+ levels.reset_own
106
+ end
107
+ save
108
+ end
109
+
110
+ levels
111
+ end
112
+
113
+ belongs_to :parent, 'AddressPart',
114
+ parent_key: [:id0, :id1, :id2, :id3],
115
+ child_key: [:parent0, :parent1, :parent2, :parent3]
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,137 @@
1
+ module FiasReader
2
+ module Cache
3
+ class AddressPart
4
+ class LevelAdapter
5
+ module AddressObjectLevel
6
+ STREET = '7'.freeze
7
+ SETTLEMENT = '6'.freeze
8
+ CITY_DISTRICT = '5'.freeze
9
+ CITY = '4'.freeze
10
+ STATE_DISTRICT = '3'.freeze
11
+ AUTONOMY = '2'.freeze
12
+ STATE = '1'.freeze
13
+ # 90 – уровень дополнительных территорий
14
+ # 91 – уровень подчиненных дополнительным территориям объектов
15
+ end
16
+
17
+ attr_reader :item
18
+
19
+ def initialize(item)
20
+ @item = item
21
+ end
22
+
23
+ def copy(other)
24
+ @item.shortname_l1 = other.item.shortname_l1
25
+ @item.offname_l1 = other.item.offname_l1
26
+ @item.shortname_l2 = other.item.shortname_l2
27
+ @item.offname_l2 = other.item.offname_l2
28
+ @item.shortname_l3 = other.item.shortname_l3
29
+ @item.offname_l3 = other.item.offname_l3
30
+ @item.shortname_l4 = other.item.shortname_l4
31
+ @item.offname_l4 = other.item.offname_l4
32
+ @item.shortname_l5 = other.item.shortname_l5
33
+ @item.offname_l5 = other.item.offname_l5
34
+ @item.shortname_l6 = other.item.shortname_l6
35
+ @item.offname_l6 = other.item.offname_l6
36
+ @item.shortname_l7 = other.item.shortname_l7
37
+ @item.offname_l7 = other.item.offname_l7
38
+ @item.shortname_l8 = other.item.shortname_l8
39
+ @item.offname_l8 = other.item.offname_l8
40
+ @item.shortname_l9 = other.item.shortname_l9
41
+ @item.offname_l9 = other.item.offname_l9
42
+ @item.shortname_l65 = other.item.shortname_l65
43
+ @item.offname_l65 = other.item.offname_l65
44
+ @item.shortname_l75 = other.item.shortname_l75
45
+ @item.offname_l75 = other.item.offname_l75
46
+ @item.shortname_l90 = other.item.shortname_l90
47
+ @item.offname_l90 = other.item.offname_l90
48
+ @item.shortname_l91 = other.item.shortname_l91
49
+ @item.offname_l91 = other.item.offname_l91
50
+ reset_own
51
+ end
52
+
53
+ def reset_own
54
+ case @item[:AOLEVEL]
55
+ when 1
56
+ @item.shortname_l1 = @item[:SHORTNAME]
57
+ @item.offname_l1 = @item[:OFFNAME]
58
+ when 2
59
+ @item.shortname_l2 = @item[:SHORTNAME]
60
+ @item.offname_l2 = @item[:OFFNAME]
61
+ when 3
62
+ @item.shortname_l3 = @item[:SHORTNAME]
63
+ @item.offname_l3 = @item[:OFFNAME]
64
+ when 4
65
+ @item.shortname_l4 = @item[:SHORTNAME]
66
+ @item.offname_l4 = @item[:OFFNAME]
67
+ when 5
68
+ @item.shortname_l5 = @item[:SHORTNAME]
69
+ @item.offname_l5 = @item[:OFFNAME]
70
+ when 6
71
+ @item.shortname_l6 = @item[:SHORTNAME]
72
+ @item.offname_l6 = @item[:OFFNAME]
73
+ when 7
74
+ @item.shortname_l7 = @item[:SHORTNAME]
75
+ @item.offname_l7 = @item[:OFFNAME]
76
+ when 8
77
+ @item.shortname_l8 = @item[:SHORTNAME]
78
+ @item.offname_l8 = @item[:OFFNAME]
79
+ when 9
80
+ @item.shortname_l9 = @item[:SHORTNAME]
81
+ @item.offname_l9 = @item[:OFFNAME]
82
+ when 65
83
+ @item.shortname_l65 = @item[:SHORTNAME]
84
+ @item.offname_l65 = @item[:OFFNAME]
85
+ when 75
86
+ @item.shortname_l75 = @item[:SHORTNAME]
87
+ @item.offname_l75 = @item[:OFFNAME]
88
+ when 90
89
+ @item.shortname_l90 = @item[:SHORTNAME]
90
+ @item.offname_l90 = @item[:OFFNAME]
91
+ when 91
92
+ @item.shortname_l91 = @item[:SHORTNAME]
93
+ @item.offname_l91 = @item[:OFFNAME]
94
+ else
95
+ raise 'errors'
96
+ end
97
+ @item.levels_filled = true
98
+ end
99
+
100
+ # Возвращает улицу
101
+ def street
102
+ [@item.shortname_l7, @item.offname_l7]
103
+ end
104
+
105
+ # Возвращает деревню, поселок
106
+ def settlement
107
+ [@item.shortname_l6, @item.offname_l6]
108
+ end
109
+
110
+ # Возвращает внутригородской территории
111
+ def city_district
112
+ [@item.shortname_l5, @item.offname_l5]
113
+ end
114
+
115
+ # Возвращает город
116
+ def city
117
+ [@item.shortname_l4, @item.offname_l4]
118
+ end
119
+
120
+ # Возвращает район
121
+ def district
122
+ [@item.shortname_l3, @item.offname_l3]
123
+ end
124
+
125
+ # Возвращает автономный округ
126
+ def autonomy
127
+ [@item.shortname_l2, @item.offname_l2]
128
+ end
129
+
130
+ # Возвращает область
131
+ def state
132
+ [@item.shortname_l1, @item.offname_l1]
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,70 @@
1
+ module FiasReader
2
+ module Cache
3
+ class Guid
4
+ DASH_STR = '-'.freeze
5
+ EMPTY_STR = ''.freeze
6
+ GUID_PARTS = [
7
+ 0..8,
8
+ 8..16,
9
+ 16..24,
10
+ 24..32
11
+ ].freeze
12
+
13
+ def initialize(*args)
14
+ if args.size == 1
15
+ guid = args[0]
16
+ case guid
17
+ when Hash
18
+ init_from_hash guid
19
+ when Array
20
+ init_from_array guid
21
+ else
22
+ init_from_guid guid
23
+ end
24
+
25
+ elsif args.size == 2
26
+ init_from_hash_slice args[0], args[1]
27
+ elsif args.size > 2
28
+ init_from_array args
29
+ end
30
+ end
31
+
32
+ def to_attr(name)
33
+ @parts.each_with_index.each_with_object({}) do |(part, index), result|
34
+ result["#{name}#{index}"] = part
35
+ end
36
+ end
37
+
38
+ GUID_FORMAT_STR = '%08x'.freeze
39
+ def to_guid
40
+ @parts.each_with_object('') { |i, str| str << format(GUID_FORMAT_STR, i) }
41
+ end
42
+
43
+ def parts
44
+ @parts ||= []
45
+ end
46
+
47
+ attr_writer :parts
48
+
49
+ def init_from_array(val)
50
+ self.parts = val
51
+ end
52
+
53
+ def init_from_guid(guid)
54
+ (self.parts = []) && return unless guid
55
+ guid.tr!(DASH_STR, EMPTY_STR)
56
+ self.parts = GUID_PARTS.map { |range| guid[range].to_i(16) }
57
+ end
58
+
59
+ def init_from_hash(hash)
60
+ hash.each { |key, val| parts[key[-1].to_i] = val }
61
+ end
62
+
63
+ def init_from_hash_slice(hash, mask)
64
+ self.parts = hash.keys.select { |key| key.to_s =~ /^#{mask}\d/ }.each_with_object([]) do |key, obj|
65
+ obj[key[-1].to_i] = hash[key]
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,48 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ extend ActiveSupport::Concern
4
+ attr_accessor :element_name, :attr_name, :attr_value, :text
5
+
6
+ def self.build(logics)
7
+ Class.new do
8
+ include FiasReader::ParseLogic::Stub
9
+
10
+ logics.reverse.each do |logic|
11
+ include logic
12
+ end
13
+
14
+ include FiasReader::ParseLogic
15
+ end
16
+ end
17
+
18
+ included do
19
+ # define_callbacks :initialize, :start_element, :end_element, :attr, :text
20
+
21
+ def initialize(options)
22
+ @options = options.dup.freeze
23
+ super
24
+ end
25
+ end
26
+
27
+ def start_element(name)
28
+ @element_name = name
29
+ super
30
+ end
31
+
32
+ def end_element(name)
33
+ @element_name = name
34
+ super
35
+ end
36
+
37
+ def attr(name, value)
38
+ @attr_name = name
39
+ @attr_value = value
40
+ super
41
+ end
42
+
43
+ def text(value)
44
+ @text = value
45
+ super
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,6 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Abstract
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Attributes
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def attr(name, value)
7
+ @row[name] = value if @row
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module AttributesSelect
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def attr(name, value)
7
+ @row[name] = value if @row && @options[:select].include?(name)
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,22 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Depth
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def initialize(options)
7
+ @depth = 0
8
+ super
9
+ end
10
+
11
+ def start_element(name)
12
+ @depth += 1
13
+ super
14
+ end
15
+
16
+ def end_element(name)
17
+ @depth -= 1
18
+ super
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Logger
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def start_element(name)
7
+ puts "start: #{name}"
8
+ super
9
+ end
10
+
11
+ def end_element(name)
12
+ puts "end: #{name}"
13
+ super
14
+ end
15
+
16
+ def attr(name, value)
17
+ puts " #{name} => #{value}"
18
+ super
19
+ end
20
+
21
+ def text(value)
22
+ puts "text #{value}"
23
+ super
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Rows
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def initialize(options)
7
+ @rows_enabled = true if @options[:rows].respond_to? :call
8
+ super
9
+ end
10
+
11
+ def start_element(name)
12
+ if @rows_enabled && @depth == 2
13
+ @row = {}
14
+ @skip = false
15
+ end
16
+ super
17
+ end
18
+
19
+ def end_element(name)
20
+ if @rows_enabled && @depth == 1
21
+ @options[:rows].call(@row) if @row
22
+ @row = nil
23
+ end
24
+ super
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,22 @@
1
+ module FiasReader
2
+ module ParseLogic
3
+ module Stub
4
+ include FiasReader::ParseLogic::Abstract
5
+
6
+ def initialize(options)
7
+ end
8
+
9
+ def start_element(name)
10
+ end
11
+
12
+ def end_element(name)
13
+ end
14
+
15
+ def attr(name, value)
16
+ end
17
+
18
+ def text(value)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ module FiasReader
2
+ class Parser < ::Ox::Sax
3
+ attr_reader :logics
4
+ delegate :start_element, :end_element, :attr, :text, to: :logics
5
+
6
+ def initialize(logics, options)
7
+ @logics = ParseLogic.build(logics).new options
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,35 @@
1
+ module FiasReader
2
+ # Инерфейс для работы XML с базой ФИАС
3
+ class Reader
4
+ attr_reader :path
5
+ def initialize(path)
6
+ @path = path
7
+ end
8
+
9
+ # Итерируем по всем активным записям домов, принимает блок в качестве аргумента
10
+ # В вызываемы блок передается объект типа FiasReader::Reader::Row.
11
+ XML_DATE_SEPARATOR = '-'.freeze
12
+ EMPTY_STRING = '0'.freeze
13
+ TODAY = Date.today.strftime('%Y0%m0%d').to_i
14
+ def each
15
+ Cache.init self
16
+
17
+ Table::House.new(self).query.all do |row|
18
+ next unless active?(row)
19
+ row = FiasReader::Reader::Row.new row
20
+ yield row if row.address_object
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def active?(row)
27
+ date_to_int(row[:ENDDATE]) >= TODAY && date_to_int(row[:STARTDATE]) < TODAY
28
+ end
29
+
30
+ def date_to_int(date)
31
+ date.tr!(XML_DATE_SEPARATOR, EMPTY_STRING)
32
+ date.to_i
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,80 @@
1
+ module FiasReader
2
+ class Reader
3
+ # Представление строки итерироемой Reader'ом
4
+ class Row
5
+ def initialize(house_row)
6
+ @house_row = house_row
7
+ end
8
+
9
+ # Возвращает объект части адреса к которой прикреплен дом(объект улицы)
10
+ def address_object
11
+ return @address_object unless @address_object.nil?
12
+ @address_object = FiasReader::Cache::AddressPart.first(address_object_id.to_attr(:id)) || false
13
+ end
14
+
15
+ def address_object_id
16
+ FiasReader::Cache::Guid.new(@house_row[:AOGUID])
17
+ end
18
+
19
+ # Возвращает хеш уровней частей адреса к которой прикреплен дом(объект улицы)
20
+ def levels
21
+ address_object.parent_string
22
+ end
23
+
24
+ # Возвращает номер дома
25
+ def house_number
26
+ @house_row[:HOUSENUM]
27
+ end
28
+
29
+ # Возвращает номер корпуса
30
+ def house_building_number
31
+ @house_row[:BUILDNUM]
32
+ end
33
+
34
+ # Возвращает номер строения
35
+ def house_structure_number
36
+ @house_row[:STRUCNUM]
37
+ end
38
+
39
+ # Возвращает улицу
40
+ def street
41
+ levels.street
42
+ end
43
+
44
+ # Возвращает почтовый индекс
45
+ def postal_code
46
+ @house_row[:POSTALCODE]
47
+ end
48
+
49
+ # Возвращает деревню, поселок
50
+ def settlement
51
+ levels.settlement
52
+ end
53
+
54
+ # Возвращает внутригородской территории
55
+ def city_district
56
+ levels.city_district
57
+ end
58
+
59
+ # Возвращает город
60
+ def city
61
+ levels.city
62
+ end
63
+
64
+ # Возвращает район
65
+ def district
66
+ levels.district
67
+ end
68
+
69
+ # Возвращает автономный округ
70
+ def autonomy
71
+ levels.autonomy
72
+ end
73
+
74
+ # Возвращает область
75
+ def state
76
+ levels.state
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,48 @@
1
+ module FiasReader
2
+ # Инерфейс для работы XML с базой ФИАС
3
+ class Scope
4
+ attr_reader :options, :logics
5
+
6
+ def initialize(table)
7
+ @table = table
8
+ @options = {}
9
+ @logics = @table.class::LOGICS.dup
10
+ end
11
+
12
+ def all
13
+ @options[:rows] = -> (row) { yield row }
14
+ parse!
15
+ end
16
+
17
+ def first
18
+ result = nil
19
+ catch :fias_parse_stop do
20
+ @options[:rows] = -> (row) do
21
+ result = row
22
+ throw :fias_parse_stop
23
+ end
24
+ parse!
25
+ end
26
+ result
27
+ end
28
+
29
+ def select(*args)
30
+ index = @logics.index(FiasReader::ParseLogic::Attributes)
31
+ @logics[index] = FiasReader::ParseLogic::AttributesSelect if index
32
+ @options[:select] = args
33
+ self
34
+ end
35
+
36
+ def parse!(parser = default_parser)
37
+ Ox.sax_parse parser, @table.file, symbolize: true
38
+ end
39
+
40
+ def default_parser
41
+ FiasReader::Parser.new(logics, @options)
42
+ end
43
+
44
+ # def logics
45
+ # @logics #+ [ParseLogic::Logger]
46
+ # end
47
+ end
48
+ end
@@ -0,0 +1,28 @@
1
+ module FiasReader
2
+ # Инерфейс для работы XML с базой ФИАС
3
+ class Table
4
+ LOGICS = [
5
+ ParseLogic::Depth,
6
+ ParseLogic::Rows,
7
+ ParseLogic::Attributes
8
+ ].freeze
9
+ def initialize(reader)
10
+ @reader = reader
11
+ end
12
+
13
+ def query
14
+ FiasReader::Scope.new(self)
15
+ end
16
+
17
+ def file
18
+ File.new(file_path, 'r')
19
+ end
20
+
21
+ def file_path
22
+ Dir[
23
+ "#{@reader.path}/#{self.class::File}_*.XML",
24
+ "#{@reader.path}/#{self.class::File}_*.xml"
25
+ ].first
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,7 @@
1
+ module FiasReader
2
+ class Table
3
+ class AddressPart < FiasReader::Table
4
+ File = 'AS_ADDROBJ'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module FiasReader
2
+ class Table
3
+ class House < FiasReader::Table
4
+ File = 'AS_HOUSE'.freeze
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module FiasReader
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,11 @@
1
+ namespace :fias_reader do
2
+ task :build do
3
+ raise "Вы должны указать путь к XML базе ФИАС в переменной окружения DB_PATH" unless ENV[:DB_PATH]
4
+ raise "Не верно указан путь к XML базе ФИАС в переменной окружения DB_PATH" unless File.exist? ENV[:DB_PATH]
5
+ Cache.init FiasReader.open(ENV[:DB_PATH])
6
+ end
7
+
8
+ task :clear do
9
+ Cache.clear
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,245 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fias_reader
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Андрей Большов
8
+ - uno4ki
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2016-06-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ox
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.4'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.4'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activesupport
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '4.2'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '4.2'
42
+ - !ruby/object:Gem::Dependency
43
+ name: data_mapper
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.2'
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.2'
56
+ - !ruby/object:Gem::Dependency
57
+ name: dm-sqlite-adapter
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.2'
63
+ type: :runtime
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.2'
70
+ - !ruby/object:Gem::Dependency
71
+ name: sqlite3
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - "~>"
75
+ - !ruby/object:Gem::Version
76
+ version: '1.3'
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.3'
84
+ - !ruby/object:Gem::Dependency
85
+ name: bundler
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '1.11'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '1.11'
98
+ - !ruby/object:Gem::Dependency
99
+ name: rake
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '10.0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '10.0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: rspec
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '3.0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '3.0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: pry
128
+ requirement: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ type: :development
134
+ prerelease: false
135
+ version_requirements: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ - !ruby/object:Gem::Dependency
141
+ name: ruby-prof
142
+ requirement: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ">="
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ type: :development
148
+ prerelease: false
149
+ version_requirements: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ - !ruby/object:Gem::Dependency
155
+ name: pry-byebug
156
+ requirement: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ type: :development
162
+ prerelease: false
163
+ version_requirements: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ">="
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ - !ruby/object:Gem::Dependency
169
+ name: pry-stack_explorer
170
+ requirement: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '0'
175
+ type: :development
176
+ prerelease: false
177
+ version_requirements: !ruby/object:Gem::Requirement
178
+ requirements:
179
+ - - ">="
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ description: FIAS reader
183
+ email:
184
+ - asnow.dev@gmail.ru
185
+ - i.have@no.mail
186
+ executables: []
187
+ extensions: []
188
+ extra_rdoc_files: []
189
+ files:
190
+ - ".gitignore"
191
+ - ".rspec"
192
+ - ".travis.yml"
193
+ - Gemfile
194
+ - README.md
195
+ - Rakefile
196
+ - bin/console
197
+ - bin/setup
198
+ - fias_reader.gemspec
199
+ - lib/fias_reader.rb
200
+ - lib/fias_reader/cache.rb
201
+ - lib/fias_reader/cache/address_part.rb
202
+ - lib/fias_reader/cache/address_part/level_adapter.rb
203
+ - lib/fias_reader/cache/guid.rb
204
+ - lib/fias_reader/parse_logic.rb
205
+ - lib/fias_reader/parse_logic/abstract.rb
206
+ - lib/fias_reader/parse_logic/attributes.rb
207
+ - lib/fias_reader/parse_logic/attributes_select.rb
208
+ - lib/fias_reader/parse_logic/depth.rb
209
+ - lib/fias_reader/parse_logic/logger.rb
210
+ - lib/fias_reader/parse_logic/rows.rb
211
+ - lib/fias_reader/parse_logic/stub.rb
212
+ - lib/fias_reader/parser.rb
213
+ - lib/fias_reader/reader.rb
214
+ - lib/fias_reader/reader/row.rb
215
+ - lib/fias_reader/scope.rb
216
+ - lib/fias_reader/table.rb
217
+ - lib/fias_reader/table/address_part.rb
218
+ - lib/fias_reader/table/house.rb
219
+ - lib/fias_reader/version.rb
220
+ - lib/tasks/build.rake
221
+ homepage: http://www.thefurnish.ru/
222
+ licenses:
223
+ - MIT
224
+ metadata: {}
225
+ post_install_message:
226
+ rdoc_options: []
227
+ require_paths:
228
+ - lib
229
+ required_ruby_version: !ruby/object:Gem::Requirement
230
+ requirements:
231
+ - - ">="
232
+ - !ruby/object:Gem::Version
233
+ version: '0'
234
+ required_rubygems_version: !ruby/object:Gem::Requirement
235
+ requirements:
236
+ - - ">="
237
+ - !ruby/object:Gem::Version
238
+ version: '0'
239
+ requirements: []
240
+ rubyforge_project:
241
+ rubygems_version: 2.6.6
242
+ signing_key:
243
+ specification_version: 4
244
+ summary: FIAS reader
245
+ test_files: []