pdf.ex 3.34 KB
Newer Older
1
2
3
4
5
defmodule Mobilizon.Service.Export.Participants.PDF do
  @moduledoc """
  Export a list of participants to PDF
  """

6
  alias Mobilizon.{Events, Export, PythonPort, PythonWorker}
7
8
9
10
11
12
13
14
  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,
15
16
17
18
19
20
21
22
    only: [
      save_upload: 5,
      columns: 0,
      to_list: 1,
      clean_exports: 2,
      export_enabled?: 1,
      export_path: 1
    ]
23
24
25
26
27
28
29
30
31
32
33
34

  @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"
35
      full_path = Path.join([export_path(@extension), filename])
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105

      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
106
    clean_exports(@extension, export_path(@extension))
107
108
109
110
  end

  @spec dependencies_ok? :: boolean
  def dependencies_ok? do
111
    PythonPort.python_exists?() && PythonWorker.has_module("weasyprint")
112
113
114
115
116
117
118
119
120
121
122
123
  end

  @spec enabled? :: boolean
  def enabled? do
    export_enabled?(__MODULE__)
  end

  @spec ready? :: boolean
  def ready? do
    enabled?() && dependencies_ok?()
  end
end