mirror of
https://github.com/philomena-dev/philomena.git
synced 2024-11-23 20:18:00 +01:00
elastix and elasticsearch binding
This commit is contained in:
parent
962c681c8e
commit
aee3bbccc4
20 changed files with 585 additions and 19 deletions
|
@ -9,6 +9,7 @@ use Mix.Config
|
|||
|
||||
config :philomena,
|
||||
ecto_repos: [Philomena.Repo],
|
||||
elasticsearch_url: "http://localhost:9200",
|
||||
password_pepper: "dn2e0EpZrvBLoxUM3gfQveBhjf0bG/6/bYhrOyq3L3hV9hdo/bimJ+irbDWsuXLP",
|
||||
image_url_root: "/img"
|
||||
|
||||
|
@ -21,6 +22,9 @@ config :philomena, :pow,
|
|||
config :bcrypt_elixir,
|
||||
log_rounds: 12
|
||||
|
||||
config :elastix,
|
||||
json_codec: Jason
|
||||
|
||||
# Configures the endpoint
|
||||
config :philomena, PhilomenaWeb.Endpoint,
|
||||
url: [host: "localhost"],
|
||||
|
|
|
@ -3,7 +3,6 @@ defmodule Philomena.Comments.Comment do
|
|||
import Ecto.Changeset
|
||||
|
||||
schema "comments" do
|
||||
|
||||
timestamps()
|
||||
end
|
||||
|
||||
|
|
99
lib/philomena/elasticsearch.ex
Normal file
99
lib/philomena/elasticsearch.ex
Normal file
|
@ -0,0 +1,99 @@
|
|||
defmodule Philomena.Elasticsearch do
|
||||
defmacro __using__(opts) do
|
||||
definition = Keyword.fetch!(opts, :definition)
|
||||
index_name = Keyword.fetch!(opts, :index_name)
|
||||
|
||||
elastic_url = Application.get_env(:philomena, :elasticsearch_url)
|
||||
|
||||
quote do
|
||||
alias Philomena.Repo
|
||||
import Ecto.Query, warn: false
|
||||
|
||||
def create_index! do
|
||||
Elastix.Index.create(
|
||||
unquote(elastic_url),
|
||||
unquote(index_name),
|
||||
unquote(definition).mapping()
|
||||
)
|
||||
end
|
||||
|
||||
def delete_index! do
|
||||
Elastix.Index.delete(unquote(elastic_url), unquote(index_name))
|
||||
end
|
||||
|
||||
def index_document(doc) do
|
||||
data = unquote(definition).as_json(doc)
|
||||
|
||||
Elastix.Document.index(unquote(elastic_url), unquote(index_name), ["_doc"], data.id, data)
|
||||
end
|
||||
|
||||
def reindex(ecto_query, batch_size \\ 1000) do
|
||||
ids =
|
||||
ecto_query
|
||||
|> exclude(:preload)
|
||||
|> exclude(:order_by)
|
||||
|> order_by(asc: :id)
|
||||
|> select([m], m.id)
|
||||
|> limit(^batch_size)
|
||||
|> Repo.all()
|
||||
|
||||
reindex(ecto_query, batch_size, ids)
|
||||
end
|
||||
|
||||
def reindex(ecto_query, batch_size, []), do: nil
|
||||
|
||||
def reindex(ecto_query, batch_size, ids) do
|
||||
lines =
|
||||
ecto_query
|
||||
|> where([m], m.id in ^ids)
|
||||
|> Repo.all()
|
||||
|> Enum.flat_map(fn m ->
|
||||
doc = unquote(definition).as_json(m)
|
||||
|
||||
[
|
||||
%{index: %{_index: unquote(index_name), _type: "_doc", _id: doc.id}},
|
||||
doc
|
||||
]
|
||||
end)
|
||||
|
||||
Elastix.Bulk.post(unquote(elastic_url), lines,
|
||||
index: unquote(index_name),
|
||||
httpoison_options: [timeout: 30_000]
|
||||
)
|
||||
|
||||
ids =
|
||||
ecto_query
|
||||
|> exclude(:preload)
|
||||
|> exclude(:order_by)
|
||||
|> order_by(asc: :id)
|
||||
|> where([m], m.id > ^Enum.max(ids))
|
||||
|> select([m], m.id)
|
||||
|> limit(^batch_size)
|
||||
|> Repo.all()
|
||||
|
||||
reindex(ecto_query, batch_size, ids)
|
||||
end
|
||||
|
||||
def search_results(elastic_query) do
|
||||
{:ok, %{body: results, status_code: 200}} =
|
||||
Elastix.Search.search(
|
||||
unquote(elastic_url),
|
||||
unquote(index_name),
|
||||
["_doc"],
|
||||
elastic_query
|
||||
)
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def search_records(elastic_query, ecto_query \\ __MODULE__) do
|
||||
results = search_results(elastic_query)
|
||||
|
||||
ids = results["hits"]["hits"] |> Enum.map(&String.to_integer(&1["_id"]))
|
||||
records = ecto_query |> where([m], m.id in ^ids) |> Repo.all()
|
||||
|
||||
records |> Enum.sort_by(&Enum.find_index(ids, fn el -> el == &1.id end))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
104
lib/philomena/galleries.ex
Normal file
104
lib/philomena/galleries.ex
Normal file
|
@ -0,0 +1,104 @@
|
|||
defmodule Philomena.Galleries do
|
||||
@moduledoc """
|
||||
The Galleries context.
|
||||
"""
|
||||
|
||||
import Ecto.Query, warn: false
|
||||
alias Philomena.Repo
|
||||
|
||||
alias Philomena.Galleries.Gallery
|
||||
|
||||
@doc """
|
||||
Returns the list of galleries.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> list_galleries()
|
||||
[%Gallery{}, ...]
|
||||
|
||||
"""
|
||||
def list_galleries do
|
||||
Repo.all(Gallery)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Gets a single gallery.
|
||||
|
||||
Raises `Ecto.NoResultsError` if the Gallery does not exist.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> get_gallery!(123)
|
||||
%Gallery{}
|
||||
|
||||
iex> get_gallery!(456)
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_gallery!(id), do: Repo.get!(Gallery, id)
|
||||
|
||||
@doc """
|
||||
Creates a gallery.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> create_gallery(%{field: value})
|
||||
{:ok, %Gallery{}}
|
||||
|
||||
iex> create_gallery(%{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def create_gallery(attrs \\ %{}) do
|
||||
%Gallery{}
|
||||
|> Gallery.changeset(attrs)
|
||||
|> Repo.insert()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Updates a gallery.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> update_gallery(gallery, %{field: new_value})
|
||||
{:ok, %Gallery{}}
|
||||
|
||||
iex> update_gallery(gallery, %{field: bad_value})
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def update_gallery(%Gallery{} = gallery, attrs) do
|
||||
gallery
|
||||
|> Gallery.changeset(attrs)
|
||||
|> Repo.update()
|
||||
end
|
||||
|
||||
@doc """
|
||||
Deletes a Gallery.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> delete_gallery(gallery)
|
||||
{:ok, %Gallery{}}
|
||||
|
||||
iex> delete_gallery(gallery)
|
||||
{:error, %Ecto.Changeset{}}
|
||||
|
||||
"""
|
||||
def delete_gallery(%Gallery{} = gallery) do
|
||||
Repo.delete(gallery)
|
||||
end
|
||||
|
||||
@doc """
|
||||
Returns an `%Ecto.Changeset{}` for tracking gallery changes.
|
||||
|
||||
## Examples
|
||||
|
||||
iex> change_gallery(gallery)
|
||||
%Ecto.Changeset{source: %Gallery{}}
|
||||
|
||||
"""
|
||||
def change_gallery(%Gallery{} = gallery) do
|
||||
Gallery.changeset(gallery, %{})
|
||||
end
|
||||
end
|
24
lib/philomena/galleries/gallery.ex
Normal file
24
lib/philomena/galleries/gallery.ex
Normal file
|
@ -0,0 +1,24 @@
|
|||
defmodule Philomena.Galleries.Gallery do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "galleries" do
|
||||
belongs_to :thumbnail, Philomena.Images.Image, source: :thumbnail_id
|
||||
belongs_to :creator, Philomena.Users.User, source: :creator_id
|
||||
|
||||
field :title, :string
|
||||
field :spoiler_warning, :string
|
||||
field :description, :string
|
||||
field :image_count, :integer
|
||||
field :order_position_asc, :boolean
|
||||
|
||||
timestamps(inserted_at: :created_at)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(gallery, attrs) do
|
||||
gallery
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
18
lib/philomena/galleries/interaction.ex
Normal file
18
lib/philomena/galleries/interaction.ex
Normal file
|
@ -0,0 +1,18 @@
|
|||
defmodule Philomena.Galleries.Interaction do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "gallery_interactions" do
|
||||
belongs_to :gallery, Philomena.Galleries.Gallery
|
||||
belongs_to :image, Philomena.Images.Image
|
||||
|
||||
field :position, :integer
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(interaction, attrs) do
|
||||
interaction
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
147
lib/philomena/images/elasticsearch.ex
Normal file
147
lib/philomena/images/elasticsearch.ex
Normal file
|
@ -0,0 +1,147 @@
|
|||
defmodule Philomena.Images.Elasticsearch do
|
||||
def mapping do
|
||||
%{
|
||||
settings: %{
|
||||
index: %{
|
||||
number_of_shards: 5,
|
||||
max_result_window: 10_000_000
|
||||
}
|
||||
},
|
||||
mappings: %{
|
||||
_doc: %{
|
||||
_all: %{enabled: false},
|
||||
dynamic: false,
|
||||
properties: %{
|
||||
anonymous: %{type: "boolean"},
|
||||
aspect_ratio: %{type: "float"},
|
||||
comment_count: %{type: "integer"},
|
||||
commenters: %{type: "keyword"},
|
||||
created_at: %{type: "date"},
|
||||
deleted_by_user: %{type: "keyword"},
|
||||
deleted_by_user_id: %{type: "keyword"},
|
||||
deletion_reason: %{type: "text", analyzer: "snowball"},
|
||||
description: %{type: "text", analyzer: "snowball"},
|
||||
downvoter_ids: %{type: "keyword"},
|
||||
downvoters: %{type: "keyword"},
|
||||
downvotes: %{type: "integer"},
|
||||
faves: %{type: "integer"},
|
||||
favourited_by_user_ids: %{type: "keyword"},
|
||||
favourited_by_users: %{type: "keyword"},
|
||||
file_name: %{type: "keyword"},
|
||||
fingerprint: %{type: "keyword"},
|
||||
first_seen_at: %{type: "date"},
|
||||
height: %{type: "integer"},
|
||||
hidden_by_user_ids: %{type: "keyword"},
|
||||
hidden_by_users: %{type: "keyword"},
|
||||
hidden_from_users: %{type: "keyword"},
|
||||
id: %{type: "integer"},
|
||||
ip: %{type: "ip"},
|
||||
mime_type: %{type: "keyword"},
|
||||
orig_sha512_hash: %{type: "keyword"},
|
||||
original_format: %{type: "keyword"},
|
||||
score: %{type: "integer"},
|
||||
sha512_hash: %{type: "keyword"},
|
||||
source_url: %{type: "keyword"},
|
||||
tag_count: %{type: "integer"},
|
||||
tag_ids: %{type: "keyword"},
|
||||
tags: %{type: "text", analyzer: "keyword"},
|
||||
true_uploader: %{type: "keyword"},
|
||||
true_uploader_id: %{type: "keyword"},
|
||||
updated_at: %{type: "date"},
|
||||
uploader: %{type: "keyword"},
|
||||
uploader_id: %{type: "keyword"},
|
||||
upvoter_ids: %{type: "keyword"},
|
||||
upvoters: %{type: "keyword"},
|
||||
upvotes: %{type: "integer"},
|
||||
user_id: %{type: "keyword"},
|
||||
width: %{type: "integer"},
|
||||
wilson_score: %{type: "float"},
|
||||
galleries: %{
|
||||
type: "nested",
|
||||
properties: %{
|
||||
id: %{type: "integer"},
|
||||
position: %{type: "integer"}
|
||||
}
|
||||
},
|
||||
namespaced_tags: %{
|
||||
properties: %{
|
||||
name: %{type: "keyword"},
|
||||
name_in_namespace: %{type: "keyword"},
|
||||
namespace: %{type: "keyword"}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
|
||||
def as_json(image) do
|
||||
%{
|
||||
id: image.id,
|
||||
upvotes: image.upvotes_count,
|
||||
downvotes: image.downvotes_count,
|
||||
score: image.score,
|
||||
faves: image.faves_count,
|
||||
comment_count: image.comments_count,
|
||||
width: image.image_width,
|
||||
height: image.image_height,
|
||||
tag_count: length(image.tags),
|
||||
aspect_ratio: image.image_aspect_ratio,
|
||||
wilson_score: wilson_score(image),
|
||||
created_at: image.created_at,
|
||||
updated_at: image.updated_at,
|
||||
first_seen_at: image.first_seen_at,
|
||||
ip: image.ip |> to_string,
|
||||
tag_ids: image.tags |> Enum.map(& &1.id),
|
||||
mime_type: image.image_mime_type,
|
||||
uploader: if(!!image.user and !image.anonymous, do: String.downcase(image.user.name)),
|
||||
true_uploader: if(!!image.user, do: String.downcase(image.user.name)),
|
||||
source_url: image.source_url |> to_string |> String.downcase(),
|
||||
file_name: image.image_name,
|
||||
original_format: image.image_format,
|
||||
fingerprint: image.fingerprint,
|
||||
uploader_id: if(!!image.user_id and !image.anonymous, do: image.user_id),
|
||||
true_uploader_id: image.user_id,
|
||||
orig_sha512_hash: image.image_orig_sha512_hash,
|
||||
sha512_hash: image.image_sha512_hash,
|
||||
hidden_from_users: image.hidden_from_users,
|
||||
anonymous: image.anonymous,
|
||||
description: image.description,
|
||||
deletion_reason: image.deletion_reason,
|
||||
favourited_by_user_ids: image.favers |> Enum.map(& &1.id),
|
||||
hidden_by_user_ids: image.hiders |> Enum.map(& &1.id),
|
||||
upvoter_ids: image.upvoters |> Enum.map(& &1.id),
|
||||
downvoter_ids: image.downvoters |> Enum.map(& &1.id),
|
||||
deleted_by_user_id: image.deleter_id,
|
||||
galleries:
|
||||
image.gallery_interactions |> Enum.map(&%{id: &1.gallery_id, position: &1.position}),
|
||||
namespaced_tags: %{
|
||||
name: image.tags |> Enum.flat_map(&([&1] ++ &1.aliases)) |> Enum.map(& &1.name)
|
||||
},
|
||||
favourited_by_users: image.favers |> Enum.map(&String.downcase(&1.name)),
|
||||
hidden_by_users: image.hiders |> Enum.map(&String.downcase(&1.name)),
|
||||
upvoters: image.upvoters |> Enum.map(&String.downcase(&1.name)),
|
||||
downvoters: image.downvoters |> Enum.map(&String.downcase(&1.name)),
|
||||
deleted_by_user: if(!!image.deleter, do: image.deleter.name)
|
||||
}
|
||||
end
|
||||
|
||||
def wilson_score(%{upvotes_count: upvotes, downvotes_count: downvotes})
|
||||
when upvotes > 0 or downvotes > 0 do
|
||||
# Population size
|
||||
n = (upvotes + downvotes) / 1
|
||||
|
||||
# Success proportion
|
||||
p_hat = upvotes / n
|
||||
|
||||
# z and z^2 values for CI upper 99.5%
|
||||
z = 2.57583
|
||||
z2 = 6.634900189
|
||||
|
||||
(p_hat + z2 / (2 * n) - z * :math.sqrt((p_hat * (1 - p_hat) + z2 / (4 * n)) / n)) /
|
||||
(1 + z2 / n)
|
||||
end
|
||||
|
||||
def wilson_score(_), do: 0
|
||||
end
|
19
lib/philomena/images/fave.ex
Normal file
19
lib/philomena/images/fave.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule Philomena.Images.Fave do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key false
|
||||
|
||||
schema "image_faves" do
|
||||
belongs_to :user, Philomena.Users.User, primary_key: true
|
||||
belongs_to :image, Philomena.Images.Image, primary_key: true
|
||||
timestamps(inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(fave, attrs) do
|
||||
fave
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
19
lib/philomena/images/hide.ex
Normal file
19
lib/philomena/images/hide.ex
Normal file
|
@ -0,0 +1,19 @@
|
|||
defmodule Philomena.Images.Hide do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key false
|
||||
|
||||
schema "image_hides" do
|
||||
belongs_to :user, Philomena.Users.User, primary_key: true
|
||||
belongs_to :image, Philomena.Images.Image, primary_key: true
|
||||
timestamps(inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(hide, attrs) do
|
||||
hide
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
|
@ -1,11 +1,26 @@
|
|||
defmodule Philomena.Images.Image do
|
||||
use Ecto.Schema
|
||||
|
||||
use Philomena.Elasticsearch,
|
||||
definition: Philomena.Images.Elasticsearch,
|
||||
index_name: "images"
|
||||
|
||||
import Ecto.Changeset
|
||||
|
||||
schema "images" do
|
||||
belongs_to :user, Philomena.Users.User
|
||||
belongs_to :deleter, Philomena.Users.User, source: :deleted_by_id
|
||||
many_to_many :tags, Philomena.Tags.Tag, join_through: "image_taggings"
|
||||
has_many :upvotes, Philomena.Images.Vote, where: [up: true]
|
||||
has_many :downvotes, Philomena.Images.Vote, where: [up: false]
|
||||
has_many :faves, Philomena.Images.Fave
|
||||
has_many :hides, Philomena.Images.Hide
|
||||
has_many :taggings, Philomena.Images.Tagging
|
||||
has_many :gallery_interactions, Philomena.Galleries.Interaction
|
||||
has_many :tags, through: [:taggings, :tag]
|
||||
has_many :upvoters, through: [:upvotes, :user]
|
||||
has_many :downvoters, through: [:downvotes, :user]
|
||||
has_many :favers, through: [:faves, :user]
|
||||
has_many :hiders, through: [:hides, :user]
|
||||
|
||||
field :image, :string
|
||||
field :image_name, :string
|
||||
|
|
18
lib/philomena/images/tagging.ex
Normal file
18
lib/philomena/images/tagging.ex
Normal file
|
@ -0,0 +1,18 @@
|
|||
defmodule Philomena.Images.Tagging do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key false
|
||||
|
||||
schema "image_taggings" do
|
||||
belongs_to :image, Philomena.Images.Image, primary_key: true
|
||||
belongs_to :tag, Philomena.Tags.Tag, primary_key: true
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(tagging, attrs) do
|
||||
tagging
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
20
lib/philomena/images/vote.ex
Normal file
20
lib/philomena/images/vote.ex
Normal file
|
@ -0,0 +1,20 @@
|
|||
defmodule Philomena.Images.Vote do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
@primary_key false
|
||||
|
||||
schema "image_votes" do
|
||||
belongs_to :user, Philomena.Users.User, primary_key: true
|
||||
belongs_to :image, Philomena.Images.Image, primary_key: true
|
||||
field :up, :boolean
|
||||
timestamps(inserted_at: :created_at, updated_at: false)
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(vote, attrs) do
|
||||
vote
|
||||
|> cast(attrs, [])
|
||||
|> validate_required([])
|
||||
end
|
||||
end
|
|
@ -3,6 +3,9 @@ defmodule Philomena.Tags.Tag do
|
|||
import Ecto.Changeset
|
||||
|
||||
schema "tags" do
|
||||
belongs_to :aliased_tag, Philomena.Tags.Tag, source: :aliased_tag_id
|
||||
has_many :aliases, Philomena.Tags.Tag, foreign_key: :aliased_tag_id
|
||||
|
||||
field :slug, :string
|
||||
field :name, :string
|
||||
field :category, :string
|
||||
|
|
|
@ -26,19 +26,20 @@ defmodule PhilomenaWeb.AppView do
|
|||
months = abs(div(days, 30))
|
||||
years = abs(div(days, 365))
|
||||
|
||||
words = cond do
|
||||
seconds < 45 -> String.replace(@time_strings[:seconds], "%d", to_string(seconds))
|
||||
seconds < 90 -> String.replace(@time_strings[:minute], "%d", to_string(1))
|
||||
minutes < 45 -> String.replace(@time_strings[:minutes], "%d", to_string(minutes))
|
||||
minutes < 90 -> String.replace(@time_strings[:hour], "%d", to_string(1))
|
||||
hours < 24 -> String.replace(@time_strings[:hours], "%d", to_string(hours))
|
||||
hours < 42 -> String.replace(@time_strings[:day], "%d", to_string(1))
|
||||
days < 30 -> String.replace(@time_strings[:days], "%d", to_string(days))
|
||||
days < 45 -> String.replace(@time_strings[:month], "%d", to_string(1))
|
||||
days < 365 -> String.replace(@time_strings[:months], "%d", to_string(months))
|
||||
days < 548 -> String.replace(@time_strings[:year], "%d", to_string(1))
|
||||
true -> String.replace(@time_strings[:years], "%d", to_string(years))
|
||||
end
|
||||
words =
|
||||
cond do
|
||||
seconds < 45 -> String.replace(@time_strings[:seconds], "%d", to_string(seconds))
|
||||
seconds < 90 -> String.replace(@time_strings[:minute], "%d", to_string(1))
|
||||
minutes < 45 -> String.replace(@time_strings[:minutes], "%d", to_string(minutes))
|
||||
minutes < 90 -> String.replace(@time_strings[:hour], "%d", to_string(1))
|
||||
hours < 24 -> String.replace(@time_strings[:hours], "%d", to_string(hours))
|
||||
hours < 42 -> String.replace(@time_strings[:day], "%d", to_string(1))
|
||||
days < 30 -> String.replace(@time_strings[:days], "%d", to_string(days))
|
||||
days < 45 -> String.replace(@time_strings[:month], "%d", to_string(1))
|
||||
days < 365 -> String.replace(@time_strings[:months], "%d", to_string(months))
|
||||
days < 548 -> String.replace(@time_strings[:year], "%d", to_string(1))
|
||||
true -> String.replace(@time_strings[:years], "%d", to_string(years))
|
||||
end
|
||||
|
||||
content_tag(:time, "#{words} #{relation}", datetime: time |> NaiveDateTime.to_iso8601())
|
||||
end
|
||||
|
|
3
mix.exs
3
mix.exs
|
@ -48,7 +48,8 @@ defmodule Philomena.MixProject do
|
|||
{:pow, "~> 1.0.11"},
|
||||
{:bcrypt_elixir, "~> 2.0"},
|
||||
{:pot, "~> 0.10.1"},
|
||||
{:secure_compare, "~> 0.1.0"}
|
||||
{:secure_compare, "~> 0.1.0"},
|
||||
{:elastix, "~> 0.7.1"}
|
||||
]
|
||||
end
|
||||
|
||||
|
|
11
mix.lock
11
mix.lock
|
@ -1,5 +1,6 @@
|
|||
%{
|
||||
"bcrypt_elixir": {:hex, :bcrypt_elixir, "2.0.3", "64e0792d5b5064391927bf3b8e436994cafd18ca2d2b76dea5c76e0adcf66b7c", [:make, :mix], [{:comeonin, "~> 5.1", [hex: :comeonin, repo: "hexpm", optional: false]}, {:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"certifi": {:hex, :certifi, "2.5.1", "867ce347f7c7d78563450a18a6a28a8090331e77fa02380b4a21962a65d36ee5", [:rebar3], [{:parse_trans, "~>3.3", [hex: :parse_trans, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"comeonin": {:hex, :comeonin, "5.1.2", "fbbbbbfcf0f0e9900c0336d16c8d462edf838ba1759577e29cc5fbd7c28a4540", [:mix], [], "hexpm"},
|
||||
"connection": {:hex, :connection, "1.0.4", "a1cae72211f0eef17705aaededacac3eb30e6625b04a6117c1b2db6ace7d5976", [:mix], [], "hexpm"},
|
||||
"cowboy": {:hex, :cowboy, "2.6.3", "99aa50e94e685557cad82e704457336a453d4abcb77839ad22dbe71f311fcc06", [:rebar3], [{:cowlib, "~> 2.7.3", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "~> 1.7.1", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -9,12 +10,19 @@
|
|||
"ecto": {:hex, :ecto, "3.1.7", "fa21d06ef56cdc2fdaa62574e8c3ba34a2751d44ea34c30bc65f0728421043e5", [:mix], [{:decimal, "~> 1.6", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ecto_network": {:hex, :ecto_network, "1.1.0", "7062004b9324ff13e50c02dab84877f8a55e06db9eabbf2d04bda21da6fc6e8a", [:mix], [{:ecto_sql, ">= 3.0.0", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 0.0.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.14.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ecto_sql": {:hex, :ecto_sql, "3.1.6", "1e80e30d16138a729c717f73dcb938590bcdb3a4502f3012414d0cbb261045d8", [:mix], [{:db_connection, "~> 2.0", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.1.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:mariaex, "~> 0.9.1", [hex: :mariaex, repo: "hexpm", optional: true]}, {:myxql, "~> 0.2.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.14.0 or ~> 0.15.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elastix": {:hex, :elastix, "0.7.1", "8e199a764a0bc018e0a97afeea950a8069b988867d87f8d25ae121d8b3288612", [:mix], [{:httpoison, "~> 1.4", [hex: :httpoison, repo: "hexpm", optional: false]}, {:poison, "~> 3.1", [hex: :poison, repo: "hexpm", optional: true]}, {:retry, "~> 0.8", [hex: :retry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"elixir_make": {:hex, :elixir_make, "0.6.0", "38349f3e29aff4864352084fc736fa7fa0f2995a819a737554f7ebd28b85aaab", [:mix], [], "hexpm"},
|
||||
"file_system": {:hex, :file_system, "0.2.7", "e6f7f155970975789f26e77b8b8d8ab084c59844d8ecfaf58cbda31c494d14aa", [:mix], [], "hexpm"},
|
||||
"gettext": {:hex, :gettext, "0.17.0", "abe21542c831887a2b16f4c94556db9c421ab301aee417b7c4fbde7fbdbe01ec", [:mix], [], "hexpm"},
|
||||
"hackney": {:hex, :hackney, "1.15.1", "9f8f471c844b8ce395f7b6d8398139e26ddca9ebc171a8b91342ee15a19963f4", [:rebar3], [{:certifi, "2.5.1", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "6.0.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "1.0.1", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~>1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "1.1.4", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"httpoison": {:hex, :httpoison, "1.5.1", "0f55b5b673b03c5c327dac7015a67cb571b99b631acc0bc1b0b98dcd6b9f2104", [:mix], [{:hackney, "~> 1.8", [hex: :hackney, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"idna": {:hex, :idna, "6.0.0", "689c46cbcdf3524c44d5f3dde8001f364cd7608a99556d8fbd8239a5798d4c10", [:rebar3], [{:unicode_util_compat, "0.4.1", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"jason": {:hex, :jason, "1.1.2", "b03dedea67a99223a2eaf9f1264ce37154564de899fd3d8b9a21b1a6fd64afe7", [:mix], [{:decimal, "~> 1.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm"},
|
||||
"mime": {:hex, :mime, "1.3.1", "30ce04ab3175b6ad0bdce0035cba77bba68b813d523d1aac73d9781b4d193cf8", [:mix], [], "hexpm"},
|
||||
"mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm"},
|
||||
"neotoma": {:hex, :neotoma, "1.7.3", "d8bd5404b73273989946e4f4f6d529e5c2088f5fa1ca790b4dbe81f4be408e61", [:rebar], [], "hexpm"},
|
||||
"parse_trans": {:hex, :parse_trans, "3.3.0", "09765507a3c7590a784615cfd421d101aec25098d50b89d7aa1d66646bc571c1", [:rebar3], [], "hexpm"},
|
||||
"pbkdf2": {:hex, :pbkdf2, "2.0.0", "11c23279fded5c0027ab3996cfae77805521d7ef4babde2bd7ec04a9086cf499", [:rebar3], [], "hexpm"},
|
||||
"phoenix": {:hex, :phoenix, "1.4.9", "746d098e10741c334d88143d3c94cab1756435f94387a63441792e66ec0ee974", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 1.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.1 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 1.0 or ~> 2.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"phoenix_ecto": {:hex, :phoenix_ecto, "4.0.0", "c43117a136e7399ea04ecaac73f8f23ee0ffe3e07acfcb8062fe5f4c9f0f6531", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.9", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
|
@ -29,7 +37,10 @@
|
|||
"pot": {:hex, :pot, "0.10.1", "af7dc220fd45478719b821fb4c1222975132516478483213507f95026298d8ab", [:rebar3], [], "hexpm"},
|
||||
"pow": {:hex, :pow, "1.0.11", "f5ef721ac17d2bf8f9dd92f5d40fa0b96512d24b91a26603147754034e3a6101", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.3.0 or ~> 1.4.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, ">= 2.0.0 and <= 3.0.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:plug, ">= 1.5.0 and < 2.0.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm"},
|
||||
"ranch": {:hex, :ranch, "1.7.1", "6b1fab51b49196860b733a49c07604465a47bdb78aa10c1c16a3d199f7f8c881", [:rebar3], [], "hexpm"},
|
||||
"retry": {:hex, :retry, "0.13.0", "bb9b2713f70f39337837852337ad280c77662574f4fb852a8386c269f3d734c4", [:mix], [], "hexpm"},
|
||||
"secure_compare": {:hex, :secure_compare, "0.1.0", "01b3c93c8edb696e8a5b38397ed48e10958c8a5ec740606656445bcbec0aadb8", [:mix], [], "hexpm"},
|
||||
"slime": {:hex, :slime, "1.2.0", "d46ede53c96b743dfdd23821268dc9b01f04ffea65d9d57c4e3d9200b162df02", [:mix], [{:neotoma, "~> 1.7", [hex: :neotoma, repo: "hexpm", optional: false]}], "hexpm"},
|
||||
"ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.4", "f0eafff810d2041e93f915ef59899c923f4568f4585904d010387ed74988e77b", [:make, :mix, :rebar3], [], "hexpm"},
|
||||
"telemetry": {:hex, :telemetry, "0.4.0", "8339bee3fa8b91cb84d14c2935f8ecf399ccd87301ad6da6b71c09553834b2ab", [:rebar3], [], "hexpm"},
|
||||
"unicode_util_compat": {:hex, :unicode_util_compat, "0.4.1", "d869e4c68901dd9531385bb0c8c40444ebf624e60b6962d95952775cac5e90cd", [:rebar3], [], "hexpm"},
|
||||
}
|
||||
|
|
62
test/philomena/galleries_test.exs
Normal file
62
test/philomena/galleries_test.exs
Normal file
|
@ -0,0 +1,62 @@
|
|||
defmodule Philomena.GalleriesTest do
|
||||
use Philomena.DataCase
|
||||
|
||||
alias Philomena.Galleries
|
||||
|
||||
describe "galleries" do
|
||||
alias Philomena.Galleries.Gallery
|
||||
|
||||
@valid_attrs %{}
|
||||
@update_attrs %{}
|
||||
@invalid_attrs %{}
|
||||
|
||||
def gallery_fixture(attrs \\ %{}) do
|
||||
{:ok, gallery} =
|
||||
attrs
|
||||
|> Enum.into(@valid_attrs)
|
||||
|> Galleries.create_gallery()
|
||||
|
||||
gallery
|
||||
end
|
||||
|
||||
test "list_galleries/0 returns all galleries" do
|
||||
gallery = gallery_fixture()
|
||||
assert Galleries.list_galleries() == [gallery]
|
||||
end
|
||||
|
||||
test "get_gallery!/1 returns the gallery with given id" do
|
||||
gallery = gallery_fixture()
|
||||
assert Galleries.get_gallery!(gallery.id) == gallery
|
||||
end
|
||||
|
||||
test "create_gallery/1 with valid data creates a gallery" do
|
||||
assert {:ok, %Gallery{} = gallery} = Galleries.create_gallery(@valid_attrs)
|
||||
end
|
||||
|
||||
test "create_gallery/1 with invalid data returns error changeset" do
|
||||
assert {:error, %Ecto.Changeset{}} = Galleries.create_gallery(@invalid_attrs)
|
||||
end
|
||||
|
||||
test "update_gallery/2 with valid data updates the gallery" do
|
||||
gallery = gallery_fixture()
|
||||
assert {:ok, %Gallery{} = gallery} = Galleries.update_gallery(gallery, @update_attrs)
|
||||
end
|
||||
|
||||
test "update_gallery/2 with invalid data returns error changeset" do
|
||||
gallery = gallery_fixture()
|
||||
assert {:error, %Ecto.Changeset{}} = Galleries.update_gallery(gallery, @invalid_attrs)
|
||||
assert gallery == Galleries.get_gallery!(gallery.id)
|
||||
end
|
||||
|
||||
test "delete_gallery/1 deletes the gallery" do
|
||||
gallery = gallery_fixture()
|
||||
assert {:ok, %Gallery{}} = Galleries.delete_gallery(gallery)
|
||||
assert_raise Ecto.NoResultsError, fn -> Galleries.get_gallery!(gallery.id) end
|
||||
end
|
||||
|
||||
test "change_gallery/1 returns a gallery changeset" do
|
||||
gallery = gallery_fixture()
|
||||
assert %Ecto.Changeset{} = Galleries.change_gallery(gallery)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -75,6 +75,7 @@ defmodule PhilomenaWeb.CommentControllerTest do
|
|||
test "deletes chosen comment", %{conn: conn, comment: comment} do
|
||||
conn = delete(conn, Routes.comment_path(conn, :delete, comment))
|
||||
assert redirected_to(conn) == Routes.comment_path(conn, :index)
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, Routes.comment_path(conn, :show, comment))
|
||||
end
|
||||
|
|
|
@ -75,6 +75,7 @@ defmodule PhilomenaWeb.TagControllerTest do
|
|||
test "deletes chosen tag", %{conn: conn, tag: tag} do
|
||||
conn = delete(conn, Routes.tag_path(conn, :delete, tag))
|
||||
assert redirected_to(conn) == Routes.tag_path(conn, :index)
|
||||
|
||||
assert_error_sent 404, fn ->
|
||||
get(conn, Routes.tag_path(conn, :show, tag))
|
||||
end
|
||||
|
|
|
@ -24,9 +24,9 @@ fi
|
|||
# Necessary for apt and elasticsearch to succeed
|
||||
install_packages apt-transport-https default-jre-headless
|
||||
|
||||
if [ ! -f /etc/apt/sources.list.d/elasticsearch-7.x.list ]; then
|
||||
if [ ! -f /etc/apt/sources.list.d/elasticsearch-6.x.list ]; then
|
||||
add_key https://packages.elastic.co/GPG-KEY-elasticsearch
|
||||
echo "deb https://artifacts.elastic.co/packages/7.x/apt stable main" > /etc/apt/sources.list.d/elasticsearch-7.x.list
|
||||
echo "deb https://artifacts.elastic.co/packages/6.x/apt stable main" > /etc/apt/sources.list.d/elasticsearch-6.x.list
|
||||
fi
|
||||
|
||||
if [ ! -f /etc/apt/sources.list.d/pgdg.list ]; then
|
||||
|
|
Loading…
Reference in a new issue