Website Metrics
Mix.install([
{:kino, "~> 0.12.0"},
{:kino_vega_lite, "~> 0.1.10"},
{:kino_explorer, "~> 0.1.11"}
])
Setup
Kino.Shorts.markdown("""
# Website Analytics
## Load metrics
Load the metrics from website database
""")
require Kino.RPC
node = String.to_atom(System.fetch_env!("LB_WEBSITE_RELEASE_NODE"))
Node.set_cookie(node, String.to_atom(System.fetch_env!("LB_WEBSITE_RELEASE_COOKIE")))
metrics =
Kino.RPC.eval_string(
node,
~S"""
import Ecto.Query, only: [from: 2]
alias SamuelWillis.Metrics.Metric
alias SamuelWillis.Repo
query = from m in Metric,
order_by: [desc: m.date],
select: %{
date: m.date,
path: m.path,
visits: m.visits,
}
Repo.all(query)
""",
file: __ENV__.file
)
Table of path visits
Kino.Shorts.markdown("""
## Daily visits
Table showing the visits per day
""")
require Explorer.DataFrame
metrics_df = Explorer.DataFrame.new(metrics)
# Group by 'date' and aggregate 'visits'
metrics_df
|> Explorer.DataFrame.group_by([:date])
|> Explorer.DataFrame.summarise(total_visits: sum(visits))
|> Explorer.DataFrame.sort_by(desc: date)
Chart visits per path
markdown =
Kino.Shorts.markdown("""
## Daily visits by path
Chart displaying the number of visits per day, broken down by path visited.
""")
Kino.render(markdown)
today = Date.utc_today()
seven_days_ago = Date.add(today, -8)
thirty_days_ago = Date.add(today, -31)
options = [
{today, "Today"},
{seven_days_ago, "Last seven days"},
{thirty_days_ago, "Last 30 days"}
]
time_period_input = Kino.Input.select("Time period", options, default: seven_days_ago)
Kino.render(time_period_input)
time_period_selected = Kino.Input.read(time_period_input)
Kino.nothing()
metrics_from_time_period =
Enum.filter(
metrics,
&(Date.compare(&1.date, time_period_selected) in [:gt, :eq])
)
# Create a DataFrame containing all dates in the time period
date_range_df =
time_period_selected
|> Date.range(Date.utc_today())
|> Enum.map(&%{date: &1, path: "/", visits: 0})
|> Explorer.DataFrame.new()
# Create a DataFrame consisting of metrics from time period
metrics_from_time_period_df = Explorer.DataFrame.new(metrics_from_time_period)
# Concatinate the two dataframes
metrics_from_time_period_df =
Explorer.DataFrame.concat_rows(
metrics_from_time_period_df,
date_range_df
)
VegaLite.new(width: 700, height: 500)
|> VegaLite.data_from_values(metrics_from_time_period_df)
|> VegaLite.mark(:bar)
|> VegaLite.encode_field(:x, "date", type: :ordinal)
|> VegaLite.encode_field(:y, "visits", type: :quantitative)
|> VegaLite.encode_field(:color, "path")