Skip to content

Commit

Permalink
Simplify community resources and refactor events
Browse files Browse the repository at this point in the history
 - Move static community resources into Community.Resources and delegate
   to Resources.all/0 as resources/0 from Community
 - Community.Resources loads up the data from priv/data/community which
   faciliates easier contributions to community resources.
 - Fellows likewise also now loads and compiles it's data in at compile
   time from priv/data/fellows.exs
 - Added Erlef.data_path/1 and Erlef.get_data/1 to support loading of
   data from priv/data
 - Added Erlef.in_env?/1 to avoid redundancies in checking if the app
   is running in a particular set of environments
 - The Events context module has been merged into the Community context
 - Event and EventType now live in Community
 - Erlef.Query.Event is now Erlef.Community.Query
 - Make readme the top page in ex_doc
 - Added fake s3 server to ease development of events
 - Remove priv/events (obsolete)
 - Removed Erlef.Community.Event.changeset/2 (not in use)
 - Ignore lib/erlef/release.ex in coverage
 - Removed dead category helper from ErlefWeb.BlogView
 - Move Erlef.Seeds into priv/repo.seeds.exs
 - Append 127.0.0.1:9998 to trusted sources in ErlefWeb.Router so images
   uploaded to the fake s3 server can be viewed in dev mode.
 - Added misc tests for existing code
  • Loading branch information
starbelly committed Dec 14, 2020
1 parent 20bb86b commit a204de5
Show file tree
Hide file tree
Showing 65 changed files with 563 additions and 735 deletions.
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ This is where the body of the post goes.

### Contributing to the community section of the site

All data for the community page of the site can be found in [lib/erlef/community](lib/erlef/community).
All resources data for the community page of the site can be found in [priv/data/community](priv/data/community).

Before proceeding please:

Expand All @@ -95,17 +95,15 @@ Before proceeding please:

#### Adding an entry to an existing section

To add an entry to an existing section simply find the relevant module in [lib/erlef/community](lib/erlef/community)
and add a new entry in the form of a map.
To add an entry to an existing section simply find the relevant `.exs` file in [priv/data/community](priv/data/community) and add a new entry. That's it!

#### Adding a new section or sub-section

- Create a new module with a name that reflects the section of the site (e.g, languages, platforms, etc.) within
the [lib/erlef/community](lib/erlef/community) directory.
- Create a new `.exs` file in [priv/data/community](priv/data/community) with a name that reflects the section of the site (e.g, languages, platforms, etc.)

- See [lib/erlef/community/languages.ex](lib/erlef/community/languages.ex) as an example.
- See [priv/data/community/languages.exs](priv/data/community/languages.exs) as an example.

- Update [lib/erlef/community.ex](lib/erlef/community.ex) to make use of the new module.
- A new function should be able after you recompile `Erlef.Community.Resources` with the base name of the file you added prefixed with `all_` (e.g., `all_languages`). Likewise it will also be available in the main data map returned by the `all/0` function.

- Add the new section or sub-section
to [lib/erlef_web/templates/page/community.html.eex](lib/erlef_web/templates/page/community.html.eex).
Expand Down
11 changes: 9 additions & 2 deletions config/dev.exs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ config :erlef, ErlefWeb.Endpoint,
patterns: [
~r"priv/static/.*(js|css|png|jpeg|jpg|gif|svg)$",
~r"priv/gettext/.*(po)$",
~r"lib/erlef/community.ex$",
~r"priv/data/community/.*(exs)$",
~r"priv/data/.*(exs)$",
~r"lib/erlef/community/.*(ex)$",
~r"lib/erlef_web/{live,views}/.*(ex)$",
~r"lib/erlef_web/templates/.*(eex)$"
Expand Down Expand Up @@ -84,4 +85,10 @@ config :extwitter, :oauth,

config :ex_aws,
access_key_id: ["access_key_id", :instance_role],
secret_access_key: ["secret_access_key", :instance_role]
secret_access_key: ["secret_access_key", :instance_role],
s3: [
scheme: "http://",
region: "New Jersey",
host: "127.0.0.1",
port: 9998
]
10 changes: 10 additions & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@ config :erlef, :wild_apricot,
client_id: "client_id",
client_secret: "client_secret"

config :ex_aws,
access_key_id: ["access_key_id", :instance_role],
secret_access_key: ["secret_access_key", :instance_role],
s3: [
scheme: "http://",
region: "New Jersey",
host: "127.0.0.1",
port: 9998
]

config :erlef, Erlef.Repo,
database: "erlef_website_test",
username: "postgres",
Expand Down
1 change: 1 addition & 0 deletions coveralls.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"skip_files": [
"test",
"lib/erlef/release.ex",
"lib/erlef_web.ex",
"lib/erlef/application.ex",
"lib/erlef_web/endpoint.ex",
Expand Down
13 changes: 13 additions & 0 deletions lib/erlef.ex
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
defmodule Erlef do
@moduledoc false

@spec in_env?([atom()]) :: boolean()
def in_env?(envs), do: Application.get_env(:erlef, :env) in envs

@spec is_env?(atom) :: boolean()
def is_env?(env), do: Application.get_env(:erlef, :env) == env

def data_path(path) do
List.to_string(:code.priv_dir(:erlef)) <> "/data/#{path}"
end

def get_data(path) do
{term, _binding} = Code.eval_file(data_path(path))
term
end
end
2 changes: 1 addition & 1 deletion lib/erlef/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ defmodule Erlef.Application do

# The WildApricot model and cache server must be started first in dev/test
defp children_for(env) when env in [:dev, :test] do
[Erlef.Test.WildApricot, Erlef.WildApricot.Cache] ++ base_children()
[Erlef.Test.WildApricot, Erlef.WildApricot.Cache, Erlef.Test.S3] ++ base_children()
end

# The WildApricot Cache server should be started last when not in dev/test
Expand Down
94 changes: 89 additions & 5 deletions lib/erlef/community.ex
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,95 @@ defmodule Erlef.Community do
Context module for erlef community data.
"""

alias Erlef.Community.Languages
alias Ecto.{Changeset, UUID}
alias Erlef.Community.Query
alias Erlef.Community.{Event, EventType}
alias Erlef.Repo
alias Erlef.Community.Resources

def all() do
%{
languages: Languages.all()
}
defdelegate approved_events(), to: Query
defdelegate get_event(id), to: Query
defdelegate get_event_by_slug(slug), to: Query
defdelegate unapproved_events(), to: Query
defdelegate unapproved_events_count(), to: Query

@doc """
Returns a map of community resources, such as lists of languages, tools, etc.
Said map does not include database backed entities such as events.
"""

defdelegate all_resources(), to: Resources, as: :all

@spec approve_event(UUID.t(), map()) :: {:ok, Event.t()} | {:error, Changeset.t()}
def approve_event(id, params) do
event = Query.get_event(id)

event
|> Event.approval_changeset(params)
|> Repo.update()
end

def change_event(event), do: Erlef.Community.Event.new_changeset(event)

@spec event_types() :: [Keyword.t()]
def event_types do
EventType
|> Repo.all()
|> Enum.map(fn x -> [key: x.name, value: x.id] end)
end

@spec format_error(any()) :: String.t()
def format_error({:error, :unsupported_org_file_type}) do
"The event organization logo you attempted to upload is not supported." <>
" " <>
"Supported formats : jpg, png"
end

def format_error(_) do
"An unknown error ocurred"
end

@spec new_event() :: Changeset.t()
def new_event, do: Event.new_changeset(%Event{}, %{})

@spec new_event(map()) :: Changeset.t()
def new_event(params), do: Event.new_changeset(%Event{}, params)

@spec submit_event(params :: map()) :: {:ok, Event.t()} | {:error, term()}
def submit_event(params) do
with {:ok, event_params} <- maybe_upload_org_image(params),
%Changeset{} = cs <- Event.new_submission(event_params),
{:ok, cs} <- valid_changeset(cs) do
Repo.insert(cs)
end
end

defp maybe_upload_org_image(%{"organizer_brand_logo" => %Plug.Upload{} = upload} = params) do
with {:ok, organizer_brand_logo} <- File.read(upload.path),
{:ok, {ext, mime}} <- org_image_type(organizer_brand_logo),
{:ok, url} <- upload_event_org_image(organizer_brand_logo, ext, mime) do
{:ok, Map.put(params, "organizer_brand_logo", url)}
end
end

defp maybe_upload_org_image(params), do: {:ok, params}

defp org_image_type(<<0xFF, 0xD8, _::binary>>), do: {:ok, {".jpg", "image/jpeg"}}

defp org_image_type(<<0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A, _::binary>>),
do: {:ok, {".png", "image/png"}}

defp org_image_type(_not_supported), do: {:error, :unsupported_org_file_type}

defp upload_event_org_image(logo, ext, mime) do
uuid = Ecto.UUID.generate()
name = "#{uuid}#{ext}"
Erlef.Storage.upload_event_org_image(name, logo, content_type: mime)
end

defp valid_changeset(%Changeset{valid?: true} = cs) do
{:ok, cs}
end

defp valid_changeset(cs), do: {:error, cs}
end
16 changes: 4 additions & 12 deletions lib/erlef/schema/event.ex → lib/erlef/community/event.ex
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
defmodule Erlef.Schema.Event do
defmodule Erlef.Community.Event do
@moduledoc """
Erlef.Schema.Event schema
Erlef.Community.Event schema
"""
use Erlef.Schema
alias Erlef.Community.EventType

@type t :: %__MODULE__{
id: Ecto.UUID.t(),
Expand Down Expand Up @@ -56,20 +57,11 @@ defmodule Erlef.Schema.Event do
field(:approved_by, Ecto.UUID)
field(:approved_at, :utc_datetime)

belongs_to(:event_type, Erlef.Schema.EventType)
belongs_to(:event_type, EventType)

timestamps()
end

@spec changeset(t(), map()) :: Ecto.Changeset.t()
def changeset(struct, params \\ %{}) do
struct
|> cast(params, @all_fields ++ [:approved_by, :approved_at])
|> validate_required(@required_fields ++ [:approved_by, :approved_at])
|> unique_constraint(:title)
|> maybe_generate_slug()
end

def new_changeset(struct, params \\ %{}) do
struct
|> cast(params, @all_fields ++ [:approved_by])
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
defmodule Erlef.Schema.EventType do
defmodule Erlef.Community.EventType do
@moduledoc """
Erlef.Schema.EventType schema
Erlef.Community.EventType schema
"""
use Ecto.Schema
import Ecto.Changeset
Expand Down
24 changes: 12 additions & 12 deletions lib/erlef/query/event.ex → lib/erlef/community/query.ex
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
defmodule Erlef.Query.Event do
defmodule Erlef.Community.Query do
@moduledoc """
Module for the event queries
"""
import Ecto.Query, only: [from: 2]

alias Erlef.Schema.Event
alias Erlef.Community.Event
alias Erlef.Repo

@doc """
Returns a event given a valid id
"""
@spec get(id :: Ecto.UUID.t(), repo :: Ecto.Repo.t()) :: Event.t() | nil
def get(id, repo \\ Repo) do
@spec get_event(id :: Ecto.UUID.t(), repo :: Ecto.Repo.t()) :: Event.t() | nil
def get_event(id, repo \\ Repo) do
repo.get(Event, id)
end

@doc """
Returns a event by slug.
"""
@spec get_by_slug(slug :: binary(), repo :: Ecto.Repo.t()) :: Event.t() | nil
def get_by_slug(slug, repo \\ Repo) do
@spec get_event_by_slug(slug :: binary(), repo :: Ecto.Repo.t()) :: Event.t() | nil
def get_event_by_slug(slug, repo \\ Repo) do
from(p in Event,
where: p.slug == ^slug,
preload: [:event_type],
Expand All @@ -31,8 +31,8 @@ defmodule Erlef.Query.Event do
@doc """
Returns all approved events
"""
@spec approved(repo :: Ecto.Repo.t()) :: [Event.t()]
def approved(repo \\ Repo) do
@spec approved_events(repo :: Ecto.Repo.t()) :: [Event.t()]
def approved_events(repo \\ Repo) do
from(p in Event,
where: p.approved,
where: p.start >= ^Date.utc_today(),
Expand All @@ -45,8 +45,8 @@ defmodule Erlef.Query.Event do
@doc """
Returns all unapproved events
"""
@spec unapproved(repo :: Ecto.Repo.t()) :: [Event.t()]
def unapproved(repo \\ Repo) do
@spec unapproved_events(repo :: Ecto.Repo.t()) :: [Event.t()]
def unapproved_events(repo \\ Repo) do
from(p in Event,
where: p.approved == false,
order_by: [p.inserted_at],
Expand All @@ -55,8 +55,8 @@ defmodule Erlef.Query.Event do
|> repo.all()
end

@spec unapproved_count(repo :: Ecto.Repo.t()) :: [Event.t()]
def unapproved_count(repo \\ Repo) do
@spec unapproved_events_count(repo :: Ecto.Repo.t()) :: [Event.t()]
def unapproved_events_count(repo \\ Repo) do
from(p in Event,
where: p.approved == false,
select: count(p.id)
Expand Down
36 changes: 36 additions & 0 deletions lib/erlef/community/resources.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
defmodule Erlef.Community.Resources do
@moduledoc """
Module for getting static community data
All resources data for the community page of the site can be found in
[priv/data/community](priv/data/community).
Said data ends up compiled into this module along with a few dynamically generated helper functions.
As an example,we have all active languages in
`priv/data/community/languages.exs`,
this file ends up being evalulated and the base name of the file without the
extension (i.e., `"languages"`) ends up being used to create a helper function called `all_languages/0`.
Like wise the atom `languages` is also used as a key pointing to the evalulated term as returned by the `all/0` function.
"""

data =
for d <- "priv/data/community/*.exs" |> Path.wildcard() |> Enum.sort() do
@external_resource d
base_name = Path.basename(d) |> String.replace(".exs", "")
name = String.to_atom(base_name)
fn_name = String.to_atom("all_#{base_name}")
{evaled, _} = Code.eval_file(d)
val = Macro.escape(evaled)

def unquote(fn_name)() do
unquote(val)
end

{name, evaled}
end

@data Enum.reduce(data, %{}, fn {k, v}, acc -> Map.put(acc, k, v) end)

def all() do
@data
end
end
Loading

0 comments on commit a204de5

Please sign in to comment.