Skip to content

Commit

Permalink
Merge pull request #22 from martide/master
Browse files Browse the repository at this point in the history
Bump elixir/erlang and dependencies & add timezone to airports
  • Loading branch information
sjoulbak authored Jul 23, 2024
2 parents 1cf6365 + 83f9195 commit 80bdc8a
Show file tree
Hide file tree
Showing 13 changed files with 99,610 additions and 14,171 deletions.
53 changes: 27 additions & 26 deletions .github/workflows/elixir.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,68 +11,69 @@ env:

# https://github.com/elixir-lang/elixir/blob/master/lib/elixir/pages/compatibility-and-deprecations.md
jobs:
elixir_1_13:
runs-on: ubuntu-20.04
elixir_1_17:
runs-on: ubuntu-latest
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
matrix:
otp: [22.x, 23.x, 24.x]
elixir: [1.13.x]
otp: [25.x, 26.x, 27.x]
elixir: [1.17.x]
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-elixir@v1
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix do deps.get, compile --warnings-as-errors
- run: mix format --dry-run --check-formatted
- run: mix coveralls.github
- run: mix test

elixir_1_12:
runs-on: ubuntu-20.04
elixir_1_16:
runs-on: ubuntu-latest
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
matrix:
otp: [22.x, 23.x, 24.x]
elixir: [1.12.x]
otp: [24.x, 25.x, 26.x]
elixir: [1.16.x]
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-elixir@v1
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix do deps.get, compile --warnings-as-errors
- run: mix format --dry-run --check-formatted
- run: mix coveralls.github
- run: mix test

elixir_1_11:
elixir_1_15:
runs-on: ubuntu-20.04
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
matrix:
otp: [21.x, 22.x, 23.x, 24.x]
elixir: [1.11.x]
otp: [24.x, 25.x, 26.x]
elixir: [1.15.x]
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-elixir@v1
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix do deps.get, compile --warnings-as-errors
- run: mix coveralls.github
- run: mix format --dry-run --check-formatted
- run: mix test

elixir_1_10:
elixir_1_14:
runs-on: ubuntu-20.04
name: OTP ${{matrix.otp}} / Elixir ${{matrix.elixir}}
strategy:
matrix:
otp: [21.x, 22.x, 23.x]
elixir: [1.10.x]
otp: [24.x, 25.x]
elixir: [1.14.x]
steps:
- uses: actions/checkout@v2
- uses: erlef/setup-elixir@v1
- uses: actions/checkout@v4
- uses: erlef/setup-beam@v1
with:
otp-version: ${{matrix.otp}}
elixir-version: ${{matrix.elixir}}
- run: mix do deps.get, compile --warnings-as-errors
- run: mix coveralls.github
- run: mix test
9 changes: 7 additions & 2 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -1,2 +1,7 @@
elixir 1.13.0-otp-24
erlang 24.1.7
# https://github.com/elixir-lang/elixir/releases

elixir 1.17.2-otp-27

# https://github.com/erlang/otp/releases

erlang 27.0.1
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
- Added dependabot
- Added Github Actions
- Swap csv for nimble_csv
- Add timezone information accessible through the `timezone_id` field on the `Airport` struct
- Set `skip_headers: true` when parsing airports from source file

## v0.1.0 (2017-06-09)

Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Airports

Airports is a collection of all known airports. Data source is https://github.com/davidmegginson/ourairports-data
Airports is a collection of all known airports. Data source is https://github.com/davidmegginson/ourairports-data

## Installation

Expand All @@ -13,6 +13,14 @@ def deps do
end
```

## Installing the Airports data

Airports depends on [tz_world](https://github.com/kimlai/tz_world) for timezone data. To install airports with timezone data, run:

```elixir
mix airports.update
```

## Contributing

Before opening a pull request, please open an issue first.
Expand Down
4 changes: 3 additions & 1 deletion lib/airport.ex
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
defmodule Airports.Airport do
@type t :: %__MODULE__{}
defstruct [
:ident,
:type,
Expand All @@ -16,6 +17,7 @@ defmodule Airports.Airport do
:local_code,
:home_link,
:wikipedia_link,
:keywords
:keywords,
:timezone_id
]
end
67 changes: 19 additions & 48 deletions lib/airports.ex
Original file line number Diff line number Diff line change
@@ -1,56 +1,27 @@
NimbleCSV.define(AirportsParser, separator: ",", escape: "\"")

defmodule Airports do
@moduledoc """
Airports main API
"""
@airports [__DIR__, "../priv", "airports.csv"]
|> Path.join()
|> File.stream!([], :line)
|> AirportsParser.parse_stream(skip_headers: false)
|> Stream.map(fn line ->
[
_,
ident,
type,
name,
latitude,
longitude,
elevation_ft,
continent,
iso_country,
iso_region,
municipality,
scheduled_service,
gps_code,
iata_code,
local_code,
home_link,
wikipedia_link,
keywords
] = line
alias Airports.Airport

%Airports.Airport{
ident: ident,
type: type,
name: name,
latitude: latitude,
longitude: longitude,
elevation_ft: elevation_ft,
continent: continent,
iso_country: iso_country,
iso_region: iso_region,
municipality: municipality,
scheduled_service: scheduled_service,
gps_code: gps_code,
iata_code: iata_code,
local_code: local_code,
home_link: home_link,
wikipedia_link: wikipedia_link,
keywords: keywords
}
end)
|> Enum.to_list()
@source_path Airports.Loader.source_path()
@airports if File.exists?(@source_path), do: Airports.Loader.run(), else: []

@spec all() :: [Airport.t()]
def all, do: @airports

@spec get_airport(String.t()) :: Airport.t() | nil
def get_airport(iata_code) do
Enum.find(all(), &(&1.iata_code == iata_code))
end

@spec get_timezone(String.t()) :: String.t() | nil
def get_timezone(iata_code) do
iata_code
|> get_airport()
|> case do
nil -> nil
airport -> airport.timezone_id
end
end
end
63 changes: 63 additions & 0 deletions lib/airports/loader.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
NimbleCSV.define(AirportsParser, separator: ",", escape: "\"")

defmodule Airports.Loader do
@moduledoc """
Load airports from a CSV file
"""
alias Airports.Airport
# Tag as external_resource to ensure this module recompiles when this file changed
@external_resource Path.join([:code.priv_dir(:airports), "airports_with_tz.csv"])

def source_path, do: @external_resource

def run do
source_path()
|> File.stream!()
|> AirportsParser.parse_stream(skip_headers: true)
|> Stream.map(fn line ->
[
_,
ident,
type,
name,
latitude,
longitude,
elevation_ft,
continent,
iso_country,
iso_region,
municipality,
scheduled_service,
gps_code,
iata_code,
local_code,
home_link,
wikipedia_link,
keywords,
timezone_id
] = line

%Airport{
ident: ident,
type: type,
name: name,
latitude: latitude,
longitude: longitude,
elevation_ft: elevation_ft,
continent: continent,
iso_country: iso_country,
iso_region: iso_region,
municipality: municipality,
scheduled_service: scheduled_service,
gps_code: gps_code,
iata_code: iata_code,
local_code: local_code,
home_link: home_link,
wikipedia_link: wikipedia_link,
keywords: keywords,
timezone_id: timezone_id
}
end)
|> Enum.to_list()
end
end
87 changes: 87 additions & 0 deletions lib/mix/tasks/update.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
defmodule Mix.Tasks.Airports.Update do
@moduledoc """
Updates the latest timezone data of each airport and save it on the timezone_id column of airports.csv
"""

@shortdoc "Update airports with timezone data"

@source_file "airports.csv"
@destination_path Path.join([:code.priv_dir(:airports), "airports_with_tz.csv"])

use Mix.Task
require Logger

def run(_args) do
restart_backends()
Process.put(:lines, 0)

stream =
[:code.priv_dir(:airports), @source_file]
|> Path.join()
|> File.stream!()

total_lines = Enum.count(stream)

Mix.shell().info("Loading airports from #{@source_file}...")

stream
|> AirportsParser.parse_stream(skip_headers: false)
|> Stream.map(fn
["id" | _] = headers ->
display_progress(total_lines)
headers ++ ["timezone_id"]

line ->
[
_,
_ident,
_type,
_name,
latitude,
longitude,
_elevation_ft,
_continent,
_iso_country,
_iso_region,
_municipality,
_scheduled_service,
_gps_code,
_iata_code,
_local_code,
_home_link,
_wikipedia_link,
_keywords
] = line

timezone_id = get_timezone(longitude, latitude)
display_progress(total_lines)
line ++ [timezone_id]
end)
|> AirportsParser.dump_to_iodata()
|> then(&File.write!(@destination_path, &1))

Mix.shell().info("\nSuccessfully saved airports data!")
end

defp display_progress(total_lines) do
Process.put(:lines, Process.get(:lines) + 1)
IO.write("\rUpdating timezone data #{div(Process.get(:lines) * 100, total_lines)}%...")
end

defp get_timezone(long, lat) do
with {long, _} <- Float.parse(long),
{lat, _} <- Float.parse(lat),
{:ok, tz} <- TzWorld.timezone_at({long, lat}) do
tz
else
_ -> nil
end
end

defp restart_backends do
GenServer.stop(TzWorld.Backend.Memory)
GenServer.stop(TzWorld.Backend.Dets)
TzWorld.Backend.Memory.start_link()
TzWorld.Backend.Dets.start_link()
end
end
Loading

0 comments on commit 80bdc8a

Please sign in to comment.