Skip to content

Commit

Permalink
Update mix.exs to newer format, add debug logging in dev, support str…
Browse files Browse the repository at this point in the history
…eets named like suffixes
  • Loading branch information
tmepple committed Apr 9, 2019
1 parent 35f4386 commit 5f7c0cf
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 65 deletions.
30 changes: 30 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# This file is responsible for configuring your application
# and its dependencies with the aid of the Mix.Config module.
use Mix.Config

# This configuration is loaded before any dependency and is restricted
# to this project. If another project depends on this project, this
# file won't be loaded nor affect the parent project. For this reason,
# if you want to provide default values for your application for
# third-party users, it should be done in your "mix.exs" file.

# You can configure your application as:
#
# config :address_us, key: :value
#
# and access this configuration in your application as:
#
# Application.get_env(:address_us, :key)
#
# You can also configure a third-party app:
#
# config :logger, level: :info
#

# It is also possible to import configuration files, relative to this
# directory. For example, you can emulate configuration per environment
# by uncommenting the line below and defining dev.exs, test.exs and such.
# Configuration from the imported file will override the ones defined
# here (which is why it is important to import them last).
#
import_config "#{Mix.env()}.exs"
3 changes: 3 additions & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

config :logger, level: :debug
3 changes: 3 additions & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

config :logger, level: :warn
3 changes: 3 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
use Mix.Config

config :logger, level: :warn
147 changes: 106 additions & 41 deletions lib/address_us.ex
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ defmodule AddressUS.Parser do
post_direction: nil, pre_direction: "S", primary_number: "2345",
secondary_designator: nil, secondary_value: nil, suffix: "St"}}
"""

require Logger

def parse_address(messy_address) when not is_binary(messy_address), do: nil

def parse_address(messy_address) do
Expand All @@ -72,9 +75,10 @@ defmodule AddressUS.Parser do
def parse_address_line(messy_address) do
messy_address
|> standardize_address
|> log_term("std addr")
|> String.split(" ")
|> Enum.reverse()
|> parse_address_list
|> parse_address_list()
end

@doc """
Expand Down Expand Up @@ -431,8 +435,8 @@ defmodule AddressUS.Parser do

# Parses the pre direction field out of the address list and returns
# {pre_direction, leftover_address_list}.
defp get_pre_direction(address) when not is_list(address), do: {nil, nil}
defp get_pre_direction([]), do: {nil, nil}
defp get_pre_direction(address) when not is_list(address), do: {nil, nil, nil}
defp get_pre_direction([]), do: {nil, nil, nil}
defp get_pre_direction(address), do: get_pre_direction(address, nil, false)

defp get_pre_direction(address, _pre_direction, false) do
Expand All @@ -454,27 +458,29 @@ defmodule AddressUS.Parser do

tail_tail_head_is_keyword = is_keyword?(tail_tail_head)

log_term({single_word_direction, next_is_direction, tail}, "g_p_d_internals")

cond do
single_word_direction != "" && next_is_direction &&
tail_tail_head_is_keyword ->
{single_word_direction, tail}
{single_word_direction, head, tail}

single_word_direction != "" && next_is_direction &&
tail_tail_head == nil ->
{single_word_direction, tail}
{single_word_direction, head, tail}

single_word_direction != "" && next_is_direction &&
!tail_tail_head_is_keyword ->
{double_word_direction, tail_tail}
{double_word_direction, head <> tail_head, tail_tail}

single_word_direction != "" && tail == [] ->
{nil, address}
# single_word_direction != "" && tail == [] ->
# {nil, address}

single_word_direction != "" ->
{single_word_direction, tail}
{single_word_direction, head, tail}

true ->
{nil, address}
{nil, nil, address}
end
end

Expand Down Expand Up @@ -767,24 +773,24 @@ defmodule AddressUS.Parser do

# Parses the suffix out of the address list and returns
# {suffix, leftover_address_list}
defp get_suffix(address) when not is_list(address), do: {nil, nil}
defp get_suffix([]), do: {nil, nil}
defp get_suffix(address), do: get_suffix(address, nil, false)
defp get_suffix(address, suffix, true), do: {suffix, address}
defp get_suffix(address) when not is_list(address), do: {nil, nil, nil}
defp get_suffix([]), do: {nil, nil, nil}
defp get_suffix(address), do: get_suffix(address, nil, nil, false)
defp get_suffix(address, suffix, raw_suffix, true), do: {suffix, raw_suffix, address}

defp get_suffix(address, _, false) do
defp get_suffix(address, _, _, false) do
[head | tail] = address
new_suffix = get_suffix_value(head)

cond do
Enum.count(clean_hyphenated_street(head)) > 1 ->
get_suffix(address, nil, true)
get_suffix(address, nil, nil, true)

new_suffix != nil ->
get_suffix(tail, new_suffix, true)
get_suffix(tail, new_suffix, title_case(head), true)

true ->
get_suffix(address, nil, true)
get_suffix(address, nil, nil, true)
end
end

Expand All @@ -797,39 +803,91 @@ defmodule AddressUS.Parser do
defp parse_address_list([""]), do: nil

defp parse_address_list(address) do
cleaned_address = Enum.map(address, &safe_replace(&1, ",", ""))
{designator, value, pmb, address_no_secondary} = get_secondary(cleaned_address)
{post_direction, address_no_secondary_direction} = get_post_direction(address_no_secondary)
{suffix, address_no_suffix} = get_suffix(address_no_secondary_direction)
reversed_address_remnants = Enum.reverse(address_no_suffix)
{primary_number, box, p_val, p_des, address_no_number} = get_number(reversed_address_remnants)
{pre_direction, address_no_pre_direction} = get_pre_direction(address_no_number)
street_name = get_street(address_no_pre_direction)

name =
case street_name == nil && !(box == nil) do
true -> box
false -> street_name
end
cleaned_address =
Enum.map(address, &safe_replace(&1, ",", ""))
|> log_term("cleaned")

{final_name, final_secondary_val} =
cond do
name == nil ->
cond do
p_val != nil && p_des == nil -> {p_val, nil}
true -> {nil, p_val}
end
{designator, value, pmb, address_no_secondary} =
get_secondary(cleaned_address)
|> log_term("get_secondary")

true ->
{name, p_val}
{post_direction, address_no_secondary_direction} =
get_post_direction(address_no_secondary)
|> log_term("get_post_direction")

{suffix, raw_suffix, address_no_suffix} =
get_suffix(address_no_secondary_direction)
|> log_term("get_suffix")

reversed_address_remnants =
Enum.reverse(address_no_suffix)
|> log_term("reversed")

{primary_number, box, p_val, p_des, address_no_number} =
get_number(reversed_address_remnants)
|> log_term("get_number")

{pre_direction, raw_pre_direction, address_no_pre_direction} =
get_pre_direction(address_no_number)
|> log_term("get_pre_direction")

street_name =
get_street(address_no_pre_direction)
|> log_term("get_street")

# name =
# case street_name == nil && !(box == nil) do
# true -> box
# false -> street_name
# end

{final_name, pre_direction, suffix, final_secondary_val} =
case {street_name, box, pre_direction, suffix, p_val, p_des} do
{nil, b, _, _, _, _} when b != nil ->
{box, pre_direction, suffix, p_val}

{nil, _, _, _, pv, nil} when pv != nil ->
{pv, pre_direction, suffix, nil}

# It's much more likely a suffix is really the street name (when name is nil)
# unless the suffix is in a short list of common suffixes which should never be street names
{nil, _, pre, suf, _, _} when pre != nil and suf in ["St", "Dr"] ->
{raw_pre_direction, nil, suffix, p_val}

{nil, _, _pre, suf, _, _} when suf != nil ->
{raw_suffix, pre_direction, nil, p_val}

{nil, _, pre, _suf, _, _} when pre != nil ->
{raw_pre_direction, nil, suffix, p_val}

_ ->
{street_name, pre_direction, suffix, p_val}
end

# IO.inspect(name, label: "name")

# {final_name, final_secondary_val} =
# cond do
# name == nil ->
# cond do
# p_val != nil && p_des == nil -> {p_val, nil}
# true -> {nil, p_val}
# end

# true ->
# {name, p_val}
# end

log_term({final_name, final_secondary_val}, "final_name&secondary_val")

final_secondary_designator =
cond do
designator == nil && p_des != nil -> p_des
true -> designator
end

log_term(final_secondary_designator, "final_secondary_designator")

final_secondary_value =
cond do
p_val == nil ->
Expand All @@ -842,6 +900,8 @@ defmodule AddressUS.Parser do
end
end

log_term(final_secondary_value, "final_secondary_value")

%Street{
secondary_designator: final_secondary_designator,
post_direction: post_direction,
Expand Down Expand Up @@ -1183,4 +1243,9 @@ defmodule AddressUS.Parser do
false
end
end

defp log_term(term, label) do
Logger.debug(label <> ": " <> inspect(term))
term
end
end
20 changes: 6 additions & 14 deletions mix.exs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
defmodule AddressUS.Mixfile do
defmodule AddressUs.MixProject do
use Mix.Project

def project do
Expand All @@ -15,22 +15,14 @@ defmodule AddressUS.Mixfile do
]
end

# Configuration for the OTP application
#
# Type `mix help compile.app` for more information
# Run "mix help compile.app" to learn about applications.
def application do
[applications: [:logger]]
[
extra_applications: [:logger]
]
end

# Dependencies can be hex.pm packages:
#
# {:mydep, "~> 0.3.0"}
#
# Or git/path repositories:
#
# {:mydep, git: "https://github.com/elixir-lang/mydep.git", tag: "0.1"}
#
# Type `mix help deps` for more examples and options
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:earmark, "~> 1.2.5", only: :dev},
Expand Down
35 changes: 25 additions & 10 deletions test/address_us_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -609,16 +609,17 @@ defmodule AddressUSTest do
assert desired_result == result
end

test "Parse address: 804 & 806 W Street, Watertown, North Dakota" do
desired_result = %Address{
city: "Watertown",
state: "ND",
street: %Street{name: "West", primary_number: "806", suffix: "St"}
}

result = parse_address("804 & 806 W Street, Watertown, North Dakota")
assert desired_result == result
end
# I think this should be parsed as "W" street not West street (i.e. W Street, Lincoln, NE and W Street NW, Washington, DC are real streets)
# test "Parse address: 804 & 806 W Street, Watertown, North Dakota" do
# desired_result = %Address{
# city: "Watertown",
# state: "ND",
# street: %Street{name: "West", primary_number: "806", suffix: "St"}
# }

# result = parse_address("804 & 806 W Street, Watertown, North Dakota")
# assert desired_result == result
# end

test "Parse address: 804 & 806 N West Street, Watertown, WI" do
desired_result = %Address{
Expand Down Expand Up @@ -1339,4 +1340,18 @@ defmodule AddressUSTest do
assert desired_result == parse_address(addr)
assert desired_result == String.upcase(addr) |> parse_address()
end

test "1410 East Boulevard, Kokomo, IN 46902" do
addr = "1410 East Boulevard, Kokomo, IN 46902"

desired_result = %Address{
city: "Kokomo",
state: "IN",
postal: "46902",
street: %Street{name: "Boulevard", primary_number: "1410", pre_direction: "E"}
}

assert desired_result == parse_address(addr)
assert desired_result == String.upcase(addr) |> parse_address()
end
end

0 comments on commit 5f7c0cf

Please sign in to comment.