atlas_engine 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.
Files changed (298) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +123 -0
  3. data/Rakefile +20 -0
  4. data/app/assets/config/atlas_engine_manifest.js +3 -0
  5. data/app/assets/stylesheets/atlas_engine/application.css +15 -0
  6. data/app/concerns/atlas_engine/handles_blob.rb +26 -0
  7. data/app/concerns/atlas_engine/handles_interruption.rb +22 -0
  8. data/app/controllers/atlas_engine/application_controller.rb +7 -0
  9. data/app/controllers/atlas_engine/connectivity_controller.rb +21 -0
  10. data/app/controllers/atlas_engine/country_imports_controller.rb +73 -0
  11. data/app/controllers/atlas_engine/graphql_controller.rb +59 -0
  12. data/app/countries/atlas_engine/ar/country_profile.yml +9 -0
  13. data/app/countries/atlas_engine/at/address_importer/corrections/open_address/city_corrector.rb +23 -0
  14. data/app/countries/atlas_engine/at/country_profile.yml +24 -0
  15. data/app/countries/atlas_engine/at/index_configuration.yml +63 -0
  16. data/app/countries/atlas_engine/at/synonyms.yml +6 -0
  17. data/app/countries/atlas_engine/at/validation_transcriber/address_parser.rb +58 -0
  18. data/app/countries/atlas_engine/au/address_importer/open_address/filter.rb +26 -0
  19. data/app/countries/atlas_engine/au/address_importer/open_address/mapper.rb +41 -0
  20. data/app/countries/atlas_engine/au/country_profile.yml +13 -0
  21. data/app/countries/atlas_engine/au/synonyms.yml +209 -0
  22. data/app/countries/atlas_engine/au/validation_transcriber/address_parser.rb +121 -0
  23. data/app/countries/atlas_engine/be/country_profile.yml +12 -0
  24. data/app/countries/atlas_engine/bm/address_importer/corrections/open_address/city_alias_corrector.rb +38 -0
  25. data/app/countries/atlas_engine/bm/address_importer/open_address/mapper.rb +40 -0
  26. data/app/countries/atlas_engine/bm/country_profile.yml +12 -0
  27. data/app/countries/atlas_engine/br/country_profile.yml +4 -0
  28. data/app/countries/atlas_engine/ca/country_profile.yml +7 -0
  29. data/app/countries/atlas_engine/ca/synonyms.yml +1615 -0
  30. data/app/countries/atlas_engine/ch/address_importer/corrections/open_address/city_corrector.rb +29 -0
  31. data/app/countries/atlas_engine/ch/address_importer/corrections/open_address/locale_corrector.rb +74 -0
  32. data/app/countries/atlas_engine/ch/address_importer/open_address/mapper.rb +40 -0
  33. data/app/countries/atlas_engine/ch/country_profile.yml +15 -0
  34. data/app/countries/atlas_engine/ch/locales/de/country_profile.yml +15 -0
  35. data/app/countries/atlas_engine/ch/locales/de/index_configuration.yml +63 -0
  36. data/app/countries/atlas_engine/ch/locales/de/synonyms.yml +7 -0
  37. data/app/countries/atlas_engine/ch/locales/fr/synonyms.yml +21 -0
  38. data/app/countries/atlas_engine/cz/country_profile.yml +6 -0
  39. data/app/countries/atlas_engine/de/country_profile.yml +19 -0
  40. data/app/countries/atlas_engine/de/index_configuration.yml +64 -0
  41. data/app/countries/atlas_engine/de/synonyms.yml +2 -0
  42. data/app/countries/atlas_engine/de/validation_transcriber/address_parser.rb +19 -0
  43. data/app/countries/atlas_engine/dk/country_profile.yml +6 -0
  44. data/app/countries/atlas_engine/dk/synonyms.yml +3 -0
  45. data/app/countries/atlas_engine/dk/validation_transcriber/address_parser.rb +21 -0
  46. data/app/countries/atlas_engine/fo/country_profile.yml +5 -0
  47. data/app/countries/atlas_engine/fr/address_importer/corrections/open_address/city_corrector.rb +28 -0
  48. data/app/countries/atlas_engine/fr/country_profile.yml +13 -0
  49. data/app/countries/atlas_engine/fr/synonyms.yml +21 -0
  50. data/app/countries/atlas_engine/fr/validation_transcriber/address_parser.rb +34 -0
  51. data/app/countries/atlas_engine/gb/address_validation/es/query_builder.rb +98 -0
  52. data/app/countries/atlas_engine/gb/country_profile.yml +10 -0
  53. data/app/countries/atlas_engine/gb/validation_transcriber/full_address_parser.rb +164 -0
  54. data/app/countries/atlas_engine/gb/validation_transcriber/parsed_address.rb +120 -0
  55. data/app/countries/atlas_engine/gg/address_validation/validators/full_address/restrictions/unsupported_city.rb +39 -0
  56. data/app/countries/atlas_engine/gg/country_profile.yml +7 -0
  57. data/app/countries/atlas_engine/ie/country_profile.yml +3 -0
  58. data/app/countries/atlas_engine/it/address_importer/corrections/open_address/city_corrector.rb +27 -0
  59. data/app/countries/atlas_engine/it/address_importer/corrections/open_address/province_corrector.rb +29 -0
  60. data/app/countries/atlas_engine/it/address_importer/open_address/mapper.rb +42 -0
  61. data/app/countries/atlas_engine/it/country_profile.yml +11 -0
  62. data/app/countries/atlas_engine/jp/address_validation/es/data_mapper.rb +63 -0
  63. data/app/countries/atlas_engine/jp/country_profile.yml +6 -0
  64. data/app/countries/atlas_engine/kr/address_importer/open_address/mapper.rb +41 -0
  65. data/app/countries/atlas_engine/kr/country_profile.yml +11 -0
  66. data/app/countries/atlas_engine/li/address_importer/corrections/open_address/city_corrector.rb +25 -0
  67. data/app/countries/atlas_engine/li/country_profile.yml +21 -0
  68. data/app/countries/atlas_engine/li/index_configuration.yml +63 -0
  69. data/app/countries/atlas_engine/li/synonyms.yml +6 -0
  70. data/app/countries/atlas_engine/lt/country_profile.yml +6 -0
  71. data/app/countries/atlas_engine/lt/synonyms.yml +7 -0
  72. data/app/countries/atlas_engine/lt/validation_transcriber/address_parser.rb +24 -0
  73. data/app/countries/atlas_engine/lu/address_importer/corrections/open_address/locale_corrector.rb +54 -0
  74. data/app/countries/atlas_engine/lu/country_profile.yml +12 -0
  75. data/app/countries/atlas_engine/nl/address_importer/corrections/open_address/city_corrector.rb +25 -0
  76. data/app/countries/atlas_engine/nl/country_profile.yml +18 -0
  77. data/app/countries/atlas_engine/nl/index_configuration.yml +52 -0
  78. data/app/countries/atlas_engine/nl/synonyms.yml +92 -0
  79. data/app/countries/atlas_engine/nl/validation_transcriber/address_parser.rb +85 -0
  80. data/app/countries/atlas_engine/no/country_profile.yml +5 -0
  81. data/app/countries/atlas_engine/nz/country_profile.yml +3 -0
  82. data/app/countries/atlas_engine/pl/country_profile.yml +5 -0
  83. data/app/countries/atlas_engine/pl/validation_transcriber/address_parser.rb +19 -0
  84. data/app/countries/atlas_engine/pt/address_importer/corrections/open_address/city_corrector.rb +32 -0
  85. data/app/countries/atlas_engine/pt/address_importer/open_address/mapper.rb +39 -0
  86. data/app/countries/atlas_engine/pt/country_profile.yml +10 -0
  87. data/app/countries/atlas_engine/pt/synonyms.yml +7 -0
  88. data/app/countries/atlas_engine/sa/country_profile.yml +10 -0
  89. data/app/countries/atlas_engine/se/country_profile.yml +5 -0
  90. data/app/countries/atlas_engine/tt/address_importer/open_address/mapper.rb +38 -0
  91. data/app/countries/atlas_engine/tt/country_profile.yml +7 -0
  92. data/app/countries/atlas_engine/us/country_profile.yml +12 -0
  93. data/app/countries/atlas_engine/us/synonyms.yml +350 -0
  94. data/app/graphql/atlas_engine/errors/locale_unsupported_error.rb +17 -0
  95. data/app/graphql/atlas_engine/schema.graphql +1293 -0
  96. data/app/graphql/atlas_engine/schema.rb +23 -0
  97. data/app/graphql/atlas_engine/types/address_validation/address_input.rb +51 -0
  98. data/app/graphql/atlas_engine/types/address_validation/concern_type.rb +20 -0
  99. data/app/graphql/atlas_engine/types/address_validation/enums/concern_enum.rb +15 -0
  100. data/app/graphql/atlas_engine/types/address_validation/field_type.rb +15 -0
  101. data/app/graphql/atlas_engine/types/address_validation/suggestion_type.rb +21 -0
  102. data/app/graphql/atlas_engine/types/base_argument.rb +9 -0
  103. data/app/graphql/atlas_engine/types/base_enum.rb +9 -0
  104. data/app/graphql/atlas_engine/types/base_field.rb +10 -0
  105. data/app/graphql/atlas_engine/types/base_input_object.rb +9 -0
  106. data/app/graphql/atlas_engine/types/base_interface.rb +10 -0
  107. data/app/graphql/atlas_engine/types/base_object.rb +9 -0
  108. data/app/graphql/atlas_engine/types/base_scalar.rb +9 -0
  109. data/app/graphql/atlas_engine/types/base_union.rb +9 -0
  110. data/app/graphql/atlas_engine/types/matching_strategy_type.rb +12 -0
  111. data/app/graphql/atlas_engine/types/mutation_type.rb +9 -0
  112. data/app/graphql/atlas_engine/types/query_type.rb +61 -0
  113. data/app/graphql/atlas_engine/types/validation_supported_country.rb +12 -0
  114. data/app/graphql/atlas_engine/types/validation_type.rb +22 -0
  115. data/app/helpers/atlas_engine/address_importer/import_log_helper.rb +66 -0
  116. data/app/helpers/atlas_engine/application_helper.rb +7 -0
  117. data/app/helpers/atlas_engine/locale_format_helper.rb +40 -0
  118. data/app/helpers/atlas_engine/log_base.rb +32 -0
  119. data/app/helpers/atlas_engine/log_helper.rb +24 -0
  120. data/app/helpers/atlas_engine/metrics_helper.rb +25 -0
  121. data/app/jobs/atlas_engine/address_importer/clear_records_job.rb +39 -0
  122. data/app/jobs/atlas_engine/address_importer/open_address/geo_json_import_job.rb +212 -0
  123. data/app/jobs/atlas_engine/address_importer/open_address/geo_json_import_launcher_job.rb +67 -0
  124. data/app/jobs/atlas_engine/address_importer/open_address/prepares_geo_json_file.rb +41 -0
  125. data/app/jobs/atlas_engine/address_importer/resumable_import_job.rb +49 -0
  126. data/app/jobs/atlas_engine/address_importer/street_backfill_job.rb +63 -0
  127. data/app/jobs/atlas_engine/application_job.rb +10 -0
  128. data/app/jobs/atlas_engine/concerns/address_importer/handles_errors.rb +43 -0
  129. data/app/lib/atlas_engine/concern_formatter.rb +40 -0
  130. data/app/lib/atlas_engine/restrictions/base.rb +20 -0
  131. data/app/lib/atlas_engine/restrictions/unsupported_script.rb +31 -0
  132. data/app/lib/atlas_engine/validation_transcriber/address_parser_base.rb +201 -0
  133. data/app/lib/atlas_engine/validation_transcriber/address_parser_factory.rb +27 -0
  134. data/app/lib/atlas_engine/validation_transcriber/address_parser_north_america.rb +39 -0
  135. data/app/lib/atlas_engine/validation_transcriber/address_parser_oceanic.rb +17 -0
  136. data/app/lib/atlas_engine/validation_transcriber/address_parser_preprocessor.rb +132 -0
  137. data/app/lib/atlas_engine/validation_transcriber/address_parsing_helper.rb +38 -0
  138. data/app/lib/atlas_engine/validation_transcriber/address_parsings.rb +54 -0
  139. data/app/lib/atlas_engine/validation_transcriber/constants.rb +50 -0
  140. data/app/lib/atlas_engine/validation_transcriber/english_street_parser.rb +59 -0
  141. data/app/lib/atlas_engine/validation_transcriber/formatter.rb +46 -0
  142. data/app/lib/atlas_engine/validation_transcriber/french_street_parser.rb +50 -0
  143. data/app/lib/atlas_engine/validation_transcriber/province_code_normalizer.rb +45 -0
  144. data/app/lib/atlas_engine/validation_transcriber/street_parser.rb +18 -0
  145. data/app/lib/atlas_engine/validation_transcriber/zip_normalizer.rb +23 -0
  146. data/app/mailers/atlas_engine/application_mailer.rb +9 -0
  147. data/app/models/atlas_engine/address_importer/corrections/corrector.rb +33 -0
  148. data/app/models/atlas_engine/address_importer/import_events_notifier/base.rb +35 -0
  149. data/app/models/atlas_engine/address_importer/import_events_notifier/notifier.rb +26 -0
  150. data/app/models/atlas_engine/address_importer/open_address/default_mapper.rb +46 -0
  151. data/app/models/atlas_engine/address_importer/open_address/feature_helper.rb +110 -0
  152. data/app/models/atlas_engine/address_importer/open_address/filter.rb +17 -0
  153. data/app/models/atlas_engine/address_importer/open_address/loader.rb +27 -0
  154. data/app/models/atlas_engine/address_importer/open_address/transformer.rb +39 -0
  155. data/app/models/atlas_engine/address_importer/open_address.rb +10 -0
  156. data/app/models/atlas_engine/address_importer/validation/base_validator.rb +86 -0
  157. data/app/models/atlas_engine/address_importer/validation/default_validator.rb +27 -0
  158. data/app/models/atlas_engine/address_importer/validation/field_validations/city.rb +47 -0
  159. data/app/models/atlas_engine/address_importer/validation/field_validations/interface.rb +29 -0
  160. data/app/models/atlas_engine/address_importer/validation/field_validations/province.rb +73 -0
  161. data/app/models/atlas_engine/address_importer/validation/field_validations/zip.rb +84 -0
  162. data/app/models/atlas_engine/address_importer/validation/validator.rb +17 -0
  163. data/app/models/atlas_engine/address_importer/validation/wrapper.rb +70 -0
  164. data/app/models/atlas_engine/address_number.rb +36 -0
  165. data/app/models/atlas_engine/address_number_range.rb +200 -0
  166. data/app/models/atlas_engine/address_validation/abstract_address.rb +49 -0
  167. data/app/models/atlas_engine/address_validation/address.rb +47 -0
  168. data/app/models/atlas_engine/address_validation/candidate.rb +109 -0
  169. data/app/models/atlas_engine/address_validation/candidate_tuple.rb +15 -0
  170. data/app/models/atlas_engine/address_validation/concern.rb +74 -0
  171. data/app/models/atlas_engine/address_validation/concern_producer.rb +19 -0
  172. data/app/models/atlas_engine/address_validation/concern_queue.rb +20 -0
  173. data/app/models/atlas_engine/address_validation/concern_record.rb +122 -0
  174. data/app/models/atlas_engine/address_validation/datastore_base.rb +27 -0
  175. data/app/models/atlas_engine/address_validation/errors.rb +13 -0
  176. data/app/models/atlas_engine/address_validation/es/candidate_selector.rb +70 -0
  177. data/app/models/atlas_engine/address_validation/es/data_mappers/decompounding_data_mapper.rb +39 -0
  178. data/app/models/atlas_engine/address_validation/es/data_mappers/default_data_mapper.rb +110 -0
  179. data/app/models/atlas_engine/address_validation/es/datastore.rb +229 -0
  180. data/app/models/atlas_engine/address_validation/es/default_query_builder.rb +30 -0
  181. data/app/models/atlas_engine/address_validation/es/query_builder.rb +160 -0
  182. data/app/models/atlas_engine/address_validation/es/term_vectors.rb +78 -0
  183. data/app/models/atlas_engine/address_validation/es/validators/full_address.rb +123 -0
  184. data/app/models/atlas_engine/address_validation/es/validators/full_address_street.rb +18 -0
  185. data/app/models/atlas_engine/address_validation/es/validators/restriction_evaluator.rb +37 -0
  186. data/app/models/atlas_engine/address_validation/field.rb +30 -0
  187. data/app/models/atlas_engine/address_validation/full_address_validator_base.rb +27 -0
  188. data/app/models/atlas_engine/address_validation/log_emitter.rb +66 -0
  189. data/app/models/atlas_engine/address_validation/matching_strategies.rb +16 -0
  190. data/app/models/atlas_engine/address_validation/normalizer.rb +38 -0
  191. data/app/models/atlas_engine/address_validation/predicate_pipeline.rb +80 -0
  192. data/app/models/atlas_engine/address_validation/request.rb +12 -0
  193. data/app/models/atlas_engine/address_validation/result.rb +154 -0
  194. data/app/models/atlas_engine/address_validation/runs_validation.rb +16 -0
  195. data/app/models/atlas_engine/address_validation/session.rb +47 -0
  196. data/app/models/atlas_engine/address_validation/statsd_emitter.rb +72 -0
  197. data/app/models/atlas_engine/address_validation/strategies.rb +10 -0
  198. data/app/models/atlas_engine/address_validation/suggestion.rb +97 -0
  199. data/app/models/atlas_engine/address_validation/token/comparator.rb +44 -0
  200. data/app/models/atlas_engine/address_validation/token/comparison.rb +76 -0
  201. data/app/models/atlas_engine/address_validation/token/sequence/comparator.rb +158 -0
  202. data/app/models/atlas_engine/address_validation/token/sequence/comparison.rb +166 -0
  203. data/app/models/atlas_engine/address_validation/token/sequence.rb +147 -0
  204. data/app/models/atlas_engine/address_validation/token/synonyms.rb +77 -0
  205. data/app/models/atlas_engine/address_validation/token.rb +113 -0
  206. data/app/models/atlas_engine/address_validation/validator.rb +147 -0
  207. data/app/models/atlas_engine/address_validation/validators/full_address/address_comparison.rb +97 -0
  208. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result.rb +164 -0
  209. data/app/models/atlas_engine/address_validation/validators/full_address/candidate_result_base.rb +46 -0
  210. data/app/models/atlas_engine/address_validation/validators/full_address/comparison_helper.rb +135 -0
  211. data/app/models/atlas_engine/address_validation/validators/full_address/components_to_validate.rb +88 -0
  212. data/app/models/atlas_engine/address_validation/validators/full_address/concern_builder.rb +127 -0
  213. data/app/models/atlas_engine/address_validation/validators/full_address/exclusions/exclusion_base.rb +23 -0
  214. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_concern_builder.rb +42 -0
  215. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_country_concern.rb +37 -0
  216. data/app/models/atlas_engine/address_validation/validators/full_address/invalid_zip_for_province_concern.rb +37 -0
  217. data/app/models/atlas_engine/address_validation/validators/full_address/no_candidate_result.rb +26 -0
  218. data/app/models/atlas_engine/address_validation/validators/full_address/number_comparison.rb +31 -0
  219. data/app/models/atlas_engine/address_validation/validators/full_address/postal_code_matcher.rb +60 -0
  220. data/app/models/atlas_engine/address_validation/validators/full_address/result_updater.rb +42 -0
  221. data/app/models/atlas_engine/address_validation/validators/full_address/suggestion_builder.rb +140 -0
  222. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_address_concern.rb +30 -0
  223. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_province_concern.rb +38 -0
  224. data/app/models/atlas_engine/address_validation/validators/full_address/unknown_zip_for_address_concern.rb +32 -0
  225. data/app/models/atlas_engine/address_validation/validators/full_address/unmatched_field_concern.rb +84 -0
  226. data/app/models/atlas_engine/address_validation/validators/full_address/unsupported_script_result.rb +22 -0
  227. data/app/models/atlas_engine/address_validation/validators/predicates/cache.rb +38 -0
  228. data/app/models/atlas_engine/address_validation/validators/predicates/city/present.rb +36 -0
  229. data/app/models/atlas_engine/address_validation/validators/predicates/country/exists.rb +34 -0
  230. data/app/models/atlas_engine/address_validation/validators/predicates/country/valid_for_zip.rb +60 -0
  231. data/app/models/atlas_engine/address_validation/validators/predicates/no_emojis.rb +38 -0
  232. data/app/models/atlas_engine/address_validation/validators/predicates/no_html_tags.rb +39 -0
  233. data/app/models/atlas_engine/address_validation/validators/predicates/no_url.rb +38 -0
  234. data/app/models/atlas_engine/address_validation/validators/predicates/not_exceed_max_length.rb +34 -0
  235. data/app/models/atlas_engine/address_validation/validators/predicates/not_exceed_max_token_count.rb +63 -0
  236. data/app/models/atlas_engine/address_validation/validators/predicates/phone/valid.rb +41 -0
  237. data/app/models/atlas_engine/address_validation/validators/predicates/predicate.rb +37 -0
  238. data/app/models/atlas_engine/address_validation/validators/predicates/province/exists.rb +43 -0
  239. data/app/models/atlas_engine/address_validation/validators/predicates/province/valid_for_country.rb +48 -0
  240. data/app/models/atlas_engine/address_validation/validators/predicates/street/building_number_in_address1.rb +45 -0
  241. data/app/models/atlas_engine/address_validation/validators/predicates/street/building_number_in_address1_or_address2.rb +43 -0
  242. data/app/models/atlas_engine/address_validation/validators/predicates/street/present.rb +35 -0
  243. data/app/models/atlas_engine/address_validation/validators/predicates/zip/present.rb +58 -0
  244. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_country.rb +45 -0
  245. data/app/models/atlas_engine/address_validation/validators/predicates/zip/valid_for_province.rb +55 -0
  246. data/app/models/atlas_engine/address_validation/validators/predicates/zip/zip_base.rb +25 -0
  247. data/app/models/atlas_engine/address_validation/zip_truncator.rb +32 -0
  248. data/app/models/atlas_engine/application_record.rb +8 -0
  249. data/app/models/atlas_engine/coded_error.rb +18 -0
  250. data/app/models/atlas_engine/coded_errors.rb +17 -0
  251. data/app/models/atlas_engine/country_import.rb +44 -0
  252. data/app/models/atlas_engine/country_profile.rb +270 -0
  253. data/app/models/atlas_engine/country_profile_ingestion_subset.rb +42 -0
  254. data/app/models/atlas_engine/country_profile_subset_base.rb +22 -0
  255. data/app/models/atlas_engine/country_profile_validation_subset.rb +48 -0
  256. data/app/models/atlas_engine/country_repository.rb +110 -0
  257. data/app/models/atlas_engine/elasticsearch/client.rb +116 -0
  258. data/app/models/atlas_engine/elasticsearch/client_interface.rb +89 -0
  259. data/app/models/atlas_engine/elasticsearch/repository.rb +246 -0
  260. data/app/models/atlas_engine/elasticsearch/repository_interface.rb +82 -0
  261. data/app/models/atlas_engine/elasticsearch/response.rb +20 -0
  262. data/app/models/atlas_engine/event.rb +12 -0
  263. data/app/models/atlas_engine/field_decompounder.rb +36 -0
  264. data/app/models/atlas_engine/index_configuration_factory.rb +188 -0
  265. data/app/models/atlas_engine/post_address.rb +114 -0
  266. data/app/models/atlas_engine/post_address_importer.rb +34 -0
  267. data/app/models/atlas_engine/services/service_helper.rb +21 -0
  268. data/app/models/atlas_engine/services/validation.rb +65 -0
  269. data/app/models/atlas_engine/services/validation_eligibility.rb +18 -0
  270. data/app/models/atlas_engine/street.rb +34 -0
  271. data/app/tasks/maintenance/atlas_engine/elasticsearch_index_create_task.rb +106 -0
  272. data/app/tasks/maintenance/atlas_engine/geo_json_import_task.rb +29 -0
  273. data/app/views/atlas_engine/connectivity/index.html.erb +50 -0
  274. data/app/views/atlas_engine/country_imports/index.html.erb +49 -0
  275. data/app/views/atlas_engine/country_imports/show.html.erb +73 -0
  276. data/app/views/layouts/atlas_engine/application.html.erb +15 -0
  277. data/config/initializers/1.ruby_patches.rb +18 -0
  278. data/config/initializers/sorbet.rb +5 -0
  279. data/config/initializers/worldwide.rb +5 -0
  280. data/config/locales/internal/en.yml +14 -0
  281. data/config/routes.rb +17 -0
  282. data/db/data/address_synonyms/index_configurations/default.yml +141 -0
  283. data/db/data/country_profiles/default.yml +23 -0
  284. data/db/data/transcriber.yml +760 -0
  285. data/db/data/validation_pipelines/es.yml +58 -0
  286. data/db/data/validation_pipelines/es_street.yml +58 -0
  287. data/db/data/validation_pipelines/local.yml +60 -0
  288. data/db/migrate/20230919173037_create_atlas_engine_post_addresses.rb +25 -0
  289. data/db/migrate/20231117142735_add_building_and_unit_ranges_column.rb +7 -0
  290. data/db/migrate/20231117143536_create_atlas_engine_country_imports.rb +11 -0
  291. data/db/migrate/20231117145844_create_atlas_engine_events_table.rb +13 -0
  292. data/db/migrate/20231123153554_add_unique_index_to_atlas_engine_post_addresses.rb +14 -0
  293. data/db/migrate/20231123154658_add_index_to_post_addresses_on_source_id_locale_country_code.rb +12 -0
  294. data/lib/atlas_engine/engine.rb +10 -0
  295. data/lib/atlas_engine/version.rb +6 -0
  296. data/lib/atlas_engine.rb +66 -0
  297. data/lib/tasks/atlas_engine/address_importer.rake +20 -0
  298. metadata +553 -0
@@ -0,0 +1,30 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class Field
7
+ extend T::Sig
8
+
9
+ Name = T.type_alias { T.any(String, Symbol) }
10
+ Value = T.type_alias { T.nilable(String) }
11
+
12
+ sig { returns(Name) }
13
+ attr_reader :name
14
+
15
+ sig { returns(Value) }
16
+ attr_reader :value
17
+
18
+ sig { params(name: Name, value: Value).void }
19
+ def initialize(name:, value:)
20
+ @name = name
21
+ @value = value
22
+ end
23
+
24
+ sig { returns(T::Hash[Symbol, T.any(Name, Value)]) }
25
+ def attributes
26
+ { name: name, value: value }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class FullAddressValidatorBase
7
+ extend T::Sig
8
+ extend T::Helpers
9
+ abstract!
10
+
11
+ sig { returns(Result) }
12
+ attr_reader :result
13
+
14
+ sig { returns(AbstractAddress) }
15
+ attr_reader :address
16
+
17
+ sig { params(address: AbstractAddress, result: Result).void }
18
+ def initialize(address:, result:)
19
+ @address = address
20
+ @result = result
21
+ end
22
+
23
+ sig { abstract.returns(Result) }
24
+ def validate; end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,66 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class LogEmitter
7
+ extend T::Sig
8
+ include LogHelper
9
+
10
+ sig { returns(AbstractAddress) }
11
+ attr_reader :address
12
+
13
+ sig { returns(AddressValidation::Result) }
14
+ attr_reader :result
15
+
16
+ sig { returns(T::Array[Symbol]) }
17
+ attr_reader :fields
18
+
19
+ sig do
20
+ params(
21
+ address: AbstractAddress,
22
+ result: AddressValidation::Result,
23
+ ).void
24
+ end
25
+ def initialize(address:, result:)
26
+ @address = address
27
+ @result = result
28
+ @fields = [:country, :province, :zip, :city, :street, :phone]
29
+ end
30
+
31
+ sig { void }
32
+ def run
33
+ formatted_address = I18n.with_locale(:en) do
34
+ Worldwide.address(**address.to_h).single_line
35
+ end
36
+ data = {
37
+ country_code: address.country_code,
38
+ formatted_address: formatted_address,
39
+ concerns: concern_codes,
40
+ suggestions: result.suggestions.map(&:attributes),
41
+ candidate: result.candidate,
42
+ validation_id: result.id,
43
+ }
44
+
45
+ if concern_codes.any?
46
+ log_info("[AddressValidation] Concern(s) found when validating address", data)
47
+ else
48
+ log_info("[AddressValidation] Address validated, no concerns returned", data)
49
+ end
50
+ end
51
+
52
+ sig { returns(T::Array[Symbol]) }
53
+ def concern_codes
54
+ fields.flat_map do |field|
55
+ if field.equal?(:street)
56
+ result.concerns.select do |c|
57
+ c.attributes[:code] =~ /^(missing_building_number|address1|address2|street).*/
58
+ end
59
+ else
60
+ result.concerns.select { |c| c.attributes[:code] =~ /^#{field}.*/ }
61
+ end
62
+ end.map(&:code)
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,16 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class MatchingStrategies < T::Enum
7
+ include Strategies
8
+
9
+ enums do
10
+ Es = new("es")
11
+ EsStreet = new("es_street")
12
+ Local = new("local")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,38 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ module Normalizer
7
+ extend T::Sig
8
+
9
+ sig do
10
+ params(
11
+ string: String,
12
+ ).returns(String)
13
+ end
14
+ def normalize(string)
15
+ string
16
+ .gsub("Æ", "AE")
17
+ .gsub("Œ", "OE")
18
+ .gsub("æ", "ae")
19
+ .gsub("œ", "oe")
20
+ .gsub(" ", " ")
21
+ # normalizes Arabic characters
22
+ .tr("آ", "ا")
23
+ .tr("أ", "ا")
24
+ .tr("إ", "ا")
25
+ .tr("ئ", "ي")
26
+ .tr("ة", "ه")
27
+ .tr("ى", "ي")
28
+ # removes Arabic stretching characters and diacritics
29
+ .gsub(/[\u064B|\u064C|\u064D|\u064E|\u064F|\u0650|\u0651|\u0652|\u0640]/, "")
30
+ # TODO: Strip hyphens for USPS not zip
31
+ .gsub(/[!@%&"'*,.();:]/, "")
32
+ .downcase
33
+ .tr(T.unsafe(AtlasEngine::ValidationTranscriber::Constants.instance).with_diacritics,
34
+ T.unsafe(AtlasEngine::ValidationTranscriber::Constants.instance).without_diacritics)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,80 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class PredicatePipeline < FrozenRecord::Base
7
+ extend T::Sig
8
+
9
+ VALIDATION_PIPELINES_ROOT = T.let(File.join(AtlasEngine::Engine.root, "db/data/validation_pipelines"), String)
10
+
11
+ module Backend
12
+ extend FrozenRecord::Backends::Yaml
13
+
14
+ class << self
15
+ extend T::Sig
16
+
17
+ sig { params(file_path: String).returns(T.untyped) }
18
+ def load(file_path)
19
+ # FrozenRecord's default is to operate on a single YAML file containing all the records.
20
+ # A custom backend like ours, that uses separate files, must load all of them and return an array.
21
+ Dir[File.join(PredicatePipeline.pipeline_path, "*.yml")].map do |validation_pipeline|
22
+ super(validation_pipeline)
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ class << self
29
+ extend T::Sig
30
+
31
+ sig { returns(String) }
32
+ def pipeline_path
33
+ @pipeline_path ||= T.let(VALIDATION_PIPELINES_ROOT, T.nilable(String))
34
+ end
35
+
36
+ sig { params(pipeline_path: String).void }
37
+ attr_writer :pipeline_path
38
+ end
39
+
40
+ self.base_path = VALIDATION_PIPELINES_ROOT
41
+ self.backend = Backend
42
+
43
+ sig { returns(T::Array[PredicateConfig]) }
44
+ def pipeline
45
+ attributes.dig("pipeline").map do |config|
46
+ PredicateConfig.new(
47
+ class_name: config["class"].constantize,
48
+ field: config["field"].to_sym,
49
+ )
50
+ end
51
+ end
52
+
53
+ sig { returns(T.nilable(T::Class[FullAddressValidatorBase])) }
54
+ def full_address_validator
55
+ attributes.dig("full_address_validator")&.constantize
56
+ end
57
+
58
+ class PredicateConfig
59
+ extend T::Sig
60
+
61
+ sig { returns(Symbol) }
62
+ attr_reader :field
63
+
64
+ sig { returns(T::Class[Validators::Predicates::Predicate]) }
65
+ attr_reader :class_name
66
+
67
+ sig do
68
+ params(
69
+ class_name: T::Class[Validators::Predicates::Predicate],
70
+ field: Symbol,
71
+ ).void
72
+ end
73
+ def initialize(class_name:, field:)
74
+ @field = field
75
+ @class_name = class_name
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,12 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class Request < T::Struct
7
+ const :address, AbstractAddress
8
+ const :locale, String, default: "en"
9
+ const :matching_strategy, T.nilable(T.any(String, Symbol)), default: nil
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,154 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class Result
7
+ extend T::Sig
8
+
9
+ SORTED_VALIDATION_SCOPES = T.let(
10
+ [:country_code, :province_code, :zip, :city, :address1, :address2, :phone].freeze,
11
+ T::Array[Symbol],
12
+ )
13
+
14
+ sig { returns(T.nilable(String)) }
15
+ attr_accessor :client_request_id
16
+
17
+ sig { returns(T.nilable(String)) }
18
+ attr_accessor :origin
19
+
20
+ sig { returns(T::Array[Field]) }
21
+ attr_accessor :fields
22
+
23
+ sig { returns(T::Array[Concern]) }
24
+ attr_accessor :concerns
25
+
26
+ sig { returns(T::Array[Suggestion]) }
27
+ attr_accessor :suggestions
28
+
29
+ sig { returns(T::Array[String]) }
30
+ attr_accessor :validation_scope
31
+
32
+ sig { returns(String) }
33
+ attr_accessor :locale
34
+
35
+ sig { returns(T.nilable(String)) }
36
+ attr_accessor :candidate
37
+
38
+ sig { returns(T::Array[Errors]) }
39
+ attr_accessor :errors
40
+
41
+ sig { returns(String) }
42
+ attr_reader :id
43
+
44
+ sig { returns(T.nilable(String)) }
45
+ attr_reader :matching_strategy
46
+
47
+ alias_attribute :components, :fields
48
+
49
+ sig do
50
+ params(
51
+ client_request_id: T.nilable(String),
52
+ origin: T.nilable(String),
53
+ fields: T::Array[Field],
54
+ concerns: T::Array[Concern],
55
+ suggestions: T::Array[Suggestion],
56
+ validation_scope: T::Array[String],
57
+ errors: T::Array[Errors],
58
+ locale: String,
59
+ candidate: T.nilable(String),
60
+ matching_strategy: T.nilable(String),
61
+ ).void
62
+ end
63
+ def initialize(
64
+ client_request_id: nil,
65
+ origin: nil,
66
+ fields: [],
67
+ concerns: [],
68
+ suggestions: [],
69
+ validation_scope: [],
70
+ errors: [],
71
+ locale: "en",
72
+ candidate: nil,
73
+ matching_strategy: nil
74
+ )
75
+ @origin = origin
76
+ @client_request_id = client_request_id
77
+ @fields = fields
78
+ @concerns = concerns
79
+ @suggestions = suggestions
80
+ @validation_scope = validation_scope
81
+ @errors = errors
82
+ @locale = locale
83
+ @candidate = candidate
84
+ @matching_strategy = matching_strategy
85
+
86
+ # For now, this UUID isn't predicated on anything and is random.
87
+ # There could be need in the future to help make this unique on all requests.
88
+ # For now, what is important is that it one is simply generated and assigned.
89
+ @id = T.let(generate_id, String)
90
+ end
91
+
92
+ sig { returns(T::Hash[Symbol, T.untyped]) }
93
+ def attributes
94
+ {
95
+ id: id,
96
+ fields: fields.map(&:attributes),
97
+ concerns: concerns.map(&:attributes),
98
+ suggestions: suggestions.map(&:attributes),
99
+ validation_scope: validation_scope,
100
+ locale: locale,
101
+ }
102
+ end
103
+
104
+ sig do
105
+ params(
106
+ code: Symbol,
107
+ type: String,
108
+ type_level: Integer,
109
+ suggestion_ids: T::Array[String],
110
+ field_names: T::Array[Symbol],
111
+ message: String,
112
+ ).returns(Concern)
113
+ end
114
+ def add_concern(code:, type:, type_level:, suggestion_ids:, field_names:, message:)
115
+ new_concern = Concern.new(
116
+ field_names: field_names,
117
+ code: code,
118
+ type: type,
119
+ type_level: type_level,
120
+ suggestion_ids: suggestion_ids,
121
+ message: message,
122
+ )
123
+ concerns << new_concern
124
+ new_concern
125
+ end
126
+
127
+ sig do
128
+ params(suggestions_to_add: T::Array[Suggestion]).returns(T::Array[T::Array[Suggestion]])
129
+ end
130
+ def add_suggestions(suggestions_to_add)
131
+ suggestions_to_add.map { |suggestion| suggestions << suggestion }
132
+ end
133
+
134
+ sig { returns(String) }
135
+ def completion_service
136
+ "AddressValidation"
137
+ end
138
+
139
+ sig { returns(T::Hash[Symbol, T.nilable(String)]) }
140
+ def address
141
+ fields.each_with_object({}) do |field, hash|
142
+ hash[field.name.to_sym] = field.value.to_s
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ sig { returns(String) }
149
+ def generate_id
150
+ Digest::UUID.uuid_v4
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,16 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ module RunsValidation
7
+ extend T::Sig
8
+ extend T::Helpers
9
+
10
+ interface!
11
+
12
+ sig { abstract.returns(Result) }
13
+ def run; end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ require "forwardable"
5
+
6
+ module AtlasEngine
7
+ module AddressValidation
8
+ class Session
9
+ extend Forwardable
10
+ extend T::Sig
11
+
12
+ sig { returns(AbstractAddress) }
13
+ attr_reader :address
14
+
15
+ sig { returns(MatchingStrategies) }
16
+ attr_accessor :matching_strategy
17
+
18
+ sig { returns(T::Hash[String, AtlasEngine::AddressValidation::DatastoreBase]) }
19
+ attr_reader :datastore_hash
20
+
21
+ def_delegators :@address, :address1, :address2, :city, :province_code, :country_code, :zip, :phone
22
+
23
+ sig do
24
+ params(
25
+ address: AbstractAddress,
26
+ matching_strategy: MatchingStrategies,
27
+ ).void
28
+ end
29
+ def initialize(address:, matching_strategy: MatchingStrategies::Es)
30
+ @address = address
31
+ @matching_strategy = matching_strategy
32
+ @datastore_hash = T.let({}, T::Hash[String, AtlasEngine::AddressValidation::DatastoreBase])
33
+ end
34
+
35
+ sig { params(locale: T.nilable(String)).returns(ValidationTranscriber::AddressParsings) }
36
+ def parsings(locale: nil)
37
+ datastore(locale: locale).parsings
38
+ end
39
+
40
+ sig { params(locale: T.nilable(String)).returns(AtlasEngine::AddressValidation::DatastoreBase) }
41
+ def datastore(locale: nil)
42
+ key = locale || "default"
43
+ @datastore_hash[key] ||= AtlasEngine::AddressValidation::Es::Datastore.new(address: address, locale: locale)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,72 @@
1
+ # typed: true
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class StatsdEmitter
7
+ extend T::Sig
8
+ attr_reader :address, :result, :components
9
+
10
+ sig { params(address: AbstractAddress, result: Result, components: T.nilable(T::Array[Symbol])).void }
11
+ def initialize(address:, result:,
12
+ components: [:country, :province, :zip, :city, :street, :building_number, :phone])
13
+ @address = address
14
+ @result = result
15
+ @components = components
16
+ end
17
+
18
+ sig { void }
19
+ def run
20
+ components.each do |component|
21
+ emit(component)
22
+ end
23
+ end
24
+
25
+ sig { params(component: Symbol).void }
26
+ def emit(component)
27
+ concerns = component_concerns(component)
28
+ ending_breadcrumb = concerns.present? ? "invalid" : "valid"
29
+
30
+ country_code = if address.country_code.blank? || !Worldwide.region(code: address.country_code).country?
31
+ "no_country"
32
+ else
33
+ Worldwide.region(code: address.country_code).iso_code
34
+ end
35
+
36
+ I18n.with_locale("en") do
37
+ tags = {
38
+ country: country_code,
39
+ component: component,
40
+ }.compact
41
+
42
+ if concerns.empty?
43
+ StatsD.increment("AddressValidation.#{ending_breadcrumb}", tags: tags)
44
+ else
45
+ concerns.each do |concern|
46
+ tags.merge!(concern.attributes.slice(:code, :type))
47
+
48
+ StatsD.increment("AddressValidation.#{ending_breadcrumb}", tags: tags)
49
+ end
50
+ end
51
+ end
52
+
53
+ nil
54
+ end
55
+
56
+ sig { params(component: Symbol).returns(T::Array[Concern]) }
57
+ def component_concerns(component)
58
+ if component.equal?(:street)
59
+ result.concerns.select do |c|
60
+ c.attributes[:code] =~ /^(address1|address2|street).*/
61
+ end
62
+ elsif component.equal?(:building_number)
63
+ result.concerns.select do |c|
64
+ c.attributes[:code] =~ /^(missing_building_number).*/
65
+ end
66
+ else
67
+ result.concerns.select { |c| c.attributes[:code] =~ /^#{component}.*/ }
68
+ end
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,10 @@
1
+ # typed: false
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ module Strategies
7
+ extend T::Helpers
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,97 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module AtlasEngine
5
+ module AddressValidation
6
+ class Suggestion
7
+ extend T::Sig
8
+
9
+ sig { returns(T.nilable(String)) }
10
+ attr_reader :id
11
+
12
+ sig { returns(T.nilable(String)) }
13
+ attr_accessor :address1
14
+
15
+ sig { returns(T.nilable(String)) }
16
+ attr_accessor :address2
17
+
18
+ sig { returns(T.nilable(String)) }
19
+ attr_accessor :city
20
+
21
+ sig { returns(T.nilable(String)) }
22
+ attr_accessor :zip
23
+
24
+ sig { params(province_code: T.nilable(String)).returns(T.nilable(String)) }
25
+ attr_writer :province_code
26
+
27
+ sig { returns(T.nilable(String)) }
28
+ attr_accessor :country_code
29
+
30
+ sig do
31
+ params(
32
+ address1: T.nilable(String),
33
+ address2: T.nilable(String),
34
+ city: T.nilable(String),
35
+ zip: T.nilable(String),
36
+ province_code: T.nilable(String),
37
+ country_code: T.nilable(String),
38
+ ).void
39
+ end
40
+ def initialize(address1: nil, address2: nil, city: nil, zip: nil, province_code: nil, country_code: nil)
41
+ @address1 = address1
42
+ @address2 = address2
43
+ @city = city
44
+ @zip = zip
45
+ @province_code = province_code
46
+ @original_country_code = country_code
47
+ @country_code = country_code
48
+
49
+ # generate_id uses the values of the attributes to calculate the UUID, so must be called after they're set
50
+ @id = T.let(generate_id, String)
51
+ end
52
+
53
+ sig { returns(T::Hash[Symbol, T.untyped]) }
54
+ def attributes
55
+ {
56
+ id: id,
57
+ address1: address1,
58
+ address2: address2,
59
+ city: city,
60
+ zip: zip,
61
+ province_code: province_code,
62
+ province: province,
63
+ country_code: country_code,
64
+ }
65
+ end
66
+
67
+ sig { returns(T.nilable(String)) }
68
+ def province
69
+ return if @original_country_code.nil? || @province_code.nil?
70
+
71
+ province = Worldwide.region(code: @original_country_code)&.zone(code: @province_code)
72
+ province.province? ? province.full_name : nil
73
+ end
74
+
75
+ sig { returns(T.nilable(String)) }
76
+ def province_code
77
+ return @province_code if @province_code.blank?
78
+
79
+ # This hack is required since checkout-web is using province codes differently for Japan Vs Other countries
80
+ # Japan province codes are expected as full ISO codes (JP-14)
81
+ # while other countries are expected as 2 digit subdivision codes (e.g. ON)
82
+ # we can remove this logic if the client can accept 2 digit subdivision codes / ISO codes as standard response
83
+ province = Worldwide.region(code: @original_country_code)&.zone(code: @province_code)
84
+ return @province_code unless province.province?
85
+
86
+ province.legacy_code
87
+ end
88
+
89
+ private
90
+
91
+ sig { returns(String) }
92
+ def generate_id
93
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, attributes.except(:id).to_s)
94
+ end
95
+ end
96
+ end
97
+ end