From 8a705cd67779d3109f923a7917ed1447691fddb6 Mon Sep 17 00:00:00 2001 From: davidvanleeuwen Date: Wed, 11 Oct 2023 17:00:49 +0200 Subject: [PATCH] Change play to duration to accomodate rebuffering --- lib/mave_metrics/api.ex | 195 +++++++++++------- lib/mave_metrics/keys.ex | 3 +- .../session/{play.ex => duration.ex} | 8 +- lib/mave_metrics/session/session.ex | 12 +- lib/mave_metrics/stats.ex | 73 ++++--- .../channels/session_channel.ex | 179 ++++++++++++---- mix.lock | 48 ++--- .../20231011091809_add_duration_type.exs | 13 ++ 8 files changed, 360 insertions(+), 171 deletions(-) rename lib/mave_metrics/session/{play.ex => duration.ex} (71%) create mode 100644 priv/repo/migrations/20231011091809_add_duration_type.exs diff --git a/lib/mave_metrics/api.ex b/lib/mave_metrics/api.ex index 2c19008..664895e 100644 --- a/lib/mave_metrics/api.ex +++ b/lib/mave_metrics/api.ex @@ -17,42 +17,62 @@ defmodule MaveMetrics.API do @ttl :timer.seconds(30) - alias MaveMetrics.Session.Play - - @decorate cacheable(cache: Cache, key: {Play, "plays" <> key(query) <> key(interval) <> key(timeframe) <> key(minimum_watch_seconds)}, opts: [ttl: @ttl]) + alias MaveMetrics.Session.Duration + + @decorate cacheable( + cache: Cache, + key: + {Duration, + "plays" <> + key(query) <> key(interval) <> key(timeframe) <> key(minimum_watch_seconds)}, + opts: [ttl: @ttl] + ) def get_plays(query, interval, timeframe, minimum_watch_seconds) do interval = interval || @default_interval timeframe = timeframe || @default_timeframe minimum_watch_seconds = minimum_watch_seconds || @default_minimum_watch_seconds - result = Play - |> join(:left, [p], s in assoc(p, :session)) - |> join(:left, [p, s], v in assoc(s, :video)) - |> where_query(query) - |> where_timeframe(timeframe) - |> group_by([p, s, v], [fragment(~s|time_bucket('?', ?)|, literal(^interval), p.timestamp), p.session_id, s.platform, s.device_type, s.browser_type]) - |> select_details(interval) - |> subquery() - |> where([e], e.elapsed_time >= ^minimum_watch_seconds) - |> group_by([e], e.interval) - |> format_output() - |> Repo.all() + result = + Duration + |> where([d], d.type == :play) + |> join(:left, [d], s in assoc(d, :session)) + |> join(:left, [d, s], v in assoc(s, :video)) + |> where_query(query) + |> where_timeframe(timeframe) + |> group_by([d, s, v], [ + fragment(~s|time_bucket('?', ?)|, literal(^interval), d.timestamp), + d.session_id, + s.platform, + s.device_type, + s.browser_type + ]) + |> select_details(interval) + |> subquery() + |> where([e], e.elapsed_time >= ^minimum_watch_seconds) + |> group_by([e], e.interval) + |> format_output() + |> Repo.all() result end - @decorate cacheable(cache: Cache, key: {Play, "engagement" <> key(query) <> key(timeframe) <> key(ranges)}, opts: [ttl: @ttl]) + @decorate cacheable( + cache: Cache, + key: {Duration, "engagement" <> key(query) <> key(timeframe) <> key(ranges)}, + opts: [ttl: @ttl] + ) def get_engagement(query, timeframe, ranges) do timeframe = timeframe || @default_timeframe ranges = ranges || @default_ranges last_point = - Play - |> join(:left, [p], s in assoc(p, :session)) - |> join(:left, [p, s], v in assoc(s, :video)) + Duration + |> where([d], d.type == :play) + |> join(:left, [d], s in assoc(d, :session)) + |> join(:left, [d, s], v in assoc(s, :video)) |> where_query(query) |> where_timeframe(timeframe) - |> select([p, s, v], max(p.to)) + |> select([d, s, v], max(d.to)) |> Repo.one() if is_nil(last_point) do @@ -60,60 +80,87 @@ defmodule MaveMetrics.API do else part = last_point / ranges - {:ok, result} = Repo.transaction(fn -> - 0 .. ranges - 1 |> Enum.map(fn m -> - from_moment = part * m - to_moment = part * (m + 1) - - result = Play - |> join(:left, [p], s in assoc(p, :session)) - |> join(:left, [p, s], v in assoc(s, :video)) - |> where_query(query) - |> where_timeframe(timeframe) - |> select([p, s, v], %{from: p.from, to: p.to}) - |> subquery() - |> where([e], e.from >= ^from_moment and e.from <= ^from_moment) # starts within range - |> or_where([e], e.from < ^from_moment and e.to > ^to_moment) # starts before range and ends after range - |> or_where([e], e.from < ^from_moment and e.to >= ^from_moment and e.to <= ^to_moment) # starts before range and ends within range - |> Repo.all() - - %{range: m, viewers: result |> Enum.count, range_time: %{from: from_moment, to: to_moment}} - end) |> Enum.sort(&(&1.range < &2.range)) - end) + {:ok, result} = + Repo.transaction(fn -> + 0..(ranges - 1) + |> Enum.map(fn m -> + from_moment = part * m + to_moment = part * (m + 1) + + result = + Duration + |> where([d], d.type == :play) + |> join(:left, [d], s in assoc(d, :session)) + |> join(:left, [d, s], v in assoc(s, :video)) + |> where_query(query) + |> where_timeframe(timeframe) + |> select([d, s, v], %{from: d.from, to: d.to}) + |> subquery() + # starts within range + |> where([e], e.from >= ^from_moment and e.from <= ^from_moment) + # starts before range and ends after range + |> or_where([e], e.from < ^from_moment and e.to > ^to_moment) + # starts before range and ends within range + |> or_where( + [e], + e.from < ^from_moment and e.to >= ^from_moment and e.to <= ^to_moment + ) + |> Repo.all() + + %{ + range: m, + viewers: result |> Enum.count(), + range_time: %{from: from_moment, to: to_moment} + } + end) + |> Enum.sort(&(&1.range < &2.range)) + end) result end end - @decorate cacheable(cache: Cache, key: {Play, "source" <> key(query) <> key(interval) <> key(timeframe) <> key(minimum_watch_seconds)}, opts: [ttl: @ttl]) + @decorate cacheable( + cache: Cache, + key: + {Duration, + "source" <> + key(query) <> key(interval) <> key(timeframe) <> key(minimum_watch_seconds)}, + opts: [ttl: @ttl] + ) def get_sources(query, interval, timeframe, minimum_watch_seconds) do interval = interval || @default_interval timeframe = timeframe || @default_timeframe minimum_watch_seconds = minimum_watch_seconds || @default_minimum_watch_seconds - result = Play - |> join(:left, [p], s in assoc(p, :session)) - |> join(:left, [p, s], v in assoc(s, :video)) - |> where_query(query) - |> where_timeframe(timeframe) - |> group_by([p, s, v], [fragment(~s|time_bucket('?', ?)|, literal(^interval), p.timestamp), p.session_id, v.source_uri]) - |> select([p, s, v], %{ - host: fragment(~s|(? ->> ?)|, v.source_uri, "host"), - path: fragment(~s|(? ->> ?)|, v.source_uri, "path"), - interval: fragment(~s|time_bucket('?', ?)|, literal(^interval), p.timestamp), - elapsed_time: sum(p.elapsed_time) - }) - |> subquery() - |> where([e], e.elapsed_time >= ^minimum_watch_seconds) - |> having([e], sum(e.elapsed_time) >= ^minimum_watch_seconds) - |> group_by([e], [e.host, e.path, e.interval]) - |> select([e], %{ - interval: e.interval, - host: e.host, - path: e.path, - views: count(e.interval) - }) - |> Repo.all() + result = + Duration + |> join(:left, [d], s in assoc(d, :session)) + |> join(:left, [d, s], v in assoc(s, :video)) + |> where_query(query) + |> where_timeframe(timeframe) + |> group_by([d, s, v], [ + fragment(~s|time_bucket('?', ?)|, literal(^interval), d.timestamp), + d.session_id, + v.source_uri + ]) + |> select([d, s, v], %{ + host: fragment(~s|(? ->> ?)|, v.source_uri, "host"), + path: fragment(~s|(? ->> ?)|, v.source_uri, "path"), + interval: fragment(~s|time_bucket('?', ?)|, literal(^interval), d.timestamp), + elapsed_time: sum(d.elapsed_time) + }) + |> subquery() + |> where([e], e.elapsed_time >= ^minimum_watch_seconds) + |> having([e], sum(e.elapsed_time) >= ^minimum_watch_seconds) + |> group_by([e], [e.host, e.path, e.interval]) + |> select([e], %{ + interval: e.interval, + host: e.host, + path: e.path, + views: count(e.interval) + }) + |> Repo.all() result end @@ -129,22 +176,22 @@ defmodule MaveMetrics.API do end defp where_query(q, %{"video" => video_query} = _query) do - q |> where([p, s, v], fragment("? @> ?", v.metadata, ^video_query)) + q |> where([d, s, v], fragment("? @> ?", v.metadata, ^video_query)) end defp where_query(q, %{"session" => session_query} = _query) do - q |> where([p, s, v], fragment("? @> ?", s.metadata, ^session_query)) + q |> where([d, s, v], fragment("? @> ?", s.metadata, ^session_query)) end defp where_query(q, query) do - q |> where([p, s, v], v.identifier == ^query) + q |> where([d, s, v], v.identifier == ^query) end defp select_details(query, interval) do query - |> select([p, s, v], %{ - interval: fragment("time_bucket('?', ?)", literal(^interval), p.timestamp), - elapsed_time: sum(p.elapsed_time), + |> select([d, s, v], %{ + interval: fragment("time_bucket('?', ?)", literal(^interval), d.timestamp), + elapsed_time: sum(d.elapsed_time), platform_mac: case_when(s.platform == :mac, 1, 0), platform_ios: case_when(s.platform == :ios, 1, 0), platform_android: case_when(s.platform == :android, 1, 0), @@ -204,20 +251,20 @@ defmodule MaveMetrics.API do {:ok, to} = DateTime.from_unix(to_timestamp) query - |> where([p, s, v], p.timestamp >= ^from) - |> where([p, s, v], p.timestamp <= ^to) + |> where([d, s, v], d.timestamp >= ^from) + |> where([d, s, v], d.timestamp <= ^to) end defp where_timeframe(query, timeframe) when is_number(timeframe) do {:ok, from} = DateTime.from_unix(timeframe) query - |> where([p, s, v], p.timestamp >= ^from) + |> where([d, s, v], d.timestamp >= ^from) end defp where_timeframe(query, timeframe) do query - |> where([p, s, v], p.timestamp >= fragment("now() - interval '?'", literal(^timeframe))) + |> where([d, s, v], d.timestamp >= fragment("now() - interval '?'", literal(^timeframe))) end defp key(%{} = query) do diff --git a/lib/mave_metrics/keys.ex b/lib/mave_metrics/keys.ex index 7bb3334..38714b3 100644 --- a/lib/mave_metrics/keys.ex +++ b/lib/mave_metrics/keys.ex @@ -29,7 +29,7 @@ defmodule MaveMetrics.Keys do def revoke_key(%{"key" => key}) do with %Key{} = key <- Key |> where([k], k.key == ^key) |> Repo.one(), - {:ok, _} <- key |> Key.changeset(%{disabled_at: DateTime.utc_now()}) |> Repo.update() do + {:ok, _} <- key |> Key.changeset(%{disabled_at: DateTime.utc_now()}) |> Repo.update() do :ok else _ -> :error @@ -51,6 +51,7 @@ defmodule MaveMetrics.Keys do |> case do {:ok, result} -> {:ok, result.key} + {:error, reason} -> {:error, reason} end diff --git a/lib/mave_metrics/session/play.ex b/lib/mave_metrics/session/duration.ex similarity index 71% rename from lib/mave_metrics/session/play.ex rename to lib/mave_metrics/session/duration.ex index a78c61b..b1de65b 100644 --- a/lib/mave_metrics/session/play.ex +++ b/lib/mave_metrics/session/duration.ex @@ -1,4 +1,4 @@ -defmodule MaveMetrics.Session.Play do +defmodule MaveMetrics.Session.Duration do use Ecto.Schema import Ecto.Changeset @@ -6,12 +6,14 @@ defmodule MaveMetrics.Session.Play do @foreign_key_type :binary_id @required_fields ~w(timestamp from)a - @optional_fields ~w(session_id to elapsed_time)a + @optional_fields ~w(session_id to elapsed_time type)a - schema "plays" do + schema "durations" do field :timestamp, :utc_datetime_usec, primary_key: true belongs_to :session, MaveMetrics.Session, primary_key: true + field :type, Ecto.Enum, values: [:play, :rebuffering, :fullscreen] + field :from, :float field :to, :float diff --git a/lib/mave_metrics/session/session.ex b/lib/mave_metrics/session/session.ex index b1c3b55..32dc046 100644 --- a/lib/mave_metrics/session/session.ex +++ b/lib/mave_metrics/session/session.ex @@ -9,8 +9,14 @@ defmodule MaveMetrics.Session do @optional_fields ~w(browser_type platform device_type metadata)a schema "sessions" do - field :browser_type, Ecto.Enum, values: [:edge, :ie, :chrome, :firefox, :opera, :safari, :brave, :other], default: :other - field :platform, Ecto.Enum, values: [:ios, :android, :mac, :windows, :linux, :other], default: :other + field :browser_type, Ecto.Enum, + values: [:edge, :ie, :chrome, :firefox, :opera, :safari, :brave, :other], + default: :other + + field :platform, Ecto.Enum, + values: [:ios, :android, :mac, :windows, :linux, :other], + default: :other + field :device_type, Ecto.Enum, values: [:mobile, :tablet, :desktop, :other], default: :other belongs_to :video, MaveMetrics.Video @@ -18,7 +24,7 @@ defmodule MaveMetrics.Session do has_many :events, MaveMetrics.Session.Event has_many :sources, MaveMetrics.Session.Source - has_many :plays, MaveMetrics.Session.Play + has_many :durations, MaveMetrics.Session.Duration field :metadata, :map diff --git a/lib/mave_metrics/stats.ex b/lib/mave_metrics/stats.ex index 9fc2533..4f6bb48 100644 --- a/lib/mave_metrics/stats.ex +++ b/lib/mave_metrics/stats.ex @@ -3,6 +3,8 @@ defmodule MaveMetrics.Stats do The Stats context. """ + @min_rebuffering_duration 1 + import Ecto.Query, warn: false alias MaveMetrics.Repo alias Ecto.Changeset @@ -10,7 +12,7 @@ defmodule MaveMetrics.Stats do alias MaveMetrics.Video alias MaveMetrics.Session alias MaveMetrics.Session.Event - alias MaveMetrics.Session.Play + alias MaveMetrics.Session.Duration alias MaveMetrics.Session.Track alias MaveMetrics.Session.Source alias MaveMetrics.Key @@ -20,13 +22,7 @@ defmodule MaveMetrics.Stats do case get_by_source_url_and_identifier_and_metadata(source_url, identifier, metadata) do %Video{id: _id} = video -> {:ok, video} - # if Morphix.equaliform?(video.metadata, metadata) do - # {:ok, video} - # else - # video - # |> Video.changeset(%{metadata: metadata}) - # |> Repo.update() - # end + _ -> create_video(%{source_uri: source_url, identifier: identifier, metadata: metadata}) end @@ -67,56 +63,83 @@ defmodule MaveMetrics.Stats do |> Repo.insert!() end - def get_last_play_event(session_id) do + def get_last_event(type, session_id) do Event |> where([e], e.session_id == ^session_id) - |> where([e], e.name == :play) + |> where([e], e.name == ^type) |> order_by([e], desc: e.timestamp) |> limit(1) |> Repo.one() end - def play_event_not_paused?(session_id) do - case get_last_play_event(session_id) do + def duration_not_closed?(type, session_id) do + counter_type = + case type do + :play -> + :pause + + :rebuffering_start -> + :rebuffering_end + + :fullscreen_enter -> + :fullscreen_exit + end + + case get_last_event(type, session_id) do nil -> false + event -> Event |> where([e], e.session_id == ^session_id) - |> where([e], e.name == :pause) + |> where([e], e.name == ^counter_type) |> where([e], e.timestamp > ^event.timestamp) |> limit(1) |> Repo.one() |> case do nil -> true - _ -> + + result -> false end end end - def create_play(attrs) do - %Play{} - |> Play.changeset(attrs) + def create_duration(attrs) do + %Duration{} + |> Duration.changeset(attrs) |> Repo.insert!() end - def get_last_play(session_id) do - Play - |> where([p], p.session_id == ^session_id) - |> where([p], is_nil(p.to)) - |> order_by([p], desc: p.from) + def get_last_duration(session_id, type \\ :play) do + Duration + |> where([d], d.session_id == ^session_id and d.type == ^type) + |> order_by([d], desc: d.timestamp) |> limit(1) |> Repo.one() end - def update_play(%Play{} = play, attrs) do - play - |> Play.changeset(attrs) + def update_duration(%Duration{} = duration, attrs) do + duration + |> Duration.changeset(attrs) |> Repo.update() end + def update_rebuffering(duration, elapsed_time) do + if elapsed_time > @min_rebuffering_duration do + update_duration(duration, %{ + elapsed_time: elapsed_time + }) + else + delete_duration(duration) + end + end + + def delete_duration(%Duration{} = duration) do + Repo.delete(duration) + end + def create_track(attrs) do %Track{} |> Track.changeset(attrs) diff --git a/lib/mave_metrics_web/channels/session_channel.ex b/lib/mave_metrics_web/channels/session_channel.ex index 2d0d64a..de9cf61 100644 --- a/lib/mave_metrics_web/channels/session_channel.ex +++ b/lib/mave_metrics_web/channels/session_channel.ex @@ -5,8 +5,12 @@ defmodule MaveMetricsWeb.SessionChannel do alias MaveMetrics.Keys @impl true - def join("session:" <> _id, %{"identifier" => identifier, "key" => key} = params, %{id: _session_id, assigns: %{ua: ua}} = socket) do - with %UAInspector.Result.Bot{name: _name} <- UAInspector.parse(socket.assigns.ua) do + def join( + "session:" <> _id, + %{"identifier" => identifier, "key" => key} = params, + %{id: _session_id, assigns: %{ua: ua}} = socket + ) do + with %UAInspector.Result.Bot{name: _name} <- UAInspector.parse(ua) do {:error, %{reason: "bot"}} else parsed_ua -> @@ -15,32 +19,39 @@ defmodule MaveMetricsWeb.SessionChannel do source_url = params["source_url"] || socket.assigns.source_url # we're not storing the complete user-agent string to make it impossible to make a fingerprint - session_attrs = %{ - metadata: params["session_data"] - } |> Map.merge(parsed_ua |> session_info()) + session_attrs = + %{ + metadata: params["session_data"] + } + |> Map.merge(parsed_ua |> session_info()) {:ok, video} = Stats.find_or_create_video(source_url, identifier, params["metadata"]) {:ok, session} = Stats.create_session(video, key, session_attrs) {:ok, socket |> assign(:session_id, session.id) |> monitor(self())} - {:error, reason} -> + + {:error, _reason} -> {:error, %{reason: "invalid key"}} end end end @impl true - def join(session, params, _socket) do + def join(_session, _params, _socket) do {:error, %{reason: "missing parameters and/or invalid host"}} end @impl true - def handle_in("event", %{"name" => "play", "from" => from, "timestamp" => timestamp} = params, %{assigns: %{session_id: session_id}} = socket) do + def handle_in( + "event", + %{"name" => "play", "from" => from, "timestamp" => timestamp} = params, + %{assigns: %{session_id: session_id}} = socket + ) do create_event(params, socket) timestamp = DateTime.from_unix!(timestamp, :millisecond) - Stats.create_play(%{ + Stats.create_duration(%{ from: from, session_id: session_id, timestamp: timestamp @@ -50,15 +61,19 @@ defmodule MaveMetricsWeb.SessionChannel do end @impl true - def handle_in("event", %{"name" => "pause", "to" => to} = params, %{assigns: %{session_id: session_id}} = socket) do + def handle_in( + "event", + %{"name" => "pause", "to" => to} = params, + %{assigns: %{session_id: session_id}} = socket + ) do create_event(params, socket) - play = Stats.get_last_play(session_id) + duration = Stats.get_last_duration(session_id) - if play do - Stats.update_play(play, %{ + if duration do + Stats.update_duration(duration, %{ to: to, - elapsed_time: to - play.from + elapsed_time: to - duration.from }) end @@ -66,7 +81,53 @@ defmodule MaveMetricsWeb.SessionChannel do end @impl true - def handle_in("event", %{"name" => "track_set", "language" => language, "timestamp" => timestamp} = params, %{assigns: %{session_id: session_id}} = socket) do + def handle_in( + "event", + %{"name" => "rebuffering_start", "from" => from, "timestamp" => timestamp} = params, + %{assigns: %{session_id: session_id}} = socket + ) do + create_event(params, socket) + + timestamp = DateTime.from_unix!(timestamp, :millisecond) + + Stats.create_duration(%{ + from: from, + session_id: session_id, + timestamp: timestamp, + type: :rebuffering + }) + + {:noreply, socket} + end + + @impl true + def handle_in( + "event", + %{"name" => "rebuffering_end", "timestamp" => timestamp} = params, + %{assigns: %{session_id: session_id}} = socket + ) do + create_event(params, socket) + duration = Stats.get_last_duration(session_id, :rebuffering) + + if duration do + to = DateTime.from_unix!(timestamp, :millisecond) + elapsed_time = DateTime.diff(to, duration.timestamp, :millisecond) / 1000 + + Stats.update_rebuffering(duration, elapsed_time) + end + + {:noreply, socket} + end + + # TODO: + # implement fullscreen_enter, fullscreen_exit duration + + @impl true + def handle_in( + "event", + %{"name" => "track_set", "language" => language, "timestamp" => timestamp} = params, + %{assigns: %{session_id: session_id}} = socket + ) do create_event(params, socket) timestamp = DateTime.from_unix!(timestamp, :millisecond) @@ -86,8 +147,11 @@ defmodule MaveMetricsWeb.SessionChannel do {:noreply, socket} end - defp create_event(%{"name" => name, "timestamp" => timestamp}, %{assigns: %{session_id: session_id}}) do + defp create_event(%{"name" => name, "timestamp" => timestamp}, %{ + assigns: %{session_id: session_id} + }) do timestamp = DateTime.from_unix!(timestamp, :millisecond) + Stats.create_event(%{ name: name, timestamp: timestamp, @@ -111,22 +175,38 @@ defmodule MaveMetricsWeb.SessionChannel do end defp on_disconnect(%{assigns: %{session_id: session_id}} = _socket) do - with Stats.play_event_not_paused?(session_id), - %{timestamp: timestamp, from: from} = play <- Stats.get_last_play(session_id) do - now = DateTime.utc_now() - elapsed_time = DateTime.diff(now, timestamp) - to = from + elapsed_time - - Stats.update_play(play, %{to: to, elapsed_time: elapsed_time}) - - Stats.create_event(%{ - name: :pause, - timestamp: timestamp, - session_id: session_id - }) - else - _ -> nil - end + with true <- Stats.duration_not_closed?(:play, session_id), + %{timestamp: timestamp, from: from} = duration <- Stats.get_last_duration(session_id) do + now = DateTime.utc_now() + elapsed_time = DateTime.diff(now, timestamp) + to = from + elapsed_time + + Stats.update_duration(duration, %{to: to, elapsed_time: elapsed_time}) + + Stats.create_event(%{ + name: :pause, + timestamp: timestamp, + session_id: session_id + }) + else + _ -> nil + end + + with true <- Stats.duration_not_closed?(:rebuffering_start, session_id), + %{timestamp: timestamp} = duration <- Stats.get_last_duration(session_id, :rebuffering) do + now = DateTime.utc_now() + elapsed_time = DateTime.diff(now, timestamp) + + Stats.update_rebuffering(duration, elapsed_time) + + Stats.create_event(%{ + name: :rebuffering_end, + timestamp: timestamp, + session_id: session_id + }) + else + _ -> nil + end end defp on_disconnect(_socket), do: nil @@ -139,19 +219,36 @@ defmodule MaveMetricsWeb.SessionChannel do } end - defp get_browser_type(%{name: browser}) when browser in ["Chrome Mobile", "Chrome Mobile iOS", "Chrome"] , do: :chrome - defp get_browser_type(%{name: browser}) when browser in ["Firefox Mobile", "Firefox Mobile iOS", "Firefox"] , do: :firefox - defp get_browser_type(%{name: browser}) when browser in ["Mobile Safari", "Safari Technology Preview", "Safari"] , do: :safari - defp get_browser_type(%{name: browser}) when browser in ["Opera Mobile", "Opera"] , do: :opera - defp get_browser_type(%{name: browser}) when browser in ["Edge Mobile", "Edge"] , do: :edge - defp get_browser_type(%{name: browser}) when browser in ["IE Mobile", "Internet Explorer"] , do: :ie - defp get_browser_type(%{name: browser}) when browser in ["Brave"] , do: :brave + defp get_browser_type(%{name: browser}) + when browser in ["Chrome Mobile", "Chrome Mobile iOS", "Chrome"], + do: :chrome + + defp get_browser_type(%{name: browser}) + when browser in ["Firefox Mobile", "Firefox Mobile iOS", "Firefox"], + do: :firefox + + defp get_browser_type(%{name: browser}) + when browser in ["Mobile Safari", "Safari Technology Preview", "Safari"], + do: :safari + + defp get_browser_type(%{name: browser}) when browser in ["Opera Mobile", "Opera"], do: :opera + defp get_browser_type(%{name: browser}) when browser in ["Edge Mobile", "Edge"], do: :edge + + defp get_browser_type(%{name: browser}) when browser in ["IE Mobile", "Internet Explorer"], + do: :ie + + defp get_browser_type(%{name: browser}) when browser in ["Brave"], do: :brave defp get_browser_type(_browser), do: :other - defp get_platform(platform) when platform in ["Windows", "Mac", "Linux", "iOS", "Android"], do: platform |> String.downcase() |> String.to_atom + defp get_platform(platform) when platform in ["Windows", "Mac", "Linux", "iOS", "Android"], + do: platform |> String.downcase() |> String.to_atom() + defp get_platform(_platform), do: :other defp get_device(%{type: device}) when device == "smartphone", do: :mobile - defp get_device(%{type: device}) when device in ["tablet", "desktop"], do: device |> String.to_atom + + defp get_device(%{type: device}) when device in ["tablet", "desktop"], + do: device |> String.to_atom() + defp get_device(_device), do: :other end diff --git a/mix.lock b/mix.lock index 2cf1062..6cf3dc0 100644 --- a/mix.lock +++ b/mix.lock @@ -1,18 +1,18 @@ %{ "browser": {:hex, :browser, "0.5.1", "45f4e2d057bf14125b4a20d0b849bc72f97bdd93eb642e9374cb2c757859a3e7", [:mix], [{:plug, "~> 1.2", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "4cb3708b27b866783e136fa253ef66a4b8f65051ceff05cf582312033e645e26"}, "castore": {:hex, :castore, "0.1.22", "4127549e411bedd012ca3a308dede574f43819fe9394254ca55ab4895abfa1a2", [:mix], [], "hexpm", "c17576df47eb5aa1ee40cc4134316a99f5cad3e215d5c77b8dd3cfef12a22cac"}, - "certifi": {:hex, :certifi, "2.9.0", "6f2a475689dd47f19fb74334859d460a2dc4e3252a3324bd2111b8f0429e7e21", [:rebar3], [], "hexpm", "266da46bdb06d6c6d35fde799bcb28d36d985d424ad7c08b5bb48f5b5cdd4641"}, + "certifi": {:hex, :certifi, "2.12.0", "2d1cca2ec95f59643862af91f001478c9863c2ac9cb6e2f89780bfd8de987329", [:rebar3], [], "hexpm", "ee68d85df22e554040cdb4be100f33873ac6051387baf6a8f6ce82272340ff1c"}, "connection": {:hex, :connection, "1.1.0", "ff2a49c4b75b6fb3e674bfc5536451607270aac754ffd1bdfe175abe4a6d7a68", [:mix], [], "hexpm", "722c1eb0a418fbe91ba7bd59a47e28008a189d47e37e0e7bb85585a016b2869c"}, "cors_plug": {:hex, :cors_plug, "3.0.3", "7c3ac52b39624bc616db2e937c282f3f623f25f8d550068b6710e58d04a0e330", [:mix], [{:plug, "~> 1.13", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "3f2d759e8c272ed3835fab2ef11b46bddab8c1ab9528167bd463b6452edf830d"}, - "cowboy": {:hex, :cowboy, "2.9.0", "865dd8b6607e14cf03282e10e934023a1bd8be6f6bacf921a7e2a96d800cd452", [:make, :rebar3], [{:cowlib, "2.11.0", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "2c729f934b4e1aa149aff882f57c6372c15399a20d54f65c8d67bef583021bde"}, + "cowboy": {:hex, :cowboy, "2.10.0", "ff9ffeff91dae4ae270dd975642997afe2a1179d94b1887863e43f681a203e26", [:make, :rebar3], [{:cowlib, "2.12.1", [hex: :cowlib, repo: "hexpm", optional: false]}, {:ranch, "1.8.0", [hex: :ranch, repo: "hexpm", optional: false]}], "hexpm", "3afdccb7183cc6f143cb14d3cf51fa00e53db9ec80cdcd525482f5e99bc41d6b"}, "cowboy_telemetry": {:hex, :cowboy_telemetry, "0.4.0", "f239f68b588efa7707abce16a84d0d2acf3a0f50571f8bb7f56a15865aae820c", [:rebar3], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7d98bac1ee4565d31b62d59f8823dfd8356a169e7fcbb83831b8a5397404c9de"}, - "cowlib": {:hex, :cowlib, "2.11.0", "0b9ff9c346629256c42ebe1eeb769a83c6cb771a6ee5960bd110ab0b9b872063", [:make, :rebar3], [], "hexpm", "2b3e9da0b21c4565751a6d4901c20d1b4cc25cbb7fd50d91d2ab6dd287bc86a9"}, - "db_connection": {:hex, :db_connection, "2.4.3", "3b9aac9f27347ec65b271847e6baeb4443d8474289bd18c1d6f4de655b70c94d", [:mix], [{:connection, "~> 1.0", [hex: :connection, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c127c15b0fa6cfb32eed07465e05da6c815b032508d4ed7c116122871df73c12"}, - "decimal": {:hex, :decimal, "2.0.0", "a78296e617b0f5dd4c6caf57c714431347912ffb1d0842e998e9792b5642d697", [:mix], [], "hexpm", "34666e9c55dea81013e77d9d87370fe6cb6291d1ef32f46a1600230b1d44f577"}, + "cowlib": {:hex, :cowlib, "2.12.1", "a9fa9a625f1d2025fe6b462cb865881329b5caff8f1854d1cbc9f9533f00e1e1", [:make, :rebar3], [], "hexpm", "163b73f6367a7341b33c794c4e88e7dbfe6498ac42dcd69ef44c5bc5507c8db0"}, + "db_connection": {:hex, :db_connection, "2.5.0", "bb6d4f30d35ded97b29fe80d8bd6f928a1912ca1ff110831edcd238a1973652c", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "c92d5ba26cd69ead1ff7582dbb860adeedfff39774105a4f1c92cbb654b55aa2"}, + "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"}, "decorator": {:hex, :decorator, "1.4.0", "a57ac32c823ea7e4e67f5af56412d12b33274661bb7640ec7fc882f8d23ac419", [:mix], [], "hexpm", "0a07cedd9083da875c7418dea95b78361197cf2bf3211d743f6f7ce39656597f"}, "earmark_parser": {:hex, :earmark_parser, "1.4.30", "0b938aa5b9bafd455056440cdaa2a79197ca5e693830b4a982beada840513c5f", [:mix], [], "hexpm", "3b5385c2d36b0473d0b206927b841343d25adb14f95f0110062506b300cd5a1b"}, - "ecto": {:hex, :ecto, "3.9.4", "3ee68e25dbe0c36f980f1ba5dd41ee0d3eb0873bccae8aeaf1a2647242bffa35", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "de5f988c142a3aa4ec18b85a4ec34a2390b65b24f02385c1144252ff6ff8ee75"}, - "ecto_sql": {:hex, :ecto_sql, "3.9.2", "34227501abe92dba10d9c3495ab6770e75e79b836d114c41108a4bf2ce200ad5", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.9.2", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "1eb5eeb4358fdbcd42eac11c1fbd87e3affd7904e639d77903c1358b2abd3f70"}, + "ecto": {:hex, :ecto, "3.10.3", "eb2ae2eecd210b4eb8bece1217b297ad4ff824b4384c0e3fdd28aaf96edd6135", [:mix], [{:decimal, "~> 1.6 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "44bec74e2364d491d70f7e42cd0d690922659d329f6465e89feb8a34e8cd3433"}, + "ecto_sql": {:hex, :ecto_sql, "3.10.2", "6b98b46534b5c2f8b8b5f03f126e75e2a73c64f3c071149d32987a5378b0fdbd", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10.0", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.6.0", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.16.0 or ~> 0.17.0 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "68c018debca57cb9235e3889affdaec7a10616a4e3a80c99fa1d01fdafaa9007"}, "esbuild": {:hex, :esbuild, "0.6.1", "a774bfa7b4512a1211bf15880b462be12a4c48ed753a170c68c63b2c95888150", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "569f7409fb5a932211573fc20e2a930a0d5cf3377c5b4f6506c651b1783a1678"}, "ex_doc": {:hex, :ex_doc, "0.29.1", "b1c652fa5f92ee9cf15c75271168027f92039b3877094290a75abcaac82a9f77", [:mix], [{:earmark_parser, "~> 1.4.19", [hex: :earmark_parser, repo: "hexpm", optional: false]}, {:makeup_elixir, "~> 0.14", [hex: :makeup_elixir, repo: "hexpm", optional: false]}, {:makeup_erlang, "~> 0.1", [hex: :makeup_erlang, repo: "hexpm", optional: false]}], "hexpm", "b7745fa6374a36daf484e2a2012274950e084815b936b1319aeebcf7809574f6"}, "expo": {:hex, :expo, "0.3.0", "13127c1d5f653b2927f2616a4c9ace5ae372efd67c7c2693b87fd0fdc30c6feb", [:mix], [], "hexpm", "fb3cd4bf012a77bc1608915497dae2ff684a06f0fa633c7afa90c4d72b881823"}, @@ -20,18 +20,18 @@ "finch": {:hex, :finch, "0.14.0", "619bfdee18fc135190bf590356c4bf5d5f71f916adb12aec94caa3fa9267a4bc", [:mix], [{:castore, "~> 0.1", [hex: :castore, repo: "hexpm", optional: false]}, {:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.3", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 0.2.6", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "5459acaf18c4fdb47a8c22fb3baff5d8173106217c8e56c5ba0b93e66501a8dd"}, "floki": {:hex, :floki, "0.34.1", "b1f9c413d91140230788b173906065f6f8906bbbf5b3f0d3c626301aeeef44c5", [:mix], [], "hexpm", "cc9b62312a45c1239ca8f65e05377ef8c646f3d7712e5727a9b47c43c946e885"}, "gettext": {:hex, :gettext, "0.22.0", "a25d71ec21b1848957d9207b81fd61cb25161688d282d58bdafef74c2270bdc4", [:mix], [{:expo, "~> 0.3.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "cb0675141576f73720c8e49b4f0fd3f2c69f0cd8c218202724d4aebab8c70ace"}, - "hackney": {:hex, :hackney, "1.18.1", "f48bf88f521f2a229fc7bae88cf4f85adc9cd9bcf23b5dc8eb6a1788c662c4f6", [:rebar3], [{:certifi, "~> 2.9.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.3.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "a4ecdaff44297e9b5894ae499e9a070ea1888c84afdd1fd9b7b2bc384950128e"}, + "hackney": {:hex, :hackney, "1.20.1", "8d97aec62ddddd757d128bfd1df6c5861093419f8f7a4223823537bad5d064e2", [:rebar3], [{:certifi, "~> 2.12.0", [hex: :certifi, repo: "hexpm", optional: false]}, {:idna, "~> 6.1.0", [hex: :idna, repo: "hexpm", optional: false]}, {:metrics, "~> 1.0.0", [hex: :metrics, repo: "hexpm", optional: false]}, {:mimerl, "~> 1.1", [hex: :mimerl, repo: "hexpm", optional: false]}, {:parse_trans, "3.4.1", [hex: :parse_trans, repo: "hexpm", optional: false]}, {:ssl_verify_fun, "~> 1.1.0", [hex: :ssl_verify_fun, repo: "hexpm", optional: false]}, {:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "fe9094e5f1a2a2c0a7d10918fee36bfec0ec2a979994cff8cfe8058cd9af38e3"}, "heroicons": {:hex, :heroicons, "0.5.2", "a7ae72460ecc4b74a4ba9e72f0b5ac3c6897ad08968258597da11c2b0b210683", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.2", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "7ef96f455c1c136c335f1da0f1d7b12c34002c80a224ad96fc0ebf841a6ffef5"}, "hpax": {:hex, :hpax, "0.1.2", "09a75600d9d8bbd064cdd741f21fc06fc1f4cf3d0fcc335e5aa19be1a7235c84", [:mix], [], "hexpm", "2c87843d5a23f5f16748ebe77969880e29809580efdaccd615cd3bed628a8c13"}, "idna": {:hex, :idna, "6.1.1", "8a63070e9f7d0c62eb9d9fcb360a7de382448200fbbd1b106cc96d3d8099df8d", [:rebar3], [{:unicode_util_compat, "~> 0.7.0", [hex: :unicode_util_compat, repo: "hexpm", optional: false]}], "hexpm", "92376eb7894412ed19ac475e4a86f7b413c1b9fbb5bd16dccd57934157944cea"}, - "jason": {:hex, :jason, "1.4.0", "e855647bc964a44e2f67df589ccf49105ae039d4179db7f6271dfd3843dc27e6", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "79a3791085b2a0f743ca04cec0f7be26443738779d09302e01318f97bdb82121"}, + "jason": {:hex, :jason, "1.4.1", "af1504e35f629ddcdd6addb3513c3853991f694921b1b9368b0bd32beb9f1b63", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "fbb01ecdfd565b56261302f7e1fcc27c4fb8f32d56eab74db621fc154604a7a1"}, "json_diff": {:hex, :json_diff, "0.1.3", "c80d5ca5416e785867e765e906e9a91b7efc35bfd505af276654d108f4995736", [:mix], [], "hexpm", "a5332e8293e7e9f384d34ea44645d7961334db73739165178fd4a7728d06f7d1"}, "live_state": {:hex, :live_state, "0.6.1", "dfacd52ee407cc47dc4176f909fe1ae60f919046a9064abbd34bdc93b1865e05", [:mix], [{:ex_doc, ">= 0.0.0", [hex: :ex_doc, repo: "hexpm", optional: false]}, {:json_diff, ">= 0.0.0", [hex: :json_diff, repo: "hexpm", optional: false]}, {:phoenix, ">= 1.5.7", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "714e0f5b838a905ae76e4cb542f42165b5f3da38c0bb3126904bdd69746a2814"}, "makeup": {:hex, :makeup, "1.1.0", "6b67c8bc2882a6b6a445859952a602afc1a41c2e08379ca057c0f525366fc3ca", [:mix], [{:nimble_parsec, "~> 1.2.2 or ~> 1.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "0a45ed501f4a8897f580eabf99a2e5234ea3e75a4373c8a52824f6e873be57a6"}, "makeup_elixir": {:hex, :makeup_elixir, "0.16.0", "f8c570a0d33f8039513fbccaf7108c5d750f47d8defd44088371191b76492b0b", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.2.3", [hex: :nimble_parsec, repo: "hexpm", optional: false]}], "hexpm", "28b2cbdc13960a46ae9a8858c4bebdec3c9a6d7b4b9e7f4ed1502f8159f338e7"}, "makeup_erlang": {:hex, :makeup_erlang, "0.1.1", "3fcb7f09eb9d98dc4d208f49cc955a34218fc41ff6b84df7c75b3e6e533cc65f", [:mix], [{:makeup, "~> 1.0", [hex: :makeup, repo: "hexpm", optional: false]}], "hexpm", "174d0809e98a4ef0b3309256cbf97101c6ec01c4ab0b23e926a9e17df2077cbb"}, "metrics": {:hex, :metrics, "1.0.1", "25f094dea2cda98213cecc3aeff09e940299d950904393b2a29d191c346a8486", [:rebar3], [], "hexpm", "69b09adddc4f74a40716ae54d140f93beb0fb8978d8636eaded0c31b6f099f16"}, - "mime": {:hex, :mime, "2.0.3", "3676436d3d1f7b81b5a2d2bd8405f412c677558c81b1c92be58c00562bb59095", [:mix], [], "hexpm", "27a30bf0db44d25eecba73755acf4068cbfe26a4372f9eb3e4ea3a45956bff6b"}, + "mime": {:hex, :mime, "2.0.5", "dc34c8efd439abe6ae0343edbb8556f4d63f178594894720607772a041b04b02", [:mix], [], "hexpm", "da0d64a365c45bc9935cc5c8a7fc5e49a0e0f9932a761c55d6c52b142780a05c"}, "mimerl": {:hex, :mimerl, "1.2.0", "67e2d3f571088d5cfd3e550c383094b47159f3eee8ffa08e64106cdf5e981be3", [:rebar3], [], "hexpm", "f278585650aa581986264638ebf698f8bb19df297f66ad91b18910dfc6e19323"}, "mint": {:hex, :mint, "1.4.2", "50330223429a6e1260b2ca5415f69b0ab086141bc76dc2fbf34d7c389a6675b2", [:mix], [{:castore, "~> 0.1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "ce75a5bbcc59b4d7d8d70f8b2fc284b1751ffb35c7b6a6302b5192f8ab4ddd80"}, "mix_test_watch": {:hex, :mix_test_watch, "1.1.0", "330bb91c8ed271fe408c42d07e0773340a7938d8a0d281d57a14243eae9dc8c3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}], "hexpm", "52b6b1c476cbb70fd899ca5394506482f12e5f6b0d6acff9df95c7f1e0812ec3"}, @@ -40,32 +40,32 @@ "nimble_options": {:hex, :nimble_options, "0.5.2", "42703307b924880f8c08d97719da7472673391905f528259915782bb346e0a1b", [:mix], [], "hexpm", "4da7f904b915fd71db549bcdc25f8d56f378ef7ae07dc1d372cbe72ba950dce0"}, "nimble_parsec": {:hex, :nimble_parsec, "1.2.3", "244836e6e3f1200c7f30cb56733fd808744eca61fd182f731eac4af635cc6d0b", [:mix], [], "hexpm", "c8d789e39b9131acf7b99291e93dae60ab48ef14a7ee9d58c6964f59efb570b0"}, "nimble_pool": {:hex, :nimble_pool, "0.2.6", "91f2f4c357da4c4a0a548286c84a3a28004f68f05609b4534526871a22053cde", [:mix], [], "hexpm", "1c715055095d3f2705c4e236c18b618420a35490da94149ff8b580a2144f653f"}, - "parse_trans": {:hex, :parse_trans, "3.3.1", "16328ab840cc09919bd10dab29e431da3af9e9e7e7e6f0089dd5a2d2820011d8", [:rebar3], [], "hexpm", "07cd9577885f56362d414e8c4c4e6bdf10d43a8767abb92d24cbe8b24c54888b"}, - "phoenix": {:hex, :phoenix, "1.7.0-rc.2", "8faaff6f699aad2fe6a003c627da65d0864c868a4c10973ff90abfd7286c1f27", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.4", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "71abde2f67330c55b625dcc0e42bf76662dbadc7553c4f545c2f3759f40f7487"}, + "parse_trans": {:hex, :parse_trans, "3.4.1", "6e6aa8167cb44cc8f39441d05193be6e6f4e7c2946cb2759f015f8c56b76e5ff", [:rebar3], [], "hexpm", "620a406ce75dada827b82e453c19cf06776be266f5a67cff34e1ef2cbb60e49a"}, + "phoenix": {:hex, :phoenix, "1.7.8", "80cf70507f892843f5b265a89b74327141c71cc0071898fca0d150015d67ffd6", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "7549e3a2f121a232dfbd88ef8dcaeccd671fb09e5fd53ec241a178f248b2c629"}, "phoenix_ecto": {:hex, :phoenix_ecto, "4.4.0", "0672ed4e4808b3fbed494dded89958e22fb882de47a97634c0b13e7b0b5f7720", [:mix], [{:ecto, "~> 3.3", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "09864e558ed31ee00bd48fcc1d4fc58ae9678c9e81649075431e69dbabb43cc1"}, - "phoenix_html": {:hex, :phoenix_html, "3.3.0", "bf451c71ebdaac8d2f40d3b703435e819ccfbb9ff243140ca3bd10c155f134cc", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "272c5c1533499f0132309936c619186480bafcc2246588f99a69ce85095556ef"}, + "phoenix_html": {:hex, :phoenix_html, "3.3.3", "380b8fb45912b5638d2f1d925a3771b4516b9a78587249cabe394e0a5d579dc9", [:mix], [{:plug, "~> 1.5", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "923ebe6fec6e2e3b3e569dfbdc6560de932cd54b000ada0208b5f45024bdd76c"}, "phoenix_live_dashboard": {:hex, :phoenix_live_dashboard, "0.7.2", "97cc4ff2dba1ebe504db72cb45098cb8e91f11160528b980bd282cc45c73b29c", [:mix], [{:ecto, "~> 3.6.2 or ~> 3.7", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_mysql_extras, "~> 0.5", [hex: :ecto_mysql_extras, repo: "hexpm", optional: true]}, {:ecto_psql_extras, "~> 0.7", [hex: :ecto_psql_extras, repo: "hexpm", optional: true]}, {:mime, "~> 1.6 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.3", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}, {:telemetry_metrics, "~> 0.6 or ~> 1.0", [hex: :telemetry_metrics, repo: "hexpm", optional: false]}], "hexpm", "0e5fdf063c7a3b620c566a30fcf68b7ee02e5e46fe48ee46a6ec3ba382dc05b7"}, "phoenix_live_reload": {:hex, :phoenix_live_reload, "1.4.1", "2aff698f5e47369decde4357ba91fc9c37c6487a512b41732818f2204a8ef1d3", [:mix], [{:file_system, "~> 0.2.1 or ~> 0.3", [hex: :file_system, repo: "hexpm", optional: false]}, {:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}], "hexpm", "9bffb834e7ddf08467fe54ae58b5785507aaba6255568ae22b4d46e2bb3615ab"}, - "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.14", "349b491496baa964711bba676f542ba1cf379af28ed1a0b0e74d0448be888609", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "4bee150229d0169baae3fb0dfa98fe2cebf46b4db478836b3a3352bdf042e18b"}, - "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.1", "ba04e489ef03763bf28a17eb2eaddc2c20c6d217e2150a61e3298b0f4c2012b5", [:mix], [], "hexpm", "81367c6d1eea5878ad726be80808eb5a787a23dee699f96e72b1109c57cdd8d9"}, - "phoenix_template": {:hex, :phoenix_template, "1.0.1", "85f79e3ad1b0180abb43f9725973e3b8c2c3354a87245f91431eec60553ed3ef", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "157dc078f6226334c91cb32c1865bf3911686f8bcd6bcff86736f6253e6993ee"}, - "plug": {:hex, :plug, "1.14.0", "ba4f558468f69cbd9f6b356d25443d0b796fbdc887e03fa89001384a9cac638f", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "bf020432c7d4feb7b3af16a0c2701455cbbbb95e5b6866132cb09eb0c29adc14"}, - "plug_cowboy": {:hex, :plug_cowboy, "2.6.0", "d1cf12ff96a1ca4f52207c5271a6c351a4733f413803488d75b70ccf44aebec2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "073cf20b753ce6682ed72905cd62a2d4bd9bad1bf9f7feb02a1b8e525bd94fa6"}, - "plug_crypto": {:hex, :plug_crypto, "1.2.3", "8f77d13aeb32bfd9e654cb68f0af517b371fb34c56c9f2b58fe3df1235c1251a", [:mix], [], "hexpm", "b5672099c6ad5c202c45f5a403f21a3411247f164e4a8fab056e5cd8a290f4a2"}, - "postgrex": {:hex, :postgrex, "0.16.5", "fcc4035cc90e23933c5d69a9cd686e329469446ef7abba2cf70f08e2c4b69810", [:mix], [{:connection, "~> 1.1", [hex: :connection, repo: "hexpm", optional: false]}, {:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "edead639dc6e882618c01d8fc891214c481ab9a3788dfe38dd5e37fd1d5fb2e8"}, + "phoenix_live_view": {:hex, :phoenix_live_view, "0.18.18", "1f38fbd7c363723f19aad1a04b5490ff3a178e37daaf6999594d5f34796c47fc", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix, "~> 1.6.15 or ~> 1.7.0", [hex: :phoenix, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a5810d0472f3189ede6d2a95bda7f31c6113156b91784a3426cb0ab6a6d85214"}, + "phoenix_pubsub": {:hex, :phoenix_pubsub, "2.1.3", "3168d78ba41835aecad272d5e8cd51aa87a7ac9eb836eabc42f6e57538e3731d", [:mix], [], "hexpm", "bba06bc1dcfd8cb086759f0edc94a8ba2bc8896d5331a1e2c2902bf8e36ee502"}, + "phoenix_template": {:hex, :phoenix_template, "1.0.3", "32de561eefcefa951aead30a1f94f1b5f0379bc9e340bb5c667f65f1edfa4326", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "16f4b6588a4152f3cc057b9d0c0ba7e82ee23afa65543da535313ad8d25d8e2c"}, + "plug": {:hex, :plug, "1.15.1", "b7efd81c1a1286f13efb3f769de343236bd8b7d23b4a9f40d3002fc39ad8f74c", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "459497bd94d041d98d948054ec6c0b76feacd28eec38b219ca04c0de13c79d30"}, + "plug_cowboy": {:hex, :plug_cowboy, "2.6.1", "9a3bbfceeb65eff5f39dab529e5cd79137ac36e913c02067dba3963a26efe9b2", [:mix], [{:cowboy, "~> 2.7", [hex: :cowboy, repo: "hexpm", optional: false]}, {:cowboy_telemetry, "~> 0.3", [hex: :cowboy_telemetry, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "de36e1a21f451a18b790f37765db198075c25875c64834bcc82d90b309eb6613"}, + "plug_crypto": {:hex, :plug_crypto, "2.0.0", "77515cc10af06645abbfb5e6ad7a3e9714f805ae118fa1a70205f80d2d70fe73", [:mix], [], "hexpm", "53695bae57cc4e54566d993eb01074e4d894b65a3766f1c43e2c61a1b0f45ea9"}, + "postgrex": {:hex, :postgrex, "0.17.3", "c92cda8de2033a7585dae8c61b1d420a1a1322421df84da9a82a6764580c503d", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "946cf46935a4fdca7a81448be76ba3503cff082df42c6ec1ff16a4bdfbfb098d"}, "ranch": {:hex, :ranch, "1.8.0", "8c7a100a139fd57f17327b6413e4167ac559fbc04ca7448e9be9057311597a1d", [:make, :rebar3], [], "hexpm", "49fbcfd3682fab1f5d109351b61257676da1a2fdbe295904176d5e521a2ddfe5"}, "redirect": {:hex, :redirect, "0.4.0", "98b46053504ee517bc3ad2fd04c064b64b48d339e1e18266355b30c4f8bb52b0", [:mix], [{:phoenix, "~> 1.4", [hex: :phoenix, repo: "hexpm", optional: false]}, {:plug, "~> 1.8.3 or ~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}], "hexpm", "dfa29a8ecbad066ed0b73b34611cf24c78101719737f37bdf750f39197d67b97"}, "shards": {:hex, :shards, "1.1.0", "ed3032e63ae99f0eaa6d012b8b9f9cead48b9a810b3f91aeac266cfc4118eff6", [:make, :rebar3], [], "hexpm", "1d188e565a54a458a7a601c2fd1e74f5cfeba755c5a534239266d28b7ff124c7"}, - "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.6", "cf344f5692c82d2cd7554f5ec8fd961548d4fd09e7d22f5b62482e5aeaebd4b0", [:make, :mix, :rebar3], [], "hexpm", "bdb0d2471f453c88ff3908e7686f86f9be327d065cc1ec16fa4540197ea04680"}, + "ssl_verify_fun": {:hex, :ssl_verify_fun, "1.1.7", "354c321cf377240c7b8716899e182ce4890c5938111a1296add3ec74cf1715df", [:make, :mix, :rebar3], [], "hexpm", "fe4c190e8f37401d30167c8c405eda19469f34577987c76dde613e838bbc67f8"}, "swoosh": {:hex, :swoosh, "1.9.1", "0a5d7bf9954eb41d7e55525bc0940379982b090abbaef67cd8e1fd2ed7f8ca1a", [:mix], [{:cowboy, "~> 1.1 or ~> 2.4", [hex: :cowboy, repo: "hexpm", optional: true]}, {:ex_aws, "~> 2.1", [hex: :ex_aws, repo: "hexpm", optional: true]}, {:finch, "~> 0.6", [hex: :finch, repo: "hexpm", optional: true]}, {:gen_smtp, "~> 0.13 or ~> 1.0", [hex: :gen_smtp, repo: "hexpm", optional: true]}, {:hackney, "~> 1.9", [hex: :hackney, repo: "hexpm", optional: true]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mail, "~> 0.2", [hex: :mail, repo: "hexpm", optional: true]}, {:mime, "~> 1.1 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_cowboy, ">= 1.0.0", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.2 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "76dffff3ffcab80f249d5937a592eaef7cc49ac6f4cdd27e622868326ed6371e"}, "tailwind": {:hex, :tailwind, "0.1.10", "21ed80ae1f411f747ee513470578acaaa1d0eb40170005350c5b0b6d07e2d624", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}], "hexpm", "e0fc474dfa8ed7a4573851ac69c5fd3ca70fbb0a5bada574d1d657ebc6f2f1f1"}, "telemetry": {:hex, :telemetry, "1.2.1", "68fdfe8d8f05a8428483a97d7aab2f268aaff24b49e0f599faa091f1d4e7f61c", [:rebar3], [], "hexpm", "dad9ce9d8effc621708f99eac538ef1cbe05d6a874dd741de2e689c47feafed5"}, "telemetry_metrics": {:hex, :telemetry_metrics, "0.6.1", "315d9163a1d4660aedc3fee73f33f1d355dcc76c5c3ab3d59e76e3edf80eef1f", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "7be9e0871c41732c233be71e4be11b96e56177bf15dde64a8ac9ce72ac9834c6"}, "telemetry_poller": {:hex, :telemetry_poller, "1.0.0", "db91bb424e07f2bb6e73926fcafbfcbcb295f0193e0a00e825e589a0a47e8453", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "b3a24eafd66c3f42da30fc3ca7dda1e9d546c12250a2d60d7b81d264fbec4f6e"}, "timescale": {:hex, :timescale, "0.0.1-alpha.5", "ad840123c8462ff74ed32d05ddf410b954106cd53e31cceb426b5c48378d58f4", [:mix], [{:ecto_sql, "~> 3.8", [hex: :ecto_sql, repo: "hexpm", optional: false]}], "hexpm", "fa7af82c42166783e328a362de158f8d065fbe3c23b8bd770d4425b5ff9319df"}, - "ua_inspector": {:hex, :ua_inspector, "3.1.1", "96f45c5bcbf7d515b833572941811a4348b7ba4433f1660fb827953b66f8dbe2", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "9ceb674498154a16526f27f49fa9e9653a74ceb8986cbe2555c48ab48418fe74"}, + "ua_inspector": {:hex, :ua_inspector, "3.6.0", "da3d2231c1cbe798fc963a657b87634edee35c01222cbcf0746de2dcc86b3a64", [:mix], [{:hackney, "~> 1.0", [hex: :hackney, repo: "hexpm", optional: false]}, {:yamerl, "~> 0.7", [hex: :yamerl, repo: "hexpm", optional: false]}], "hexpm", "b531beaec6ff2445db79d080437bb8705e4781396aff28cf1c6bdb75b732152f"}, "unicode_util_compat": {:hex, :unicode_util_compat, "0.7.0", "bc84380c9ab48177092f43ac89e4dfa2c6d62b40b8bd132b1059ecc7232f9a78", [:rebar3], [], "hexpm", "25eee6d67df61960cf6a794239566599b09e17e668d3700247bc498638152521"}, - "websock": {:hex, :websock, "0.4.3", "184ac396bdcd3dfceb5b74c17d221af659dd559a95b1b92041ecb51c9b728093", [:mix], [], "hexpm", "5e4dd85f305f43fd3d3e25d70bec4a45228dfed60f0f3b072d8eddff335539cf"}, - "websock_adapter": {:hex, :websock_adapter, "0.4.5", "30038a3715067f51a9580562c05a3a8d501126030336ffc6edb53bf57d6d2d26", [:mix], [{:bandit, "~> 0.6", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.4", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "1d9812dc7e703c205049426fd4fe0852a247a825f91b099e53dc96f68bafe4c8"}, + "websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"}, + "websock_adapter": {:hex, :websock_adapter, "0.5.4", "7af8408e7ed9d56578539594d1ee7d8461e2dd5c3f57b0f2a5352d610ddde757", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d2c238c79c52cbe223fcdae22ca0bb5007a735b9e933870e241fce66afb4f4ab"}, "yamerl": {:hex, :yamerl, "0.10.0", "4ff81fee2f1f6a46f1700c0d880b24d193ddb74bd14ef42cb0bcf46e81ef2f8e", [:rebar3], [], "hexpm", "346adb2963f1051dc837a2364e4acf6eb7d80097c0f53cbdc3046ec8ec4b4e6e"}, } diff --git a/priv/repo/migrations/20231011091809_add_duration_type.exs b/priv/repo/migrations/20231011091809_add_duration_type.exs new file mode 100644 index 0000000..71e1695 --- /dev/null +++ b/priv/repo/migrations/20231011091809_add_duration_type.exs @@ -0,0 +1,13 @@ +defmodule MaveMetrics.Repo.Migrations.AddDurationType do + use Ecto.Migration + + def change do + rename table(:plays), to: table(:durations) + + execute "CREATE TYPE duration_type AS ENUM ('play', 'rebuffering', 'fullscreen')" + + alter table(:durations) do + add :type, :duration_type, default: "play" + end + end +end