mobilizon/lib/service/export/participants/pdf.ex

124 lines
3.3 KiB
Elixir

defmodule Mobilizon.Service.Export.Participants.PDF do
@moduledoc """
Export a list of participants to PDF
"""
alias Mobilizon.{Events, Export, PythonPort, PythonWorker}
alias Mobilizon.Events.Event
alias Mobilizon.Storage.Repo
alias Mobilizon.Web.ExportView
alias Mobilizon.Web.Gettext, as: GettextBackend
alias Phoenix.HTML.Safe
import Mobilizon.Web.Gettext, only: [gettext: 2]
import Mobilizon.Service.Export.Participants.Common,
only: [
save_upload: 5,
columns: 0,
to_list: 1,
clean_exports: 2,
export_enabled?: 1,
export_path: 1
]
@extension "pdf"
def extension do
@extension
end
@spec export(Event.t(), Keyword.t()) ::
{:ok, String.t()} | {:error, :failed_to_save_upload | :export_dependency_not_installed}
def export(%Event{id: event_id} = event, options \\ []) do
if ready?() do
filename = "#{ShortUUID.encode!(Ecto.UUID.generate())}.pdf"
full_path = Path.join([export_path(@extension), filename])
case Repo.transaction(
fn ->
content =
event_id
|> Events.participant_for_event_export_query(Keyword.get(options, :roles, []))
|> Repo.all()
|> Enum.map(&to_list/1)
|> render_template(event, Keyword.get(options, :locale, "en"))
|> generate_pdf()
File.write!(full_path, content)
with {:error, err} <- save_pdf_upload(full_path, filename, event) do
Repo.rollback(err)
end
end,
timeout: :infinity
) do
{:error, _err} ->
File.rm!(full_path)
{:error, :failed_to_save_upload}
{:ok, _ok} ->
{:ok, filename}
end
else
{:error, :export_dependency_not_installed}
end
end
@spec render_template(list(), Event.t(), String.t()) :: String.t()
defp render_template(data, %Event{} = event, locale) do
Gettext.put_locale(locale)
ExportView.render("event_participants.html",
data: data,
columns: columns(),
event: event,
locale: locale
)
|> Safe.to_iodata()
|> IO.iodata_to_binary()
end
defp generate_pdf(html) do
PythonWorker.generate_pdf(html)
end
@spec save_pdf_upload(String.t(), String.t(), Event.t()) ::
{:ok, Export.t()} | {:error, atom() | Ecto.Changeset.t()}
defp save_pdf_upload(full_path, filename, %Event{id: event_id, title: title}) do
GettextBackend.gettext_comment(
"File name template for exported list of participants. Should NOT contain spaces. Make sure the output is going to be something standardized that is acceptable as a file name on most systems."
)
save_upload(
full_path,
filename,
to_string(event_id),
gettext("%{event}_participants", event: title) <> ".pdf",
"pdf"
)
end
@doc """
Clean outdated files in export folder
"""
@spec clean_exports :: :ok
def clean_exports do
clean_exports(@extension, export_path(@extension))
end
@spec dependencies_ok? :: boolean
def dependencies_ok? do
PythonPort.python_exists?() && PythonWorker.has_module("weasyprint")
end
@spec enabled? :: boolean
def enabled? do
export_enabled?(__MODULE__)
end
@spec ready? :: boolean
def ready? do
enabled?() && dependencies_ok?()
end
end