analytics/lib/mix/tasks/send_pageview.ex
Vini Brasil e4d4f7d954
Revenue tracking: Ingestion and breakdown queries (#2957)
* Add revenue fields to ClickHouse events

This commit adds 4 fields to the ClickHouse events_v2 table:

* `revenue_source_amount` and `revenue_source_currency` store revenue in
  the original currency sent during ingestion

* `revenue_reporting_amount` and `revenue_reporting_currency` store
  revenue in a common currency to perform calculations, and this
  currency is defined by the user when setting up the goal

The type of amount fields is `Nullable(Decimal64(3))`. That covers all
fiat currencies and allows us to store huge amounts. Even though
ClickHouse does not suggest using `Nullable`, this is a good use case,
because otherwise additional work would have to be done to
differentiate missing values from real zeroes.

I ran a benchmark with the data pattern we expect in production, where
we have more missing values than real decimals. I created 100 million
records where 90% of decimals are missing. The difference between the
tables in storage is just 0.4Mb.

* Add revenue parameter to Events API

This commit adds support for sending revenue data in ingestion using the
`revenue` parameter - aliased to `$`.

* Add revenue parameter to mix send_pageview

* Add average and total revenue to breakdown queries
2023-06-12 18:29:17 +01:00

115 lines
3.2 KiB
Elixir

defmodule Mix.Tasks.SendPageview do
@moduledoc """
It's often necessary to generate fake events for development and testing purposes. This Mix Task provides a quick and easy
way to generate a pageview or custom event, either in your development environment or a remote Plausible instance.
See Mix.Tasks.SendPageview.usage/1 for more detailed documentation.
"""
use Mix.Task
require Logger
@default_host "http://localhost:8000"
@default_ip_address "127.0.0.1"
@default_user_agent "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/85.0.4183.121 Safari/537.36 OPR/71.0.3770.284"
@default_domain "dummy.site"
@default_page "/"
@default_referrer "https://google.com"
@default_event "pageview"
@default_props "{}"
@options [
ip: :string,
user_agent: :string,
domain: :string,
page: :string,
referrer: :string,
host: :string,
event: :string,
props: :string,
revenue_currency: :string,
revenue_amount: :string
]
def run(opts) do
Finch.start_link(name: Plausible.Finch)
{parsed, _, invalid} = OptionParser.parse(opts, strict: @options)
case invalid do
[] ->
do_send_pageview(parsed)
[invalid_option | _] ->
{key, _val} = invalid_option
IO.puts("Invalid option #{key}. Aborting.")
IO.puts(usage())
end
end
defp do_send_pageview(parsed_opts) do
ip = Keyword.get(parsed_opts, :ip, @default_ip_address)
user_agent = Keyword.get(parsed_opts, :user_agent, @default_user_agent)
host = Keyword.get(parsed_opts, :host, @default_host)
url = get_url(host)
headers = get_headers(ip, user_agent)
body = get_body(parsed_opts)
case Plausible.HTTPClient.post(url, headers, body) do
{:ok, resp} ->
IO.puts(
"✅ Successfully sent #{body[:name]} event to #{url}\n\nip=#{ip}\nuser_agent=#{user_agent}\nbody= #{inspect(body, pretty: true)}"
)
IO.puts("Response headers: " <> inspect(resp.headers, pretty: true))
{:error, e} ->
IO.puts("❌ Could not send event to #{url}. Got the following error: \n\n #{inspect(e)}")
end
end
defp get_url(host) do
Path.join(host, "/api/event")
end
defp get_headers(ip, user_agent) do
[
{"x-forwarded-for", ip},
{"user-agent", user_agent},
{"content-type", "text/plain"}
]
end
defp get_body(opts) do
domain = Keyword.get(opts, :domain, @default_domain)
page = Keyword.get(opts, :page, @default_page)
referrer = Keyword.get(opts, :referrer, @default_referrer)
event = Keyword.get(opts, :event, @default_event)
props = Keyword.get(opts, :props, @default_props)
revenue =
if Keyword.get(opts, :revenue_currency) do
%{
currency: Keyword.get(opts, :revenue_currency),
amount: Keyword.get(opts, :revenue_amount)
}
end
%{
name: event,
url: "http://#{domain}#{page}",
domain: domain,
referrer: referrer,
props: props,
revenue: revenue
}
end
defp usage() do
"""
usage: $ mix send_pageview [--domain domain] [--ip ip_address]"
options: #{inspect(@options, pretty: true)}
"""
end
end