Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Website Metrics

livebook/metrics.livemd

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")