Update Deepl translation

Now getting auth-key from db.
This commit is contained in:
Thelonius Kort
2023-01-09 21:29:19 +01:00
parent 3cd9d983fe
commit 71e6a8da60
4 changed files with 111 additions and 82 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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