Add importing html and save it to Article
Additionally defines a wizard logic which is partially unused yet.
This commit is contained in:
@ -35,7 +35,10 @@ defmodule Outlook.Articles do
|
||||
** (Ecto.NoResultsError)
|
||||
|
||||
"""
|
||||
def get_article!(id), do: Repo.get!(Article, id)
|
||||
def get_article!(id) do
|
||||
Repo.get!(Article, id)
|
||||
|> Repo.preload([:author])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a article.
|
||||
|
||||
@ -2,11 +2,12 @@ defmodule Outlook.Articles.Article do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Outlook.Articles.InternalTree
|
||||
alias Outlook.Authors.Author
|
||||
alias Outlook.Translations.Translation
|
||||
|
||||
schema "articles" do
|
||||
field :content, :string
|
||||
field :content, InternalTree
|
||||
field :date, :utc_datetime
|
||||
field :language, :string
|
||||
field :title, :string
|
||||
@ -20,7 +21,7 @@ defmodule Outlook.Articles.Article do
|
||||
@doc false
|
||||
def changeset(article, attrs) do
|
||||
article
|
||||
|> cast(attrs, [:title, :content, :url, :language, :date])
|
||||
|> validate_required([:title, :content, :url, :language, :date])
|
||||
|> cast(attrs, [:title, :content, :url, :language, :date, :author_id])
|
||||
|> validate_required([:title, :content, :url, :language, :date, :author_id])
|
||||
end
|
||||
end
|
||||
|
||||
52
lib/outlook/articles/internal_tree.ex
Normal file
52
lib/outlook/articles/internal_tree.ex
Normal file
@ -0,0 +1,52 @@
|
||||
defmodule Outlook.Articles.InternalTree do
|
||||
use Ecto.Type
|
||||
|
||||
alias Outlook.InternalTree.InternalNode
|
||||
alias Outlook.InternalTree.TranslationUnit
|
||||
|
||||
def type, do: :string
|
||||
|
||||
def cast(tree) when is_list(tree) do
|
||||
{:ok, tree}
|
||||
end
|
||||
|
||||
def cast(_), do: :error
|
||||
|
||||
def load(tree) when is_binary(tree) do
|
||||
{:ok, Jason.decode!(tree, keys: :atoms!) |> from_json}
|
||||
end
|
||||
|
||||
def dump(tree) when is_list(tree), do: {:ok, Jason.encode!(tree)}
|
||||
def dump(_), do: :error
|
||||
|
||||
|
||||
defp from_json([%{status: _} = node | rest]) do
|
||||
[ %TranslationUnit{
|
||||
status: String.to_atom(node.status),
|
||||
uuid: node.uuid,
|
||||
content: node.content
|
||||
} | from_json(rest) ]
|
||||
end
|
||||
|
||||
defp from_json([%{type: "element"} = node | rest]) do
|
||||
[ %InternalNode{
|
||||
name: node.name,
|
||||
attributes: node.attributes,
|
||||
type: String.to_atom(node.type),
|
||||
uuid: node.uuid,
|
||||
content: from_json(node.content)
|
||||
} | from_json(rest) ]
|
||||
end
|
||||
|
||||
defp from_json([%{type: _} = node | rest]) do
|
||||
[ %InternalNode{
|
||||
name: node.name,
|
||||
attributes: node.attributes,
|
||||
type: String.to_atom(node.type),
|
||||
uuid: node.uuid,
|
||||
content: node.content
|
||||
} | from_json(rest) ]
|
||||
end
|
||||
|
||||
defp from_json([]), do: []
|
||||
end
|
||||
@ -37,6 +37,11 @@ defmodule Outlook.Authors do
|
||||
"""
|
||||
def get_author!(id), do: Repo.get!(Author, id)
|
||||
|
||||
def get_author_with_articles!(id) do
|
||||
Repo.get!(Author, id)
|
||||
|> Repo.preload([:articles])
|
||||
end
|
||||
|
||||
@doc """
|
||||
Creates a author.
|
||||
|
||||
|
||||
@ -18,6 +18,6 @@ defmodule Outlook.Authors.Author do
|
||||
def changeset(author, attrs) do
|
||||
author
|
||||
|> cast(attrs, [:name, :description, :homepage_name, :homepage_url])
|
||||
|> validate_required([:name, :description, :homepage_name, :homepage_url])
|
||||
|> validate_required([:name, :description])
|
||||
end
|
||||
end
|
||||
|
||||
@ -11,4 +11,10 @@ defmodule Outlook.HtmlPreparations do
|
||||
|> HtmlPreparation.floki_to_internal
|
||||
|> HtmlPreparation.set_sibling_with
|
||||
end
|
||||
|
||||
def get_tree_items(content_tree) do
|
||||
content_tree
|
||||
|> HtmlPreparation.strip_whitespace_textnodes
|
||||
|> HtmlPreparation.build_indentation_list(0)
|
||||
end
|
||||
end
|
||||
|
||||
@ -62,4 +62,40 @@ defmodule Outlook.HtmlPreparations.HtmlPreparation do
|
||||
end
|
||||
|
||||
def set_sibling_with([ ]), do: ( [ ] )
|
||||
|
||||
def strip_whitespace_textnodes [ %{type: :text} = node | rest] do
|
||||
if Regex.match?(~r/^\s*$/, node.content) do
|
||||
strip_whitespace_textnodes(rest)
|
||||
else
|
||||
[ node | strip_whitespace_textnodes(rest)]
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
def strip_whitespace_textnodes [ %{type: :element} = node | rest] do
|
||||
[ %InternalNode{ node | content: strip_whitespace_textnodes(node.content) }
|
||||
| strip_whitespace_textnodes(rest) ]
|
||||
end
|
||||
|
||||
def strip_whitespace_textnodes [ node | rest] do
|
||||
[ node | strip_whitespace_textnodes(rest) ]
|
||||
end
|
||||
|
||||
def strip_whitespace_textnodes([]), do: []
|
||||
|
||||
|
||||
def build_indentation_list [ %{type: :element} = node | rest], level do
|
||||
[ %{node: Map.replace(node, :content, []), level: level}
|
||||
| [ build_indentation_list(node.content, level + 1)
|
||||
| build_indentation_list(rest, level)
|
||||
]
|
||||
] |> List.flatten
|
||||
end
|
||||
|
||||
def build_indentation_list [ node | rest ], level do
|
||||
[ %{node: node, level: level}
|
||||
| build_indentation_list( rest, level ) ]
|
||||
end
|
||||
|
||||
def build_indentation_list([ ], _), do: []
|
||||
end
|
||||
|
||||
30
lib/outlook/internal_tree.ex
Normal file
30
lib/outlook/internal_tree.ex
Normal file
@ -0,0 +1,30 @@
|
||||
defmodule Outlook.InternalTree do
|
||||
|
||||
alias Outlook.InternalTree.{Html,Modifiers,Basic}
|
||||
alias Outlook.HtmlPreparations.HtmlPreparation
|
||||
|
||||
def render_html(tree) do
|
||||
tree
|
||||
|> HtmlPreparation.strip_whitespace_textnodes()
|
||||
|> Html.to_html()
|
||||
end
|
||||
|
||||
def render_html_preview(tree) do
|
||||
tree
|
||||
|> partition_text
|
||||
|> Html.to_html_preview("1")
|
||||
end
|
||||
|
||||
require Logger
|
||||
def apply_modifier(tree, modifier, uuids, opts \\ %{}) do
|
||||
# Logger.info modifier
|
||||
Modifiers.traverse_tree(tree, modifier, uuids, opts)
|
||||
end
|
||||
|
||||
def partition_text(tree) do
|
||||
# validate_sibling_collocation(tree)
|
||||
tree
|
||||
|> Basic.set_split_markers()
|
||||
|> Basic.partition_textnodes()
|
||||
end
|
||||
end
|
||||
52
lib/outlook/internal_tree/html.ex
Normal file
52
lib/outlook/internal_tree/html.ex
Normal file
@ -0,0 +1,52 @@
|
||||
defmodule Outlook.InternalTree.Html do
|
||||
|
||||
alias Outlook.InternalTree.InternalNode
|
||||
alias Outlook.InternalTree.TranslationUnit
|
||||
|
||||
def to_html([ %InternalNode{type: :element} = node | rest]) do
|
||||
attr_string = Map.put(node.attributes, :uuid, node.uuid)
|
||||
|> Enum.map_join(" ", fn {k,v} -> "#{k}=\"#{v}\"" end)
|
||||
"<#{node.name} #{attr_string}>" <>
|
||||
to_html(node.content) <>
|
||||
"</#{node.name}>" <>
|
||||
to_html(rest)
|
||||
end
|
||||
|
||||
def to_html([ %InternalNode{type: :text} = node | rest]) do
|
||||
node.content <> to_html(rest)
|
||||
end
|
||||
|
||||
def to_html([ %InternalNode{type: :comment} = node | rest]) do
|
||||
"<!--#{node.content}-->" <> to_html(rest)
|
||||
end
|
||||
|
||||
def to_html([ %TranslationUnit{} = tunit | rest]) do
|
||||
~s(<span class="tunit" uuid="#{tunit.uuid}" tu-status="#{tunit.status}">#{tunit.content}</span>) <> to_html(rest)
|
||||
end
|
||||
|
||||
def to_html([]), do: ""
|
||||
|
||||
def to_html_preview([ %InternalNode{type: :element} = node | rest], target_id) do
|
||||
attr_string = Map.put(node.attributes, :uuid, node.uuid)
|
||||
|> Enum.map_join(" ", fn {k,v} -> "#{k}=\"#{v}\"" end)
|
||||
"<#{node.name} #{attr_string}>" <>
|
||||
to_html_preview(node.content, target_id) <>
|
||||
"</#{node.name}>" <>
|
||||
to_html_preview(rest, target_id)
|
||||
end
|
||||
|
||||
def to_html_preview([ %InternalNode{type: :text} = node | rest], target_id) do
|
||||
~s(<span uuid="#{node.uuid}">#{node.content}</span>) <> to_html_preview(rest, target_id)
|
||||
end
|
||||
|
||||
def to_html_preview([ %InternalNode{type: :comment} = node | rest], target_id) do
|
||||
~s(<span uuid="#{node.uuid}"><!--#{node.content}--></span>) <> to_html_preview(rest, target_id)
|
||||
end
|
||||
|
||||
def to_html_preview([ %TranslationUnit{} = tunit | rest], target_id) do
|
||||
~s|<span class="tunit" uuid="#{tunit.uuid}" tu-status="#{tunit.status}" phx-click="select_current_tunit"
|
||||
phx-value-uuid="#{tunit.uuid}" phx-target="#{target_id}">#{tunit.content}</span>| <> to_html_preview(rest, target_id)
|
||||
end
|
||||
|
||||
def to_html_preview([], _target_id), do: ""
|
||||
end
|
||||
70
lib/outlook/internal_tree/internal_tree_basic.ex
Normal file
70
lib/outlook/internal_tree/internal_tree_basic.ex
Normal file
@ -0,0 +1,70 @@
|
||||
defmodule Outlook.InternalTree.Basic do
|
||||
|
||||
alias Ecto.UUID
|
||||
alias Outlook.InternalTree.InternalNode
|
||||
alias Outlook.InternalTree.TranslationUnit
|
||||
alias Outlook.InternalTree.Html
|
||||
|
||||
@splitmarker "@@translationunit@@"
|
||||
|
||||
def set_split_markers([ %InternalNode{type: :text} = textnode | rest ]) do
|
||||
[ %InternalNode{textnode |
|
||||
content: String.replace(textnode.content, ~r|([.?!]["'”]?\s*)|u, "\\1#{@splitmarker}")
|
||||
} | set_split_markers(rest) ]
|
||||
end
|
||||
|
||||
def set_split_markers([ %InternalNode{type: :element} = node | rest ]) do
|
||||
[ %InternalNode{node | content: set_split_markers(node.content)}
|
||||
| set_split_markers(rest) ]
|
||||
end
|
||||
|
||||
def set_split_markers([ node | rest ]) do
|
||||
[ node | set_split_markers(rest) ]
|
||||
end
|
||||
|
||||
def set_split_markers([]), do: []
|
||||
|
||||
def partition_textnodes([ %InternalNode{type: :element} = node | rest ]) do
|
||||
[ %InternalNode{node | content: case get_sibling_collocation(node.content) do
|
||||
:block -> partition_textnodes(node.content)
|
||||
:inline -> inline_to_translation_units(node.content)
|
||||
_ -> node # rare case of only whitespace textnode(s) - does this ever happen?
|
||||
end
|
||||
} | partition_textnodes(rest) ]
|
||||
end
|
||||
|
||||
def partition_textnodes([ node | rest ]) do
|
||||
[ node | partition_textnodes(rest) ]
|
||||
end
|
||||
|
||||
def partition_textnodes([]), do: []
|
||||
|
||||
|
||||
defp inline_to_translation_units(contents) do
|
||||
contents
|
||||
# |> Html.strip_attributes # to be implemented
|
||||
|> Html.to_html()
|
||||
|> String.split(@splitmarker, trim: true)
|
||||
|> Enum.map(fn sentence ->
|
||||
%TranslationUnit{
|
||||
content: sentence,
|
||||
status: :untranslated,
|
||||
uuid: UUID.generate()
|
||||
}
|
||||
end
|
||||
)
|
||||
end
|
||||
|
||||
defp contains_elements?(content) do
|
||||
|
||||
end
|
||||
|
||||
@doc "Returns just either :block or :inline. Assumes that it doesn't contain both."
|
||||
def get_sibling_collocation(content) do
|
||||
content
|
||||
|> Enum.map(fn node -> node.sibling_with end)
|
||||
|> Enum.uniq()
|
||||
|> List.delete(:both)
|
||||
|> List.first
|
||||
end
|
||||
end
|
||||
19
lib/outlook/internal_tree/raw_internal_tree.ex
Normal file
19
lib/outlook/internal_tree/raw_internal_tree.ex
Normal file
@ -0,0 +1,19 @@
|
||||
defmodule Outlook.Articles.RawInternalTree do
|
||||
use Ecto.Type
|
||||
|
||||
def type, do: :string
|
||||
|
||||
def cast([] = tree) do
|
||||
{:ok, tree}
|
||||
end
|
||||
|
||||
def cast(_), do: :error
|
||||
|
||||
def load(tree) when is_binary(tree) do
|
||||
{:ok, Jason.decode!(tree, keys: :atoms!)}
|
||||
end
|
||||
|
||||
def dump([] = tree ), do: {:ok, Jason.encode!(tree)}
|
||||
def dump(_), do: :error
|
||||
|
||||
end
|
||||
29
lib/outlook/internal_tree/raw_internal_tree_schema.ex
Normal file
29
lib/outlook/internal_tree/raw_internal_tree_schema.ex
Normal file
@ -0,0 +1,29 @@
|
||||
defmodule Outlook.Articles.RawInternalTreeSchema do
|
||||
use Ecto.Schema
|
||||
import Ecto.Changeset
|
||||
|
||||
alias Outlook.Articles.RawInternalTree
|
||||
|
||||
embedded_schema do
|
||||
field :tree, RawInternalTree
|
||||
end
|
||||
|
||||
@doc false
|
||||
def changeset(raw_tree, attrs) do
|
||||
raw_tree
|
||||
|> check_sibling_collocation(:tree)
|
||||
end
|
||||
|
||||
def check_sibling_collocation(changeset, field) when is_atom(field) do
|
||||
validate_change(changeset, field, fn field, value ->
|
||||
case value do
|
||||
true ->
|
||||
[]
|
||||
|
||||
false ->
|
||||
[{field, "html should be useful"}]
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
end
|
||||
4
lib/outlook/internal_tree/translation_unit.ex
Normal file
4
lib/outlook/internal_tree/translation_unit.ex
Normal file
@ -0,0 +1,4 @@
|
||||
defmodule Outlook.InternalTree.TranslationUnit do
|
||||
@derive Jason.Encoder
|
||||
defstruct status: :atom, uuid: "", content: ""
|
||||
end
|
||||
Reference in New Issue
Block a user