File size: 4,509 Bytes
936b397 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | defmodule Plausible.Funnels do
@moduledoc """
This module implements contextual Funnel interface, allowing listing,
creating and deleting funnel definitions.
For brief explanation of what a Funnel is, please see `Plausible.Funnel` schema.
See `Plausible.Stats.Funnel` for the evaluation logic.
"""
use Plausible.Funnel
alias Plausible.Repo
import Ecto.Query
@spec create(Plausible.Site.t(), String.t(), [map()], Keyword.t()) ::
{:ok, Funnel.t()}
| {:error, Ecto.Changeset.t() | :invalid_funnel_size | :upgrade_required}
def create(site, name, steps, opts \\ [])
def create(site, name, steps, opts)
when is_list(steps) and length(steps) in Funnel.min_steps()..Funnel.max_steps() do
site = Plausible.Repo.preload(site, :team)
case Plausible.Billing.Feature.Funnels.check_availability(site.team) do
{:error, _} = error ->
error
:ok ->
site
|> create_changeset(name, steps, opts)
|> Repo.insert()
end
end
def create(_site, _name, _goals, _opts) do
{:error, :invalid_funnel_size}
end
@spec update(Funnel.t(), String.t(), [map()], Keyword.t()) ::
{:ok, Funnel.t()}
| {:error, Ecto.Changeset.t() | :invalid_funnel_size | :upgrade_required}
def update(funnel, name, steps, opts \\ []) do
site = Plausible.Repo.preload(funnel, site: :team).site
case Plausible.Billing.Feature.Funnels.check_availability(site.team) do
{:error, _} = error ->
error
:ok ->
funnel
|> edit_changeset(name, steps,
strict_order?: Keyword.get(opts, :strict_order?, !!funnel.strict_order)
)
|> Repo.update()
end
end
@spec create_changeset(Plausible.Site.t(), String.t(), [map()], Keyword.t()) ::
Ecto.Changeset.t()
def create_changeset(site, name, steps, opts \\ []) do
Funnel.changeset(%Funnel{site_id: site.id}, %{
name: name,
steps: steps,
strict_order: Keyword.get(opts, :strict_order?, false)
})
end
@spec edit_changeset(Plausible.Funnel.t(), String.t(), [map()], Keyword.t()) ::
Ecto.Changeset.t()
def edit_changeset(funnel, name, steps, opts) do
Funnel.changeset(funnel, %{
name: name,
steps: steps,
strict_order: Keyword.get(opts, :strict_order?, false)
})
end
@spec ephemeral_definition(Plausible.Site.t(), String.t(), [map()], Keyword.t()) :: Funnel.t()
def ephemeral_definition(site, name, steps, opts \\ []) do
site
|> create_changeset(name, steps, opts)
|> Ecto.Changeset.apply_changes()
end
@spec list(Plausible.Site.t()) :: [
%{name: String.t(), id: pos_integer(), steps_count: pos_integer()}
]
def list(%Plausible.Site{} = site) do
q =
from(f in Funnel,
inner_join: steps in assoc(f, :steps),
where: f.site_id == ^site.id,
group_by: f.id,
order_by: [desc: :id],
select: %{name: f.name, id: f.id, steps_count: count(steps)}
)
Repo.all(q)
end
@spec delete(Plausible.Site.t() | pos_integer(), pos_integer()) :: :ok
def delete(%Plausible.Site{id: site_id}, funnel_id) do
delete(site_id, funnel_id)
end
def delete(site_id, funnel_id) do
Repo.delete_all(
from(f in Funnel,
where: f.site_id == ^site_id,
where: f.id == ^funnel_id
)
)
:ok
end
@spec get(Plausible.Site.t() | pos_integer(), pos_integer() | String.t()) ::
Funnel.t() | nil
def get(%Plausible.Site{id: site_id}, by) do
get(site_id, by)
end
def get(site_id, funnel_name) when is_integer(site_id) and is_binary(funnel_name) do
site_id |> base_get_query() |> where([f], f.name == ^funnel_name) |> Repo.one()
end
def get(site_id, funnel_id) when is_integer(site_id) and is_integer(funnel_id) do
site_id |> base_get_query() |> where([f], f.id == ^funnel_id) |> Repo.one()
end
@spec with_goals_query(Plausible.Site.t()) :: Ecto.Query.t()
def with_goals_query(site) do
from(f in Funnel,
inner_join: steps in assoc(f, :steps),
where: f.site_id == ^site.id,
group_by: f.id,
order_by: [desc: :id],
preload: [steps: :goal]
)
end
defp base_get_query(site_id) do
from(f in Funnel,
where: f.site_id == ^site_id,
inner_join: steps in assoc(f, :steps),
inner_join: goal in assoc(steps, :goal),
order_by: steps.step_order,
preload: [
steps: {steps, goal: goal}
]
)
end
end
|