cuzk-rest 0.1.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/.rspec +3 -0
- data/.rubocop.yml +65 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +321 -0
- data/Rakefile +10 -0
- data/lib/cuzk/rest/client.rb +209 -0
- data/lib/cuzk/rest/configuration.rb +50 -0
- data/lib/cuzk/rest/connection.rb +131 -0
- data/lib/cuzk/rest/errors.rb +50 -0
- data/lib/cuzk/rest/rate_limiter.rb +43 -0
- data/lib/cuzk/rest/resource.rb +62 -0
- data/lib/cuzk/rest/resources/app_service.rb +50 -0
- data/lib/cuzk/rest/resources/building.rb +130 -0
- data/lib/cuzk/rest/resources/building_right.rb +50 -0
- data/lib/cuzk/rest/resources/ownership.rb +102 -0
- data/lib/cuzk/rest/resources/parcel.rb +116 -0
- data/lib/cuzk/rest/resources/procedure.rb +108 -0
- data/lib/cuzk/rest/resources/registry.rb +86 -0
- data/lib/cuzk/rest/resources/territory.rb +80 -0
- data/lib/cuzk/rest/resources/unit.rb +122 -0
- data/lib/cuzk/rest/version.rb +7 -0
- data/lib/cuzk/rest.rb +41 -0
- data/lib/cuzk-rest.rb +3 -0
- metadata +127 -0
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
class Configuration
|
|
6
|
+
DEFAULTS = {
|
|
7
|
+
base_url: 'https://api-kn.cuzk.gov.cz',
|
|
8
|
+
api_version: 'v1',
|
|
9
|
+
timeout: 30,
|
|
10
|
+
open_timeout: 10,
|
|
11
|
+
retries: 3,
|
|
12
|
+
retry_interval: 0.5,
|
|
13
|
+
requests_per_minute: 60,
|
|
14
|
+
burst_limit: 10,
|
|
15
|
+
cache_ttl: 86_400, # 24 hours in seconds
|
|
16
|
+
enable_caching: false,
|
|
17
|
+
log_requests: false,
|
|
18
|
+
log_responses: false,
|
|
19
|
+
privacy_mode: :strict,
|
|
20
|
+
mask_personal_data: true
|
|
21
|
+
}.freeze
|
|
22
|
+
|
|
23
|
+
attr_accessor :api_key, :base_url, :api_version,
|
|
24
|
+
:timeout, :open_timeout,
|
|
25
|
+
:retries, :retry_interval,
|
|
26
|
+
:requests_per_minute, :burst_limit,
|
|
27
|
+
:cache_store, :cache_ttl, :enable_caching,
|
|
28
|
+
:logger, :log_requests, :log_responses,
|
|
29
|
+
:privacy_mode, :mask_personal_data,
|
|
30
|
+
:user_agent
|
|
31
|
+
|
|
32
|
+
def initialize
|
|
33
|
+
DEFAULTS.each { |key, value| public_send(:"#{key}=", value) }
|
|
34
|
+
@api_key = ENV.fetch('CUZK_REST_API_KEY', nil)
|
|
35
|
+
@user_agent = "cuzk-rest/#{CUZK::REST::VERSION} Ruby/#{RUBY_VERSION}"
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def api_url
|
|
39
|
+
"#{base_url}/api/#{api_version}"
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def validate!
|
|
43
|
+
raise ConfigurationError, 'api_key is required' if api_key.nil? || api_key.empty?
|
|
44
|
+
raise ConfigurationError, 'base_url is required' if base_url.nil? || base_url.empty?
|
|
45
|
+
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
class Connection
|
|
6
|
+
attr_reader :config, :last_response_metadata
|
|
7
|
+
|
|
8
|
+
def initialize(config = nil)
|
|
9
|
+
@config = config || CUZK::REST.configuration
|
|
10
|
+
@config.validate!
|
|
11
|
+
@rate_limiter = RateLimiter.new(
|
|
12
|
+
requests_per_minute: @config.requests_per_minute,
|
|
13
|
+
burst_limit: @config.burst_limit
|
|
14
|
+
)
|
|
15
|
+
@last_response_metadata = {}
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def get(path, params = {})
|
|
19
|
+
request(:get, path, params)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def post(path, body = {})
|
|
23
|
+
request(:post, path, body)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def request(method, path, payload = {})
|
|
29
|
+
@rate_limiter.throttle!
|
|
30
|
+
|
|
31
|
+
response = connection.public_send(method, path) do |req|
|
|
32
|
+
case method
|
|
33
|
+
when :get
|
|
34
|
+
req.params.update(payload) unless payload.empty?
|
|
35
|
+
when :post
|
|
36
|
+
req.body = Oj.dump(payload, mode: :compat) unless payload.empty?
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
handle_response(response)
|
|
41
|
+
rescue Faraday::TimeoutError, ::Timeout::Error => e
|
|
42
|
+
raise TimeoutError.new("Request timed out: #{e.message}", original_error: e)
|
|
43
|
+
rescue Faraday::ConnectionFailed => e
|
|
44
|
+
if e.message.include?('execution expired') || e.message.include?('timed out') || e.cause.is_a?(::Timeout::Error)
|
|
45
|
+
raise TimeoutError.new("Request timed out: #{e.message}", original_error: e)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
raise ConnectionError.new("Connection failed: #{e.message}", original_error: e)
|
|
49
|
+
rescue Faraday::RetriableResponse => e
|
|
50
|
+
handle_response(e.response)
|
|
51
|
+
rescue Faraday::Error => e
|
|
52
|
+
raise NetworkError.new("Network error: #{e.message}", original_error: e)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def connection
|
|
56
|
+
@connection ||= Faraday.new(url: config.api_url) do |f|
|
|
57
|
+
f.headers['Accept'] = 'application/json'
|
|
58
|
+
f.headers['Content-Type'] = 'application/json'
|
|
59
|
+
f.headers['User-Agent'] = config.user_agent
|
|
60
|
+
f.headers['Accept-Language'] = 'cs-CZ'
|
|
61
|
+
f.headers['ApiKey'] = config.api_key if config.api_key
|
|
62
|
+
|
|
63
|
+
if config.retries.positive?
|
|
64
|
+
f.request :retry, max: config.retries,
|
|
65
|
+
interval: config.retry_interval,
|
|
66
|
+
retry_statuses: [429, 500, 502, 503, 504],
|
|
67
|
+
methods: %i[get],
|
|
68
|
+
exceptions: [Faraday::TimeoutError, Faraday::ConnectionFailed]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
f.options.timeout = config.timeout
|
|
72
|
+
f.options.open_timeout = config.open_timeout
|
|
73
|
+
|
|
74
|
+
f.response :logger, config.logger, bodies: config.log_responses if config.logger && config.log_requests
|
|
75
|
+
|
|
76
|
+
f.adapter Faraday.default_adapter
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def handle_response(response)
|
|
81
|
+
body = response.respond_to?(:body) ? response.body : response[:body]
|
|
82
|
+
status = response.respond_to?(:status) ? response.status : response[:status]
|
|
83
|
+
|
|
84
|
+
case status
|
|
85
|
+
when 200..299
|
|
86
|
+
parse_body(body)
|
|
87
|
+
when 401
|
|
88
|
+
raise AuthenticationError.new('Authentication failed', status: status, response_body: body)
|
|
89
|
+
when 403
|
|
90
|
+
raise AuthorizationError.new('Access forbidden', status: status, response_body: body)
|
|
91
|
+
when 404
|
|
92
|
+
raise NotFoundError.new('Resource not found', status: status, response_body: body)
|
|
93
|
+
when 422
|
|
94
|
+
raise ValidationError.new('Validation failed', status: status, response_body: body)
|
|
95
|
+
when 429
|
|
96
|
+
raise RateLimitedError.new('Rate limit exceeded', status: status, response_body: body)
|
|
97
|
+
when 400..499
|
|
98
|
+
raise APIError.new("Client error (#{status})", status: status, response_body: body)
|
|
99
|
+
when 500..599
|
|
100
|
+
raise ServerError.new("Server error (#{status})", status: status, response_body: body)
|
|
101
|
+
else
|
|
102
|
+
raise InvalidResponseError, "Unexpected response status: #{status}"
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def parse_body(body)
|
|
107
|
+
return nil if body.nil? || body.empty?
|
|
108
|
+
|
|
109
|
+
parsed = Oj.load(body, mode: :compat, symbol_keys: false)
|
|
110
|
+
unwrap_response(parsed)
|
|
111
|
+
rescue EncodingError, Oj::ParseError, Oj::Error => e
|
|
112
|
+
raise ParsingError.new("Failed to parse response: #{e.message}", original_error: e)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def unwrap_response(parsed)
|
|
116
|
+
if wrapped_response?(parsed)
|
|
117
|
+
@last_response_metadata = parsed.except('data')
|
|
118
|
+
parsed['data']
|
|
119
|
+
else
|
|
120
|
+
@last_response_metadata = {}
|
|
121
|
+
parsed
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def wrapped_response?(parsed)
|
|
126
|
+
parsed.is_a?(Hash) && parsed.key?('data') &&
|
|
127
|
+
(parsed.key?('zpravy') || parsed.key?('provedenoVolani') || parsed.key?('aktualnostDatK'))
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
# Base error for all CUZK REST errors
|
|
6
|
+
class Error < StandardError
|
|
7
|
+
attr_reader :original_error
|
|
8
|
+
|
|
9
|
+
def initialize(message = nil, original_error: nil)
|
|
10
|
+
@original_error = original_error
|
|
11
|
+
super(message)
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Configuration errors
|
|
16
|
+
class ConfigurationError < Error; end
|
|
17
|
+
|
|
18
|
+
# API errors (HTTP response errors)
|
|
19
|
+
class APIError < Error
|
|
20
|
+
attr_reader :status, :response_body
|
|
21
|
+
|
|
22
|
+
def initialize(message = nil, status: nil, response_body: nil, **kwargs)
|
|
23
|
+
@status = status
|
|
24
|
+
@response_body = response_body
|
|
25
|
+
super(message, **kwargs)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class AuthenticationError < APIError; end
|
|
30
|
+
class AuthorizationError < APIError; end
|
|
31
|
+
class NotFoundError < APIError; end
|
|
32
|
+
class ValidationError < APIError; end
|
|
33
|
+
class RateLimitedError < APIError; end
|
|
34
|
+
class ServerError < APIError; end
|
|
35
|
+
|
|
36
|
+
# Network errors
|
|
37
|
+
class NetworkError < Error; end
|
|
38
|
+
class TimeoutError < NetworkError; end
|
|
39
|
+
class ConnectionError < NetworkError; end
|
|
40
|
+
|
|
41
|
+
# Data errors
|
|
42
|
+
class DataError < Error; end
|
|
43
|
+
class ParsingError < DataError; end
|
|
44
|
+
class InvalidResponseError < DataError; end
|
|
45
|
+
|
|
46
|
+
# Privacy & compliance errors
|
|
47
|
+
class PrivacyError < Error; end
|
|
48
|
+
class GDPRViolationError < PrivacyError; end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
class RateLimiter
|
|
6
|
+
def initialize(requests_per_minute: 60, burst_limit: 10)
|
|
7
|
+
@requests_per_minute = requests_per_minute
|
|
8
|
+
@burst_limit = burst_limit
|
|
9
|
+
@timestamps = []
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def throttle!
|
|
14
|
+
@mutex.synchronize do
|
|
15
|
+
now = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
16
|
+
cleanup_old_timestamps(now)
|
|
17
|
+
|
|
18
|
+
if @timestamps.size >= @requests_per_minute
|
|
19
|
+
oldest = @timestamps.first
|
|
20
|
+
wait_time = 60.0 - (now - oldest)
|
|
21
|
+
sleep(wait_time) if wait_time.positive?
|
|
22
|
+
cleanup_old_timestamps(Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@timestamps << Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def requests_remaining
|
|
30
|
+
@mutex.synchronize do
|
|
31
|
+
cleanup_old_timestamps(Process.clock_gettime(Process::CLOCK_MONOTONIC))
|
|
32
|
+
@requests_per_minute - @timestamps.size
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
def cleanup_old_timestamps(now)
|
|
39
|
+
@timestamps.reject! { |ts| now - ts > 60.0 }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
# Base class for all API resource objects.
|
|
6
|
+
# Provides common attribute handling and connection management.
|
|
7
|
+
class Resource
|
|
8
|
+
class << self
|
|
9
|
+
def connection
|
|
10
|
+
@connection || CUZK::REST::Client.default_connection
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
attr_writer :connection, :endpoint_path
|
|
14
|
+
|
|
15
|
+
def endpoint_path
|
|
16
|
+
@endpoint_path || raise(NotImplementedError, "#{name} must define endpoint_path")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
|
|
21
|
+
def get(path, params = {})
|
|
22
|
+
connection.get("#{endpoint_path}#{path}", params)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def build(data)
|
|
26
|
+
new(data)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def build_collection(data, key: nil)
|
|
30
|
+
items = key ? data.fetch(key, []) : data
|
|
31
|
+
return [] unless items.is_a?(Array)
|
|
32
|
+
|
|
33
|
+
items.map { |item| build(item) }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
attr_reader :attributes
|
|
38
|
+
|
|
39
|
+
def initialize(attributes = {})
|
|
40
|
+
@attributes = attributes.transform_keys(&:to_s)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def [](key)
|
|
44
|
+
@attributes[key.to_s]
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def to_h
|
|
48
|
+
@attributes.dup
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def inspect
|
|
52
|
+
"#<#{self.class.name} #{summary_attributes}>"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
private
|
|
56
|
+
|
|
57
|
+
def summary_attributes
|
|
58
|
+
''
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
module Resources
|
|
6
|
+
class AppService < Resource
|
|
7
|
+
self.endpoint_path = 'AplikacniSluzby'
|
|
8
|
+
|
|
9
|
+
def status
|
|
10
|
+
attributes['status']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def last_check
|
|
14
|
+
attributes['lastCheck']
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def total_duration
|
|
18
|
+
attributes['totalDuration']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def entries
|
|
22
|
+
attributes['entries'] || []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def health
|
|
27
|
+
data = get('/Health')
|
|
28
|
+
build(data)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def account_status
|
|
32
|
+
data = get('/StavUctu')
|
|
33
|
+
build(data)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def data_currency
|
|
37
|
+
data = get('/AktualnostDat')
|
|
38
|
+
build(data)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def summary_attributes
|
|
45
|
+
"status=#{status}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
module Resources
|
|
6
|
+
class Building < Resource
|
|
7
|
+
self.endpoint_path = 'Stavby'
|
|
8
|
+
|
|
9
|
+
def id
|
|
10
|
+
attributes['id']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def descriptive_number
|
|
14
|
+
attributes['cisloPopisne']
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def registration_number
|
|
18
|
+
attributes['cisloEvidencni']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def number
|
|
22
|
+
descriptive_number || registration_number
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def number_type
|
|
26
|
+
if descriptive_number
|
|
27
|
+
:descriptive
|
|
28
|
+
elsif registration_number
|
|
29
|
+
:registration
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def building_type
|
|
34
|
+
attributes['typStavby']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def building_type_code
|
|
38
|
+
attributes['kodTypuStavby']
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def purpose
|
|
42
|
+
attributes['zpusobVyuziti']
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def purpose_code
|
|
46
|
+
attributes['kodZpusobuVyuziti']
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cadastral_unit_code
|
|
50
|
+
attributes['kodKatastralnihoUzemi']
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def cadastral_unit_name
|
|
54
|
+
attributes['nazevKatastralnihoUzemi']
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def parcel_id
|
|
58
|
+
attributes['idParcely']
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def lv_number
|
|
62
|
+
attributes['cisloLv']
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def municipality
|
|
66
|
+
attributes['obec']
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def municipality_part
|
|
70
|
+
attributes['castObce']
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def street
|
|
74
|
+
attributes['ulice']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def house_number
|
|
78
|
+
attributes['cisloDomovni']
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def postal_code
|
|
82
|
+
attributes['psc']
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def full_address
|
|
86
|
+
parts = [street, house_number, municipality_part, municipality, postal_code].compact
|
|
87
|
+
parts.join(', ')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
class << self
|
|
91
|
+
def find(id)
|
|
92
|
+
data = get("/#{id}")
|
|
93
|
+
build(data)
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def search(params = {})
|
|
97
|
+
data = get('/Vyhledani', normalize_search_params(params))
|
|
98
|
+
build_collection(data, key: 'stavby')
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def find_by_address_point(address_code)
|
|
102
|
+
data = get("/AdresniMisto/#{address_code}")
|
|
103
|
+
build(data)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
def normalize_search_params(params)
|
|
109
|
+
mapping = {
|
|
110
|
+
municipality_part: 'KodCastiObce',
|
|
111
|
+
building_type: 'TypStavby',
|
|
112
|
+
house_number: 'CisloDomovni'
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
params.each_with_object({}) do |(key, value), result|
|
|
116
|
+
api_key = mapping[key] || key.to_s
|
|
117
|
+
result[api_key] = value
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
def summary_attributes
|
|
125
|
+
"id=#{id} number=#{number} type=#{number_type} municipality=#{municipality}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
module Resources
|
|
6
|
+
class BuildingRight < Resource
|
|
7
|
+
self.endpoint_path = 'PravaStavby'
|
|
8
|
+
|
|
9
|
+
def id
|
|
10
|
+
attributes['id']
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parcel_id
|
|
14
|
+
attributes['idParcely']
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def building_id
|
|
18
|
+
attributes['idStavby']
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def description
|
|
22
|
+
attributes['popis']
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
class << self
|
|
26
|
+
def find(id)
|
|
27
|
+
data = get("/#{id}")
|
|
28
|
+
build(data)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def find_by_parcel(parcel_id)
|
|
32
|
+
data = get("/Parcela/#{parcel_id}")
|
|
33
|
+
build_collection(data, key: 'pravaStavby')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def find_by_building(building_id)
|
|
37
|
+
data = get("/Stavba/#{building_id}")
|
|
38
|
+
build_collection(data, key: 'pravaStavby')
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def summary_attributes
|
|
45
|
+
"id=#{id} parcel=#{parcel_id} building=#{building_id}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module CUZK
|
|
4
|
+
module REST
|
|
5
|
+
module Resources
|
|
6
|
+
# Data model for ownership records. The ČÚZK REST API does not have a standalone
|
|
7
|
+
# ownership endpoint. Ownership data is embedded within other resources (parcels,
|
|
8
|
+
# buildings, units) via the LV (list vlastnictví) number.
|
|
9
|
+
class Ownership < Resource
|
|
10
|
+
def id
|
|
11
|
+
attributes['id']
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def owner_name
|
|
15
|
+
if masked?
|
|
16
|
+
attributes['jmenoMaskovane'] || '[chráněno]'
|
|
17
|
+
else
|
|
18
|
+
attributes['jmeno']
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def owner_type
|
|
23
|
+
attributes['typVlastnika']
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def owner_type_code
|
|
27
|
+
attributes['kodTypuVlastnika']
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def ownership_type
|
|
31
|
+
attributes['typVlastnictvi']
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def ownership_type_code
|
|
35
|
+
attributes['kodTypuVlastnictvi']
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def share_numerator
|
|
39
|
+
attributes['podilCitatel']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def share_denominator
|
|
43
|
+
attributes['podilJmenovatel']
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def ownership_share
|
|
47
|
+
return nil unless share_numerator && share_denominator
|
|
48
|
+
|
|
49
|
+
"#{share_numerator}/#{share_denominator}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def share_percentage
|
|
53
|
+
return nil unless share_numerator && share_denominator&.to_f&.positive?
|
|
54
|
+
|
|
55
|
+
(share_numerator.to_f / share_denominator * 100).round(2)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def property_id
|
|
59
|
+
attributes['idNemovitosti']
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def property_type
|
|
63
|
+
attributes['typNemovitosti']
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def lv_number
|
|
67
|
+
attributes['cisloLv']
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def cadastral_unit_code
|
|
71
|
+
attributes['kodKatastralnihoUzemi']
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def legal_basis
|
|
75
|
+
attributes['pravniDuvod']
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def acquisition_date
|
|
79
|
+
attributes['datumNabiti']
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def restrictions
|
|
83
|
+
attributes['omezeni'] || []
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def encumbrances
|
|
87
|
+
attributes['vecnaBremena'] || []
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def masked?
|
|
93
|
+
CUZK::REST.configuration.mask_personal_data
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def summary_attributes
|
|
97
|
+
"id=#{id} owner=#{owner_name} share=#{ownership_share} lv=#{lv_number}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|