atlas_engine 0.1.2 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +497 -33
- data/app/countries/atlas_engine/be/country_profile.yml +2 -0
- data/app/countries/atlas_engine/be/validation_transcriber/address_parser.rb +84 -0
- data/app/countries/atlas_engine/bm/address_importer/corrections/open_address/city_alias_corrector.rb +12 -11
- data/app/countries/atlas_engine/bm/synonyms.yml +6 -0
- data/app/countries/atlas_engine/ch/country_profile.yml +2 -0
- data/app/countries/atlas_engine/ch/locales/de/country_profile.yml +0 -1
- data/app/countries/atlas_engine/ch/locales/fr/country_profile.yml +3 -0
- data/app/countries/atlas_engine/ch/locales/fr/validation_transcriber/address_parser.rb +29 -0
- data/app/countries/atlas_engine/cz/address_validation/es/query_builder.rb +43 -0
- data/app/countries/atlas_engine/cz/country_profile.yml +2 -1
- data/app/countries/atlas_engine/cz/validation_transcriber/address_parser.rb +26 -0
- data/app/countries/atlas_engine/it/address_importer/open_address/mapper.rb +1 -1
- data/app/countries/atlas_engine/it/country_profile.yml +1 -0
- data/app/countries/atlas_engine/sa/country_profile.yml +4 -1
- data/app/countries/atlas_engine/us/country_profile.yml +0 -2
- data/app/lib/atlas_engine/validation_transcriber/address_parsings.rb +1 -1
- data/app/lib/atlas_engine/validation_transcriber/formatter.rb +2 -2
- data/app/models/atlas_engine/address_validation/datastore_base.rb +3 -0
- data/app/models/atlas_engine/address_validation/es/datastore.rb +11 -6
- data/app/models/atlas_engine/address_validation/es/query_builder.rb +40 -29
- data/app/models/atlas_engine/address_validation/es/validators/full_address.rb +1 -1
- data/app/models/atlas_engine/address_validation/log_emitter.rb +1 -0
- data/app/models/atlas_engine/address_validation/normalizer.rb +0 -9
- data/app/models/atlas_engine/address_validation/validators/full_address/address_comparison.rb +7 -23
- data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result.rb +42 -16
- data/app/models/atlas_engine/address_validation/validators/full_address/comparison_helper.rb +109 -109
- data/app/models/atlas_engine/address_validation/validators/full_address/{components_to_validate.rb → relevant_components.rb} +26 -18
- data/app/models/atlas_engine/country_profile_validation_subset.rb +5 -0
- data/app/tasks/maintenance/atlas_engine/elasticsearch_index_create_task.rb +1 -1
- data/db/data/country_profiles/default.yml +0 -2
- data/lib/atlas_engine/version.rb +1 -1
- metadata +11 -5
data/app/models/atlas_engine/address_validation/validators/full_address/comparison_helper.rb
CHANGED
@@ -6,128 +6,128 @@ module AtlasEngine
|
|
6
6
|
module Validators
|
7
7
|
module FullAddress
|
8
8
|
class ComparisonHelper
|
9
|
-
|
10
|
-
extend T::Sig
|
11
|
-
|
12
|
-
sig do
|
13
|
-
params(
|
14
|
-
datastore: DatastoreBase,
|
15
|
-
candidate: Candidate,
|
16
|
-
).returns(T.nilable(Token::Sequence::Comparison))
|
17
|
-
end
|
18
|
-
def street_comparison(datastore:, candidate:)
|
19
|
-
street_sequences = datastore.fetch_street_sequences
|
20
|
-
candidate_sequences = T.must(candidate.component(:street)).sequences
|
21
|
-
|
22
|
-
street_sequences.map do |street_sequence|
|
23
|
-
best_comparison(
|
24
|
-
street_sequence,
|
25
|
-
candidate_sequences,
|
26
|
-
)
|
27
|
-
end.min
|
28
|
-
end
|
9
|
+
extend T::Sig
|
29
10
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
def city_comparison(datastore:, candidate:)
|
37
|
-
best_comparison(
|
38
|
-
datastore.fetch_city_sequence,
|
39
|
-
T.must(candidate.component(:city)).sequences,
|
40
|
-
)
|
41
|
-
end
|
11
|
+
sig { params(address: AbstractAddress, candidate: Candidate, datastore: DatastoreBase).void }
|
12
|
+
def initialize(address:, candidate:, datastore:)
|
13
|
+
@address = address
|
14
|
+
@datastore = datastore
|
15
|
+
@candidate = candidate
|
16
|
+
end
|
42
17
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
candidate: Candidate,
|
47
|
-
).returns(T.nilable(Token::Sequence::Comparison))
|
48
|
-
end
|
49
|
-
def province_code_comparison(address:, candidate:)
|
50
|
-
normalized_session_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
|
51
|
-
country_code: address.country_code,
|
52
|
-
province_code: address.province_code,
|
53
|
-
)
|
54
|
-
normalized_candidate_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
|
55
|
-
country_code: T.must(candidate.component(:country_code)).value,
|
56
|
-
province_code: T.must(candidate.component(:province_code)).value,
|
57
|
-
)
|
18
|
+
sig { returns(T.nilable(Token::Sequence::Comparison)) }
|
19
|
+
def street_comparison
|
20
|
+
return @street_comparison if defined?(@street_comparison)
|
58
21
|
|
59
|
-
|
60
|
-
|
61
|
-
[Token::Sequence.from_string(normalized_candidate_province_code)],
|
62
|
-
)
|
63
|
-
end
|
22
|
+
street_sequences = datastore.fetch_street_sequences
|
23
|
+
candidate_sequences = T.must(candidate.component(:street)).sequences
|
64
24
|
|
65
|
-
|
66
|
-
params(
|
67
|
-
address: AbstractAddress,
|
68
|
-
candidate: Candidate,
|
69
|
-
).returns(T.nilable(Token::Sequence::Comparison))
|
70
|
-
end
|
71
|
-
def zip_comparison(address:, candidate:)
|
72
|
-
candidate.component(:zip)&.value = PostalCodeMatcher.new(
|
73
|
-
T.must(address.country_code),
|
74
|
-
T.must(address.zip),
|
75
|
-
candidate.component(:zip)&.value,
|
76
|
-
).truncate
|
77
|
-
|
78
|
-
normalized_zip = ValidationTranscriber::ZipNormalizer.normalize(
|
79
|
-
country_code: address.country_code, zip: address.zip,
|
80
|
-
)
|
81
|
-
zip_sequence = Token::Sequence.from_string(normalized_zip)
|
25
|
+
@street_comparison = street_sequences.map do |street_sequence|
|
82
26
|
best_comparison(
|
83
|
-
|
84
|
-
|
27
|
+
street_sequence,
|
28
|
+
candidate_sequences,
|
85
29
|
)
|
86
|
-
end
|
30
|
+
end.min
|
31
|
+
end
|
87
32
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
candidate: Candidate,
|
92
|
-
).returns(NumberComparison)
|
93
|
-
end
|
94
|
-
def building_comparison(datastore:, candidate:)
|
95
|
-
NumberComparison.new(
|
96
|
-
numbers: datastore.parsings.potential_building_numbers,
|
97
|
-
candidate_ranges: building_ranges_from_candidate(candidate),
|
98
|
-
)
|
99
|
-
end
|
33
|
+
sig { returns(T.nilable(Token::Sequence::Comparison)) }
|
34
|
+
def city_comparison
|
35
|
+
return @city_comparison if defined?(@city_comparison)
|
100
36
|
|
101
|
-
|
37
|
+
@city_comparison = best_comparison(
|
38
|
+
datastore.fetch_city_sequence,
|
39
|
+
T.must(candidate.component(:city)).sequences,
|
40
|
+
)
|
41
|
+
end
|
102
42
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
43
|
+
sig { returns(T.nilable(Token::Sequence::Comparison)) }
|
44
|
+
def province_code_comparison
|
45
|
+
return @province_code_comparison if defined?(@province_code_comparison)
|
46
|
+
|
47
|
+
normalized_session_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
|
48
|
+
country_code: address.country_code,
|
49
|
+
province_code: address.province_code,
|
50
|
+
)
|
51
|
+
normalized_candidate_province_code = ValidationTranscriber::ProvinceCodeNormalizer.normalize(
|
52
|
+
country_code: T.must(candidate.component(:country_code)).value,
|
53
|
+
province_code: T.must(candidate.component(:province_code)).value,
|
54
|
+
)
|
55
|
+
|
56
|
+
@province_code_comparison = best_comparison(
|
57
|
+
Token::Sequence.from_string(normalized_session_province_code),
|
58
|
+
[Token::Sequence.from_string(normalized_candidate_province_code)],
|
59
|
+
)
|
60
|
+
end
|
61
|
+
|
62
|
+
sig { returns(T.nilable(Token::Sequence::Comparison)) }
|
63
|
+
def zip_comparison
|
64
|
+
return @zip_comparison if defined?(@zip_comparison)
|
121
65
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
66
|
+
candidate.component(:zip)&.value = PostalCodeMatcher.new(
|
67
|
+
T.must(address.country_code),
|
68
|
+
T.must(address.zip),
|
69
|
+
candidate.component(:zip)&.value,
|
70
|
+
).truncate
|
126
71
|
|
127
|
-
|
128
|
-
|
72
|
+
normalized_zip = ValidationTranscriber::ZipNormalizer.normalize(
|
73
|
+
country_code: address.country_code, zip: address.zip,
|
74
|
+
)
|
75
|
+
zip_sequence = Token::Sequence.from_string(normalized_zip)
|
76
|
+
@zip_comparison = best_comparison(
|
77
|
+
zip_sequence,
|
78
|
+
T.must(candidate.component(:zip)).sequences,
|
79
|
+
)
|
80
|
+
end
|
81
|
+
|
82
|
+
sig { returns(NumberComparison) }
|
83
|
+
def building_comparison
|
84
|
+
@building_comparison ||= NumberComparison.new(
|
85
|
+
numbers: datastore.parsings.potential_building_numbers,
|
86
|
+
candidate_ranges: building_ranges_from_candidate(candidate),
|
87
|
+
)
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
sig { returns(AbstractAddress) }
|
93
|
+
attr_reader :address
|
94
|
+
|
95
|
+
sig { returns(DatastoreBase) }
|
96
|
+
attr_reader :datastore
|
97
|
+
|
98
|
+
sig { returns(Candidate) }
|
99
|
+
attr_reader :candidate
|
100
|
+
|
101
|
+
sig do
|
102
|
+
params(
|
103
|
+
sequence: Token::Sequence,
|
104
|
+
component_sequences: T::Array[Token::Sequence],
|
105
|
+
).returns(T.nilable(Token::Sequence::Comparison))
|
106
|
+
end
|
107
|
+
def best_comparison(
|
108
|
+
sequence,
|
109
|
+
component_sequences
|
110
|
+
)
|
111
|
+
component_sequences.map do |component_sequence|
|
112
|
+
Token::Sequence::Comparator.new(
|
113
|
+
left_sequence: sequence,
|
114
|
+
right_sequence: component_sequence,
|
115
|
+
).compare
|
116
|
+
end.min_by.with_index do |comparison, index|
|
117
|
+
# ruby's `min` and `sort` methods are not stable
|
118
|
+
# so we need to prefer the leftmost comparison when two comparisons are equivalent
|
119
|
+
[comparison, index]
|
129
120
|
end
|
130
121
|
end
|
122
|
+
|
123
|
+
sig { params(candidate: Candidate).returns(T::Array[AddressNumberRange]) }
|
124
|
+
def building_ranges_from_candidate(candidate)
|
125
|
+
building_and_unit_ranges = candidate.component(:building_and_unit_ranges)&.value
|
126
|
+
return [] if building_and_unit_ranges.blank?
|
127
|
+
|
128
|
+
building_ranges = JSON.parse(building_and_unit_ranges).keys
|
129
|
+
building_ranges.map { |building_range| AddressNumberRange.new(range_string: building_range) }
|
130
|
+
end
|
131
131
|
end
|
132
132
|
end
|
133
133
|
end
|
@@ -5,7 +5,7 @@ module AtlasEngine
|
|
5
5
|
module AddressValidation
|
6
6
|
module Validators
|
7
7
|
module FullAddress
|
8
|
-
class
|
8
|
+
class RelevantComponents
|
9
9
|
extend T::Sig
|
10
10
|
|
11
11
|
attr_reader :session, :candidate, :street_comparison
|
@@ -31,29 +31,35 @@ module AtlasEngine
|
|
31
31
|
end
|
32
32
|
|
33
33
|
sig { returns(T::Array[Symbol]) }
|
34
|
-
def
|
34
|
+
def components_to_validate
|
35
35
|
supported_components = ALL_SUPPORTED_COMPONENTS.dup - unsupported_components_for_country
|
36
36
|
supported_components.delete(:street) if exclude_street_validation?
|
37
37
|
supported_components
|
38
38
|
end
|
39
39
|
|
40
|
+
sig { returns(T::Array[Symbol]) }
|
41
|
+
def components_to_compare
|
42
|
+
ALL_SUPPORTED_COMPONENTS.dup - unsupported_components_for_country
|
43
|
+
end
|
44
|
+
|
40
45
|
private
|
41
46
|
|
42
47
|
sig { returns(T::Boolean) }
|
43
48
|
def exclude_street_validation?
|
44
|
-
return
|
49
|
+
return @exclude_street_validation if defined?(@exclude_street_validation)
|
45
50
|
|
46
|
-
if
|
51
|
+
@exclude_street_validation = if session.matching_strategy !=
|
52
|
+
AddressValidation::MatchingStrategies::EsStreet
|
53
|
+
true
|
54
|
+
elsif street_comparison.blank?
|
47
55
|
emit_excluded_validation("street", "not_found")
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
if exclusions("street").any? { |exclusion| exclusion.apply?(session, candidate) }
|
56
|
+
true
|
57
|
+
elsif exclusions("street").any? { |exclusion| exclusion.apply?(session, candidate) }
|
52
58
|
emit_excluded_validation("street", "excluded")
|
53
|
-
|
59
|
+
true
|
60
|
+
else
|
61
|
+
false
|
54
62
|
end
|
55
|
-
|
56
|
-
false
|
57
63
|
end
|
58
64
|
|
59
65
|
sig { params(component: String).returns(T::Array[T.class_of(Exclusions::ExclusionBase)]) }
|
@@ -73,13 +79,15 @@ module AtlasEngine
|
|
73
79
|
|
74
80
|
sig { returns(T::Array[Symbol]) }
|
75
81
|
def unsupported_components_for_country
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
82
|
+
@unsupported_components_for_country ||= begin
|
83
|
+
unsupported_components = []
|
84
|
+
country = Worldwide.region(code: session.address.country_code)
|
85
|
+
unsupported_components << :province_code if country.province_optional?
|
86
|
+
unsupported_components << :province_code if country.hide_provinces_from_addresses
|
87
|
+
unsupported_components << :city unless country.city_required?
|
88
|
+
unsupported_components << :zip unless country.zip_required? && !country.zip_autofill_enabled
|
89
|
+
unsupported_components.uniq
|
90
|
+
end
|
83
91
|
end
|
84
92
|
end
|
85
93
|
end
|
@@ -36,7 +36,7 @@ module Maintenance
|
|
36
36
|
record_count = ::AtlasEngine::PostAddress.where(address_conditions).size
|
37
37
|
raise "No records to process for country code: #{country_code}" if record_count.zero?
|
38
38
|
|
39
|
-
repository.create_next_index(ensure_clean: true)
|
39
|
+
repository.create_next_index(ensure_clean: true, raise_errors: true)
|
40
40
|
|
41
41
|
::AtlasEngine::PostAddress.where(address_conditions).in_batches(of: batch_size)
|
42
42
|
end
|
data/lib/atlas_engine/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: atlas_engine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Shopify
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-01-
|
11
|
+
date: 2024-01-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: annex_29
|
@@ -212,14 +212,14 @@ dependencies:
|
|
212
212
|
requirements:
|
213
213
|
- - ">="
|
214
214
|
- !ruby/object:Gem::Version
|
215
|
-
version:
|
215
|
+
version: 0.6.4
|
216
216
|
type: :runtime
|
217
217
|
prerelease: false
|
218
218
|
version_requirements: !ruby/object:Gem::Requirement
|
219
219
|
requirements:
|
220
220
|
- - ">="
|
221
221
|
- !ruby/object:Gem::Version
|
222
|
-
version:
|
222
|
+
version: 0.6.4
|
223
223
|
description: The Atlas Engine is a rails engine that provides a GraphQL API for address
|
224
224
|
validation.
|
225
225
|
email: developers@shopify.com
|
@@ -250,9 +250,11 @@ files:
|
|
250
250
|
- app/countries/atlas_engine/au/synonyms.yml
|
251
251
|
- app/countries/atlas_engine/au/validation_transcriber/address_parser.rb
|
252
252
|
- app/countries/atlas_engine/be/country_profile.yml
|
253
|
+
- app/countries/atlas_engine/be/validation_transcriber/address_parser.rb
|
253
254
|
- app/countries/atlas_engine/bm/address_importer/corrections/open_address/city_alias_corrector.rb
|
254
255
|
- app/countries/atlas_engine/bm/address_importer/open_address/mapper.rb
|
255
256
|
- app/countries/atlas_engine/bm/country_profile.yml
|
257
|
+
- app/countries/atlas_engine/bm/synonyms.yml
|
256
258
|
- app/countries/atlas_engine/br/country_profile.yml
|
257
259
|
- app/countries/atlas_engine/ca/country_profile.yml
|
258
260
|
- app/countries/atlas_engine/ca/synonyms.yml
|
@@ -263,8 +265,12 @@ files:
|
|
263
265
|
- app/countries/atlas_engine/ch/locales/de/country_profile.yml
|
264
266
|
- app/countries/atlas_engine/ch/locales/de/index_configuration.yml
|
265
267
|
- app/countries/atlas_engine/ch/locales/de/synonyms.yml
|
268
|
+
- app/countries/atlas_engine/ch/locales/fr/country_profile.yml
|
266
269
|
- app/countries/atlas_engine/ch/locales/fr/synonyms.yml
|
270
|
+
- app/countries/atlas_engine/ch/locales/fr/validation_transcriber/address_parser.rb
|
271
|
+
- app/countries/atlas_engine/cz/address_validation/es/query_builder.rb
|
267
272
|
- app/countries/atlas_engine/cz/country_profile.yml
|
273
|
+
- app/countries/atlas_engine/cz/validation_transcriber/address_parser.rb
|
268
274
|
- app/countries/atlas_engine/de/country_profile.yml
|
269
275
|
- app/countries/atlas_engine/de/index_configuration.yml
|
270
276
|
- app/countries/atlas_engine/de/synonyms.yml
|
@@ -437,7 +443,6 @@ files:
|
|
437
443
|
- app/models/atlas_engine/address_validation/validators/full_address/candidate_result.rb
|
438
444
|
- app/models/atlas_engine/address_validation/validators/full_address/candidate_result_base.rb
|
439
445
|
- app/models/atlas_engine/address_validation/validators/full_address/comparison_helper.rb
|
440
|
-
- app/models/atlas_engine/address_validation/validators/full_address/components_to_validate.rb
|
441
446
|
- app/models/atlas_engine/address_validation/validators/full_address/concern_builder.rb
|
442
447
|
- app/models/atlas_engine/address_validation/validators/full_address/exclusions/exclusion_base.rb
|
443
448
|
- app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_concern_builder.rb
|
@@ -446,6 +451,7 @@ files:
|
|
446
451
|
- app/models/atlas_engine/address_validation/validators/full_address/no_candidate_result.rb
|
447
452
|
- app/models/atlas_engine/address_validation/validators/full_address/number_comparison.rb
|
448
453
|
- app/models/atlas_engine/address_validation/validators/full_address/postal_code_matcher.rb
|
454
|
+
- app/models/atlas_engine/address_validation/validators/full_address/relevant_components.rb
|
449
455
|
- app/models/atlas_engine/address_validation/validators/full_address/result_updater.rb
|
450
456
|
- app/models/atlas_engine/address_validation/validators/full_address/suggestion_builder.rb
|
451
457
|
- app/models/atlas_engine/address_validation/validators/full_address/unknown_address_concern.rb
|