Update Deepl translation
Now getting auth-key from db.
This commit is contained in:
@ -6,99 +6,93 @@ defmodule Outlook.Translators do
|
|||||||
import Ecto.Query, warn: false
|
import Ecto.Query, warn: false
|
||||||
alias Outlook.Repo
|
alias Outlook.Repo
|
||||||
|
|
||||||
alias Outlook.Translators.DeeplAccount
|
alias Outlook.InternalTree.TranslationUnit
|
||||||
|
alias Outlook.Translators.{DeeplAccount,Deepl}
|
||||||
|
alias OutlookWeb.HtmlDocComponent
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns the list of deepl_accounts.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> list_deepl_accounts()
|
|
||||||
[%DeeplAccount{}, ...]
|
|
||||||
|
|
||||||
"""
|
|
||||||
def list_deepl_accounts do
|
def list_deepl_accounts do
|
||||||
Repo.all(DeeplAccount)
|
Repo.all(DeeplAccount)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Gets a single deepl_account.
|
|
||||||
|
|
||||||
Raises `Ecto.NoResultsError` if the Deepl account does not exist.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> get_deepl_account!(123)
|
|
||||||
%DeeplAccount{}
|
|
||||||
|
|
||||||
iex> get_deepl_account!(456)
|
|
||||||
** (Ecto.NoResultsError)
|
|
||||||
|
|
||||||
"""
|
|
||||||
def get_deepl_account!(id), do: Repo.get!(DeeplAccount, id)
|
def get_deepl_account!(id), do: Repo.get!(DeeplAccount, id)
|
||||||
|
|
||||||
@doc """
|
def get_deepl_auth_key(user_id) do
|
||||||
Creates a deepl_account.
|
query =
|
||||||
|
from DeeplAccount,
|
||||||
|
where: [user_id: ^user_id],
|
||||||
|
select: [:auth_key]
|
||||||
|
Repo.one!(query)
|
||||||
|
|> Map.get(:auth_key)
|
||||||
|
end
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> create_deepl_account(%{field: value})
|
|
||||||
{:ok, %DeeplAccount{}}
|
|
||||||
|
|
||||||
iex> create_deepl_account(%{field: bad_value})
|
|
||||||
{:error, %Ecto.Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
def create_deepl_account(attrs \\ %{}) do
|
def create_deepl_account(attrs \\ %{}) do
|
||||||
%DeeplAccount{}
|
%DeeplAccount{}
|
||||||
|> DeeplAccount.changeset(attrs)
|
|> DeeplAccount.changeset(attrs)
|
||||||
|> Repo.insert()
|
|> Repo.insert()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Updates a deepl_account.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> update_deepl_account(deepl_account, %{field: new_value})
|
|
||||||
{:ok, %DeeplAccount{}}
|
|
||||||
|
|
||||||
iex> update_deepl_account(deepl_account, %{field: bad_value})
|
|
||||||
{:error, %Ecto.Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
def update_deepl_account(%DeeplAccount{} = deepl_account, attrs) do
|
def update_deepl_account(%DeeplAccount{} = deepl_account, attrs) do
|
||||||
deepl_account
|
deepl_account
|
||||||
|> DeeplAccount.changeset(attrs)
|
|> DeeplAccount.changeset(attrs)
|
||||||
|> Repo.update()
|
|> Repo.update()
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Deletes a deepl_account.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> delete_deepl_account(deepl_account)
|
|
||||||
{:ok, %DeeplAccount{}}
|
|
||||||
|
|
||||||
iex> delete_deepl_account(deepl_account)
|
|
||||||
{:error, %Ecto.Changeset{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
def delete_deepl_account(%DeeplAccount{} = deepl_account) do
|
def delete_deepl_account(%DeeplAccount{} = deepl_account) do
|
||||||
Repo.delete(deepl_account)
|
Repo.delete(deepl_account)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
|
||||||
Returns an `%Ecto.Changeset{}` for tracking deepl_account changes.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
iex> change_deepl_account(deepl_account)
|
|
||||||
%Ecto.Changeset{data: %DeeplAccount{}}
|
|
||||||
|
|
||||||
"""
|
|
||||||
def change_deepl_account(%DeeplAccount{} = deepl_account, attrs \\ %{}) do
|
def change_deepl_account(%DeeplAccount{} = deepl_account, attrs \\ %{}) do
|
||||||
DeeplAccount.changeset(deepl_account, attrs)
|
DeeplAccount.changeset(deepl_account, attrs)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def translate(translation, current_user) do
|
||||||
|
%{lang: target_lang,
|
||||||
|
article: %{content: article_tree, language: source_lang}
|
||||||
|
} = translation
|
||||||
|
article_as_html = prepare_article(article_tree)
|
||||||
|
auth_key = get_deepl_auth_key(current_user.id)
|
||||||
|
args = [
|
||||||
|
self(),
|
||||||
|
article_as_html,
|
||||||
|
%{
|
||||||
|
source_lang: source_lang,
|
||||||
|
target_lang: target_lang,
|
||||||
|
auth_key: auth_key
|
||||||
|
}
|
||||||
|
]
|
||||||
|
Task.start_link(Deepl, :translate, args)
|
||||||
|
end
|
||||||
|
|
||||||
|
defp prepare_article(tree) do
|
||||||
|
# Logger.info "so far."
|
||||||
|
HtmlDocComponent.render_doc(%{tree: tree})
|
||||||
|
|> Phoenix.HTML.Safe.to_iodata()
|
||||||
|
|> IO.iodata_to_binary()
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_translation_result(result, tunit_ids) do
|
||||||
|
# TODO: update :our_character_count
|
||||||
|
process_translation(result.translation, tunit_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
def process_translation(translation, tunit_ids) do
|
||||||
|
tunit_map = translation
|
||||||
|
|> Floki.parse_fragment!
|
||||||
|
|> Floki.find("span.tunit")
|
||||||
|
|> Enum.map(fn {_,atts,cont} ->
|
||||||
|
%TranslationUnit{
|
||||||
|
uuid: Enum.find(atts, fn {k,_} -> k == "uuid" end) |> Tuple.to_list |> Enum.at(1),
|
||||||
|
content: Floki.raw_html(cont),
|
||||||
|
status: :untranslated
|
||||||
|
}
|
||||||
|
end)
|
||||||
|
|> Enum.map(fn tunit -> {tunit.uuid, tunit} end)
|
||||||
|
|> Enum.into(%{})
|
||||||
|
|
||||||
|
case Enum.sort(Map.keys(tunit_map)) == Enum.sort(tunit_ids) do
|
||||||
|
true -> {:ok, tunit_map}
|
||||||
|
false -> {:error, "keys don't equal the originals"}
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
defmodule Outlook.Translators.Deepl do
|
defmodule Outlook.Translators.Deepl do
|
||||||
|
|
||||||
def test(pid) do
|
def translate(pid, article, options) do
|
||||||
for n <- 0..100 do
|
send(pid, {:progress, %{progress: 0}})
|
||||||
send(pid, {:progress, %{progress: n}})
|
credentials = start_translation(article, options)
|
||||||
Process.sleep 50
|
status = check_status(pid, credentials)
|
||||||
end
|
translation = get_translation(credentials)
|
||||||
|
send(pid, {:translation, %{translation: translation, billed_characters: status.billed_characters}})
|
||||||
|
Process.sleep 1000
|
||||||
send(pid, {:progress, %{progress: nil}})
|
send(pid, {:progress, %{progress: nil}})
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -13,10 +15,11 @@ defmodule Outlook.Translators.Deepl do
|
|||||||
@doc """
|
@doc """
|
||||||
Upload the content to translate and return document_id and document_key as Map.
|
Upload the content to translate and return document_id and document_key as Map.
|
||||||
"""
|
"""
|
||||||
def start_translation %{auth_key: auth_key} = _credentials, content, target_lang do
|
def start_translation content, options do
|
||||||
form = get_multipart_form(
|
form = get_multipart_form(
|
||||||
[
|
[
|
||||||
{"target_lang", target_lang},
|
{"source_lang", options.source_lang},
|
||||||
|
{"target_lang", options.target_lang},
|
||||||
{"file", content, {"form-data", [{:name, "file"}, {:filename, "datei.html"}]}, []}
|
{"file", content, {"form-data", [{:name, "file"}, {:filename, "datei.html"}]}, []}
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@ -25,16 +28,16 @@ defmodule Outlook.Translators.Deepl do
|
|||||||
:post,
|
:post,
|
||||||
"https://api-free.deepl.com/v2/document",
|
"https://api-free.deepl.com/v2/document",
|
||||||
form,
|
form,
|
||||||
get_multipart_headers(auth_key)
|
get_multipart_headers(options.auth_key)
|
||||||
)
|
)
|
||||||
Jason.decode!(response_raw.body, keys: :atoms)
|
Jason.decode!(response_raw.body, keys: :atoms)
|
||||||
|> Map.put(:auth_key, auth_key)
|
|> Map.put(:auth_key, options.auth_key)
|
||||||
end
|
end
|
||||||
|
|
||||||
@doc """
|
@doc """
|
||||||
Upload the content to translate and return the estimated time until done.
|
Check the status until translation is "done".
|
||||||
"""
|
"""
|
||||||
def check_status credentials do
|
def check_status pid, credentials do
|
||||||
response_raw = HTTPoison.request!(
|
response_raw = HTTPoison.request!(
|
||||||
:post,
|
:post,
|
||||||
"https://api-free.deepl.com/v2/document/#{credentials.document_id}",
|
"https://api-free.deepl.com/v2/document/#{credentials.document_id}",
|
||||||
@ -45,8 +48,12 @@ defmodule Outlook.Translators.Deepl do
|
|||||||
|
|
||||||
case response do
|
case response do
|
||||||
%{status: "translating"} ->
|
%{status: "translating"} ->
|
||||||
Process.sleep(String.to_integer(response.seconds_remaining) * 1000)
|
steps = response.seconds_remaining * 5
|
||||||
check_status(credentials)
|
for n <- 0..steps do
|
||||||
|
send(pid, {:progress, %{progress: 100 * n / steps}})
|
||||||
|
Process.sleep 200
|
||||||
|
end
|
||||||
|
check_status(pid, credentials)
|
||||||
%{status: "done"} ->
|
%{status: "done"} ->
|
||||||
response
|
response
|
||||||
end
|
end
|
||||||
|
|||||||
@ -63,9 +63,21 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
|
|||||||
{:ok, socket |> assign(deepl_progress: progress)}
|
{:ok, socket |> assign(deepl_progress: progress)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def update(%{deepl_translation: translation}, socket) do
|
||||||
|
tunit_keys = Map.keys(socket.assigns.translation_content)
|
||||||
|
case Outlook.Translators.process_translation_result(translation, tunit_keys) do
|
||||||
|
{:ok, new_translation_content} ->
|
||||||
|
{:ok, socket
|
||||||
|
|> assign(translation_content: new_translation_content)
|
||||||
|
|> update_current_tunit()}
|
||||||
|
{:error, message} ->
|
||||||
|
{:ok, socket |> put_flash(:error, message)}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
@impl true
|
@impl true
|
||||||
def handle_event("translate-deepl", _, socket) do
|
def handle_event("translate-deepl", _, socket) do
|
||||||
Task.start_link(Outlook.Translators.Deepl, :test, [self()])
|
Outlook.Translators.translate(socket.assigns.translation, socket.assigns.current_user)
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -98,11 +110,21 @@ defmodule OutlookWeb.TranslationLive.FormComponent do
|
|||||||
|> assign(:current_tunit, socket.assigns.translation_content[uuid])}
|
|> assign(:current_tunit, socket.assigns.translation_content[uuid])}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@doc "updating on browser events"
|
||||||
def handle_event("update_current_tunit", %{"content" => content}, socket) do
|
def handle_event("update_current_tunit", %{"content" => content}, socket) do
|
||||||
tunit = %TranslationUnit{socket.assigns.current_tunit | content: content}
|
tunit = %TranslationUnit{socket.assigns.current_tunit | content: content}
|
||||||
{:noreply, socket |> assign(:current_tunit, tunit)}
|
{:noreply, socket |> assign(:current_tunit, tunit)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
# updating after Deepl translation
|
||||||
|
defp update_current_tunit(socket) when is_struct(socket.assigns.current_tunit) do
|
||||||
|
assign(socket,
|
||||||
|
:current_tunit,
|
||||||
|
socket.assigns.translation_content[socket.assigns.current_tunit.uuid])
|
||||||
|
end
|
||||||
|
defp update_current_tunit(socket), do: socket
|
||||||
|
|
||||||
defp update_translation_with_current_tunit(socket) do
|
defp update_translation_with_current_tunit(socket) do
|
||||||
translation_content = if socket.assigns.current_tunit do
|
translation_content = if socket.assigns.current_tunit do
|
||||||
socket.assigns.translation_content
|
socket.assigns.translation_content
|
||||||
|
|||||||
@ -17,6 +17,7 @@ defmodule OutlookWeb.TranslationLive.NewEdit do
|
|||||||
action={@live_action}
|
action={@live_action}
|
||||||
translation={@translation}
|
translation={@translation}
|
||||||
translation_content={@translation_content}
|
translation_content={@translation_content}
|
||||||
|
current_user={@current_user}
|
||||||
navigate={~p"/translations"}
|
navigate={~p"/translations"}
|
||||||
/>
|
/>
|
||||||
"""
|
"""
|
||||||
@ -56,4 +57,9 @@ defmodule OutlookWeb.TranslationLive.NewEdit do
|
|||||||
send_update(self(), FormComponent, progress: payload.progress, id: @form_cmpnt_id)
|
send_update(self(), FormComponent, progress: payload.progress, id: @form_cmpnt_id)
|
||||||
{:noreply, socket}
|
{:noreply, socket}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def handle_info({:translation, payload}, socket) do
|
||||||
|
send_update(self(), FormComponent, deepl_translation: payload, id: @form_cmpnt_id)
|
||||||
|
{:noreply, socket}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user