Skip to content

Commit

Permalink
Merge pull request #25 from kipcole9/master
Browse files Browse the repository at this point in the history
Add --include-oceans and --force options to tzworld.update. Closes #23
  • Loading branch information
kipcole9 authored Oct 12, 2022
2 parents 9f0b9a3 + 8488ef7 commit f23903e
Show file tree
Hide file tree
Showing 5 changed files with 137 additions and 77 deletions.
12 changes: 10 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
## Changelog for Tz_World

## Tz_World v1.1.1
## Tz_World v1.2.0

This is the changelog for Tz_World v1.1.1 released on October 12th, 2022. For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)
This is the changelog for Tz_World v1.2.0 released on October 12th, 2022. For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)

### Bug Fixes

* Fix `TzWorld.Backend.Dets` to not raise an exception if there is no timezone data available.

### Enhancements

* Adds options to `mix tzworld.update` mix task:
* `--include_oceans` will download a 10% larger geojson data set that covers the worlds oceans
* `--force` will force a data update, even if the data is the latest release. This can be used
to switch between data that includes oceans and that which does not.
* Thanks to @lguminski for the feedback and suggestion.

## Tz_World v1.1.0

This is the changelog for Tz_World v1.1.0 released on August 26th, 2022. For older changelogs please consult the release tag on [GitHub](https://github.com/kimlai/tz_world/tags)
Expand Down
58 changes: 38 additions & 20 deletions lib/mix/tasks/update_timezones_geojson.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,46 +4,55 @@ defmodule Mix.Tasks.TzWorld.Update do
## Argument
* `--include_oceans [true | false]` determines whether to
include the geojson for oceans in the downloaded data.
The default is `false.`
* `--include-oceans` Will include the geojson for
oceans in the downloaded data.
* `--force` will force an update even if the data is
current. This can be used to force downloading data
including (or not including) time zone data for the oceans.
"""

@shortdoc "Downloads and installs the latest Timezone GeoJSON data"
@tag "[TzWorld]"

@aliases [o: :include_oceans]
@strict [include_oceans: :boolean]
@aliases [o: :include_oceans, f: :force]
@strict [include_oceans: :boolean, force: :boolean]

use Mix.Task
alias TzWorld.Downloader
require Logger

def run(args) do
case OptionParser.parse(args, aliases: @aliases, strict: @strict) do
{[include_oceans: include_oceans?], [], []} ->
update(include_oceans?)

{[], [], [{"--include_oceans", nil}]} ->
update(false)
{options, [], []} ->
include_oceans? = Keyword.get(options, :include_oceans, false)
force_update? = Keyword.get(options, :force, false)

{[], [], []} ->
update(false)
update(include_oceans?, force_update?)

_other ->
Mix.raise("Invalid arguments found. TzWorld.Update accepts only a single optional " <>
"boolean argument `--include_oceans`.", exit_status: 1)
Mix.raise(
"""
Invalid arguments found. `tzword.update` accepts the following:
--include-oceans
--no-include-oceans (default)
--force
--no-force (default)
""",
exit_status: 1)
end
end

def update(include_oceans?) do
Application.ensure_all_started(:tz_world)
Application.ensure_all_started(:inets)
Application.ensure_all_started(:ssl)
def update(include_oceans?, true = _force_update?) do
start_applications()

TzWorld.Backend.Memory.start_link
TzWorld.Backend.Dets.start_link
{latest_release, asset_url} = Downloader.latest_release(include_oceans?)
Downloader.get_latest_release(latest_release, asset_url)
end

def update(include_oceans?, false = _force_update?) do
start_applications()

case Downloader.current_release() do
{:ok, current_release} ->
Expand All @@ -68,4 +77,13 @@ defmodule Mix.Tasks.TzWorld.Update do
Downloader.get_latest_release(latest_release, asset_url)
end
end

defp start_applications do
Application.ensure_all_started(:tz_world)
Application.ensure_all_started(:inets)
Application.ensure_all_started(:ssl)

TzWorld.Backend.Memory.start_link
TzWorld.Backend.Dets.start_link
end
end
106 changes: 56 additions & 50 deletions lib/tz_world/backend/dets.ex
Original file line number Diff line number Diff line change
Expand Up @@ -43,63 +43,15 @@ defmodule TzWorld.Backend.Dets do
GenServer.call(__MODULE__, :reload_data, @timeout * 3)
end

@slots 1_000

@doc false
def filename do
TzWorld.GeoData.data_dir()
|> Path.join("timezones-geodata.dets")
|> String.to_charlist()
end
@doc false
defp dets_options do
[file: filename(), access: :read, estimated_no_objects: @slots]
end

@doc false
def get_geodata_table do
:dets.open_file(__MODULE__, dets_options())
end

@doc false
def save_dets_geodata do
dets_options = Keyword.put(dets_options(), :access, :read_write)

{:ok, __MODULE__} = :dets.open_file(__MODULE__, dets_options)
:ok = :dets.delete_all_objects(__MODULE__)

{:ok, geodata} = TzWorld.GeoData.load_compressed_data()
[version | shapes] = geodata

for shape <- shapes do
add_to_dets(__MODULE__, shape)
end

:ok = :dets.insert(__MODULE__, {@tz_world_version, version})
:dets.close(__MODULE__)
end

defp add_to_dets(t, shape) do
case shape.properties.bounding_box do
%Geo.Polygon{} = box ->
[[{x_min, y_max}, {_, y_min}, {x_max, _}, _]] = box.coordinates
:dets.insert(t, {{x_min, x_max, y_min, y_max}, shape})

polygons when is_list(polygons) ->
for box <- polygons do
[[{x_min, y_max}, {_, y_min}, {x_max, _}, _]] = box.coordinates
:dets.insert(t, {{x_min, x_max, y_min, y_max}, shape})
end
end
end

# --- Server callback implementation

@doc false
def handle_continue(:open_dets_file, _state) do
case get_geodata_table() do
{:error, {:file_error, _, :enoent}} ->
{:noreply, {:error, :enoent}}
{:error, {:not_closed, _path}} ->
raise "NOT CLOSED"
{:ok, __MODULE__} ->
{:noreply, {:ok, __MODULE__}}
end
Expand Down Expand Up @@ -145,6 +97,8 @@ defmodule TzWorld.Backend.Dets do
{:reply, get_geodata_table(), get_geodata_table()}
end

# --- Helpers

@doc false
defp find_zones(%Geo.Point{} = point) do
point
Expand Down Expand Up @@ -187,4 +141,56 @@ defmodule TzWorld.Backend.Dets do
}
]
end

@doc false
def filename do
TzWorld.GeoData.data_dir()
|> Path.join("timezones-geodata.dets")
|> String.to_charlist()
end

@doc false
def save_dets_geodata do
dets_options = Keyword.put(dets_options(), :access, :read_write)

{:ok, __MODULE__} = :dets.open_file(__MODULE__, dets_options)
:ok = :dets.delete_all_objects(__MODULE__)

{:ok, geodata} = TzWorld.GeoData.load_compressed_data()
[version | shapes] = geodata

for shape <- shapes do
add_to_dets(__MODULE__, shape)
end

:ok = :dets.insert(__MODULE__, {@tz_world_version, version})
:dets.close(__MODULE__)
end

defp add_to_dets(t, shape) do
case shape.properties.bounding_box do
%Geo.Polygon{} = box ->
[[{x_min, y_max}, {_, y_min}, {x_max, _}, _]] = box.coordinates
:dets.insert(t, {{x_min, x_max, y_min, y_max}, shape})

polygons when is_list(polygons) ->
for box <- polygons do
[[{x_min, y_max}, {_, y_min}, {x_max, _}, _]] = box.coordinates
:dets.insert(t, {{x_min, x_max, y_min, y_max}, shape})
end
end
end

@doc false
def get_geodata_table do
:dets.open_file(__MODULE__, dets_options())
end

@slots 1_000

@doc false
defp dets_options do
[file: filename(), access: :read, estimated_no_objects: @slots]
end

end
36 changes: 32 additions & 4 deletions lib/tz_world/downloader.ex
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,40 @@ defmodule TzWorld.Downloader do
Updates the timezone geo JSON data if there
is a more recent release.
## Arguments
* `options` is a keyword list of options. The
default is `[include_oceans: false, force: false]`.
## Options
* `:include_oceans` is a boolean that indicates whether
to include time zone data for the world's oceans. The
default is `false`.
* `:force` is a boolean that indicates whether to force
an update of the data, even if the current data is the
latest release. This option is useful when switching
from the data without oceans to the data with oceans
(or the other way arouond).
"""
def update_release do
def update_release(options \\ []) do
include_oceans? = Keyword.get(options, :include_oceans, false)
force_update? = Keyword.get(options, :force, false)

update_release(include_oceans?, force_update?)
end

def update_release(include_oceans?, true = _force_update?) do
{latest_release, asset_url} = latest_release(include_oceans?)
get_and_load_latest_release(latest_release, asset_url)
end

def update_release(include_oceans?, false = _force_update?) do
case current_release() do
{:ok, current_release} ->
{latest_release, asset_url} = latest_release()
{latest_release, asset_url} = latest_release(include_oceans?)

if latest_release > current_release do
get_and_load_latest_release(latest_release, asset_url)
Expand All @@ -57,7 +86,7 @@ defmodule TzWorld.Downloader do
end

{:error, :enoent} ->
{latest_release, asset_url} = latest_release()
{latest_release, asset_url} = latest_release(include_oceans?)
get_and_load_latest_release(latest_release, asset_url)

end
Expand All @@ -72,7 +101,6 @@ defmodule TzWorld.Downloader do
def get_latest_release(latest_release, asset_url) do
with {:ok, source_data} <- get_url(asset_url) do
GeoData.generate_compressed_data(source_data, latest_release)
TzWorld.Backend.Dets.start_link()
TzWorld.Backend.Dets.reload_timezone_data()
end
end
Expand Down
2 changes: 1 addition & 1 deletion mix.exs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ defmodule TzWorld.Mixfile do
use Mix.Project

@source_url "https://github.com/kimlai/tz_world"
@version "1.1.1"
@version "1.2.0"

def project do
[
Expand Down

0 comments on commit f23903e

Please sign in to comment.