mirror of
https://github.com/plausible/analytics.git
synced 2024-12-24 10:02:10 +03:00
Merge pull request #23 from plausible-insights/bounce-rate-by-referrer
Show bounce rate for referrers and pages
This commit is contained in:
commit
cfe3695ba8
@ -218,3 +218,7 @@ a {
|
||||
width: 1em;
|
||||
transform: translateY(0.15em);
|
||||
}
|
||||
|
||||
.table-striped tbody tr:nth-child(odd) {
|
||||
background-color: #f1f5f8;
|
||||
}
|
||||
|
@ -4,7 +4,6 @@ import { withRouter } from 'react-router-dom'
|
||||
import Modal from './modal'
|
||||
import * as api from '../../api'
|
||||
import numberFormatter from '../../number-formatter'
|
||||
import Bar from '../bar'
|
||||
import {parseQuery} from '../../query'
|
||||
|
||||
class PagesModal extends React.Component {
|
||||
@ -16,19 +15,25 @@ class PagesModal extends React.Component {
|
||||
componentDidMount() {
|
||||
const query = parseQuery(this.props.location.search, this.props.site)
|
||||
|
||||
api.get(`/api/stats/${this.props.site.domain}/pages`, query, {limit: 100})
|
||||
api.get(`/api/stats/${this.props.site.domain}/pages`, query, {limit: 100, include: 'bounce_rate'})
|
||||
.then((res) => this.setState({loading: false, pages: res}))
|
||||
}
|
||||
|
||||
formatBounceRate(page) {
|
||||
if (page.bounce_rate) {
|
||||
return page.bounce_rate + '%'
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
renderPage(page) {
|
||||
return (
|
||||
<React.Fragment key={page.name}>
|
||||
<div className="flex items-center justify-between my-2">
|
||||
<span>{ page.name }</span>
|
||||
<span>{numberFormatter(page.count)}</span>
|
||||
</div>
|
||||
<Bar count={page.count} all={this.state.pages} color="orange" />
|
||||
</React.Fragment>
|
||||
<tr className="text-sm" key={page.name}>
|
||||
<td className="p-2 truncate">{page.name}</td>
|
||||
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(page.count)}</td>
|
||||
<td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(page)}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
@ -43,13 +48,21 @@ class PagesModal extends React.Component {
|
||||
<header className="modal__header">
|
||||
<h1>Top pages</h1>
|
||||
</header>
|
||||
<div className="text-grey-darker text-lg ml-1 mt-1">by pageviews</div>
|
||||
|
||||
<div className="my-4 border-b border-grey-light"></div>
|
||||
<main className="modal__content">
|
||||
<div className="mt-8">
|
||||
{ this.state.pages.map(this.renderPage.bind(this)) }
|
||||
</div>
|
||||
<table className="w-full table-striped table-fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="p-2 text-xs tracking-wide font-bold text-grey-dark" align="left">Page url</th>
|
||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Pageviews</th>
|
||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Bounce rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ this.state.pages.map(this.renderPage.bind(this)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
@ -4,7 +4,6 @@ import { Link, withRouter } from 'react-router-dom'
|
||||
import Modal from './modal'
|
||||
import * as api from '../../api'
|
||||
import numberFormatter from '../../number-formatter'
|
||||
import Bar from '../bar'
|
||||
import {parseQuery, toHuman} from '../../query'
|
||||
|
||||
class ReferrerDrilldownModal extends React.Component {
|
||||
@ -17,19 +16,27 @@ class ReferrerDrilldownModal extends React.Component {
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
api.get(`/api/stats/${this.props.site.domain}/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100})
|
||||
api.get(`/api/stats/${this.props.site.domain}/referrers/${this.props.match.params.referrer}`, this.state.query, {limit: 100, include: 'bounce_rate'})
|
||||
.then((res) => this.setState({loading: false, referrers: res.referrers, totalVisitors: res.total_visitors}))
|
||||
}
|
||||
|
||||
formatBounceRate(ref) {
|
||||
if (ref.bounce_rate) {
|
||||
return ref.bounce_rate + '%'
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
renderReferrer(referrer) {
|
||||
return (
|
||||
<React.Fragment key={referrer.name}>
|
||||
<div className="flex items-center justify-between my-2">
|
||||
<a className="hover:underline truncate" target="_blank" style={{maxWidth: '80%'}} href={'//' + referrer.name}>{ referrer.name }</a>
|
||||
<span>{numberFormatter(referrer.count)}</span>
|
||||
</div>
|
||||
<Bar count={referrer.count} all={this.state.referrers} color="blue" />
|
||||
</React.Fragment>
|
||||
<tr className="text-sm" key={referrer.name}>
|
||||
<td className="p-2 truncate">
|
||||
<a className="hover:underline" target="_blank" href={'//' + referrer.name}>{ referrer.name }</a>
|
||||
</td>
|
||||
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(referrer.count)}</td>
|
||||
<td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(referrer)}</td>
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
@ -58,9 +65,18 @@ class ReferrerDrilldownModal extends React.Component {
|
||||
<h1 className="mb-0 leading-none">{this.state.totalVisitors} visitors from {this.props.match.params.referrer}<br /> {toHuman(this.state.query)}</h1>
|
||||
{this.renderGoalText()}
|
||||
|
||||
<div className="mt-8">
|
||||
{ this.state.referrers.map(this.renderReferrer.bind(this)) }
|
||||
</div>
|
||||
<table className="w-full table-striped table-fixed mt-4">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="p-2 text-xs tracking-wide font-bold text-grey-dark" align="left">Referrer</th>
|
||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Visitors</th>
|
||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Bounce rate</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ this.state.referrers.map(this.renderReferrer.bind(this)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
@ -4,31 +4,45 @@ import { Link, withRouter } from 'react-router-dom'
|
||||
import Modal from './modal'
|
||||
import * as api from '../../api'
|
||||
import numberFormatter from '../../number-formatter'
|
||||
import Bar from '../bar'
|
||||
import {parseQuery} from '../../query'
|
||||
|
||||
class ReferrersModal extends React.Component {
|
||||
constructor(props) {
|
||||
super(props)
|
||||
this.state = {loading: true}
|
||||
this.state = {
|
||||
loading: true,
|
||||
query: parseQuery(props.location.search, props.site)
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const query = parseQuery(this.props.location.search, this.props.site)
|
||||
const include = this.showBounceRate() ? 'bounce_rate' : null
|
||||
|
||||
api.get(`/api/stats/${this.props.site.domain}/referrers`, query, {limit: 100})
|
||||
api.get(`/api/stats/${this.props.site.domain}/referrers`, this.state.query, {limit: 100, include: include})
|
||||
.then((res) => this.setState({loading: false, referrers: res}))
|
||||
}
|
||||
|
||||
showBounceRate() {
|
||||
return !this.state.query.filters.goal
|
||||
}
|
||||
|
||||
formatBounceRate(page) {
|
||||
if (page.bounce_rate) {
|
||||
return page.bounce_rate + '%'
|
||||
} else {
|
||||
return '-'
|
||||
}
|
||||
}
|
||||
|
||||
renderReferrer(referrer) {
|
||||
return (
|
||||
<React.Fragment key={referrer.name}>
|
||||
<div className="flex items-center justify-between my-2">
|
||||
<tr className="text-sm" key={referrer.name}>
|
||||
<td className="p-2">
|
||||
<Link className="hover:underline truncate" style={{maxWidth: '80%'}} to={`/${this.props.site.domain}/referrers/${referrer.name}${window.location.search}`}>{ referrer.name }</Link>
|
||||
<span>{numberFormatter(referrer.count)}</span>
|
||||
</div>
|
||||
<Bar count={referrer.count} all={this.state.referrers} color="blue" />
|
||||
</React.Fragment>
|
||||
</td>
|
||||
<td className="p-2 w-32 font-medium" align="right">{numberFormatter(referrer.count)}</td>
|
||||
{this.showBounceRate() && <td className="p-2 w-32 font-medium" align="right">{this.formatBounceRate(referrer)}</td> }
|
||||
</tr>
|
||||
)
|
||||
}
|
||||
|
||||
@ -41,15 +55,23 @@ class ReferrersModal extends React.Component {
|
||||
return (
|
||||
<React.Fragment>
|
||||
<header className="modal__header">
|
||||
<h1>Referrers</h1>
|
||||
<h1>Top Referrers</h1>
|
||||
</header>
|
||||
<div className="text-grey-darker text-lg ml-1 mt-1">by visitors</div>
|
||||
|
||||
<div className="my-4 border-b border-grey-light"></div>
|
||||
<main className="modal__content">
|
||||
<div className="mt-8">
|
||||
{ this.state.referrers.map(this.renderReferrer.bind(this)) }
|
||||
</div>
|
||||
<table className="w-full table-striped table-fixed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th className="p-2 text-xs tracking-wide font-bold text-grey-dark" align="left">Referrer</th>
|
||||
<th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Visitors</th>
|
||||
{this.showBounceRate() && <th className="p-2 w-32 text-xs tracking-wide font-bold text-grey-dark" align="right">Bounce rate</th>}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{ this.state.referrers.map(this.renderReferrer.bind(this)) }
|
||||
</tbody>
|
||||
</table>
|
||||
</main>
|
||||
</React.Fragment>
|
||||
)
|
||||
|
@ -49,6 +49,7 @@ defmodule Plausible.Ingest.Session do
|
||||
hostname: event.hostname,
|
||||
user_id: event.user_id,
|
||||
new_visitor: event.new_visitor,
|
||||
entry_page: event.pathname,
|
||||
is_bounce: state[:is_bounce],
|
||||
length: length,
|
||||
referrer: event.referrer,
|
||||
|
@ -10,6 +10,7 @@ defmodule Plausible.Session do
|
||||
field :start, :naive_datetime, null: false
|
||||
field :length, :integer
|
||||
field :is_bounce, :boolean
|
||||
field :entry_page, :string
|
||||
|
||||
field :referrer, :string
|
||||
field :referrer_source, :string
|
||||
@ -23,7 +24,7 @@ defmodule Plausible.Session do
|
||||
|
||||
def changeset(session, attrs) do
|
||||
session
|
||||
|> cast(attrs, [:hostname, :referrer, :new_visitor, :user_id, :start, :length, :is_bounce, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|
||||
|> cast(attrs, [:hostname, :entry_page, :referrer, :new_visitor, :user_id, :start, :length, :is_bounce, :operating_system, :browser, :referrer_source, :country_code, :screen_size])
|
||||
|> validate_required([:hostname, :new_visitor, :user_id, :is_bounce, :start])
|
||||
end
|
||||
end
|
||||
|
@ -163,14 +163,60 @@ defmodule Plausible.Stats do
|
||||
))
|
||||
end
|
||||
|
||||
def top_referrers(site, query, limit \\ 5) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
def top_referrers(site, query, limit \\ 5, include \\ []) do
|
||||
referrers = Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.referrer_source, count: count(e.user_id, :distinct)},
|
||||
group_by: e.referrer_source,
|
||||
where: not is_nil(e.referrer_source),
|
||||
order_by: [desc: 2],
|
||||
limit: ^limit
|
||||
)
|
||||
|
||||
if "bounce_rate" in include do
|
||||
bounce_rates = bounce_rates_by_referrer_source(site, query, Enum.map(referrers, fn ref -> ref[:name] end))
|
||||
|
||||
Enum.map(referrers, fn referrer ->
|
||||
Map.put(referrer, :bounce_rate, bounce_rates[referrer[:name]])
|
||||
end)
|
||||
else
|
||||
referrers
|
||||
end
|
||||
end
|
||||
|
||||
defp bounce_rates_by_referrer_source(site, query, referrers) do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_referrer = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.referrer_source in ^referrers,
|
||||
group_by: s.referrer_source,
|
||||
select: {s.referrer_source, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_referrer = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
where: s.referrer_source in ^referrers,
|
||||
group_by: s.referrer_source,
|
||||
select: {s.referrer_source, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
Enum.reduce(referrers, %{}, fn referrer, acc ->
|
||||
total_sessions = Map.get(total_sessions_by_referrer, referrer, 0)
|
||||
bounced_sessions = Map.get(bounced_sessions_by_referrer, referrer, 0)
|
||||
|
||||
bounce_rate = if total_sessions > 0 do
|
||||
round(bounced_sessions / total_sessions * 100)
|
||||
end
|
||||
|
||||
Map.put(acc, referrer, bounce_rate)
|
||||
end)
|
||||
end
|
||||
|
||||
def visitors_from_referrer(site, query, referrer) do
|
||||
@ -181,23 +227,115 @@ defmodule Plausible.Stats do
|
||||
)
|
||||
end
|
||||
|
||||
def referrer_drilldown(site, query, referrer) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
def referrer_drilldown(site, query, referrer, include \\ []) do
|
||||
referring_urls = Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.referrer, count: count(e.user_id, :distinct)},
|
||||
group_by: e.referrer,
|
||||
where: e.referrer_source == ^referrer,
|
||||
order_by: [desc: 2],
|
||||
limit: 100
|
||||
)
|
||||
|
||||
if "bounce_rate" in include do
|
||||
bounce_rates = bounce_rates_by_referring_url(site, query, Enum.map(referring_urls, fn ref -> ref[:name] end))
|
||||
|
||||
Enum.map(referring_urls, fn url ->
|
||||
Map.put(url, :bounce_rate, bounce_rates[url[:name]])
|
||||
end)
|
||||
else
|
||||
referring_urls
|
||||
end
|
||||
end
|
||||
|
||||
def top_pages(site, query, limit \\ 5) do
|
||||
Repo.all(from e in base_query(site, query),
|
||||
defp bounce_rates_by_referring_url(site, query, referring_urls) do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.referrer in ^referring_urls,
|
||||
group_by: s.referrer,
|
||||
select: {s.referrer, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
where: s.referrer in ^referring_urls,
|
||||
group_by: s.referrer,
|
||||
select: {s.referrer, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
Enum.reduce(referring_urls, %{}, fn url, acc ->
|
||||
total_sessions = Map.get(total_sessions_by_url, url, 0)
|
||||
bounced_sessions = Map.get(bounced_sessions_by_url, url, 0)
|
||||
|
||||
bounce_rate = if total_sessions > 0 do
|
||||
round(bounced_sessions / total_sessions * 100)
|
||||
end
|
||||
|
||||
Map.put(acc, url, bounce_rate)
|
||||
end)
|
||||
end
|
||||
|
||||
def top_pages(site, query, limit \\ 5, include \\ []) do
|
||||
pages = Repo.all(from e in base_query(site, query),
|
||||
select: %{name: e.pathname, count: count(e.pathname)},
|
||||
group_by: e.pathname,
|
||||
order_by: [desc: count(e.pathname)],
|
||||
limit: ^limit
|
||||
)
|
||||
|
||||
if "bounce_rate" in include do
|
||||
bounce_rates = bounce_rates_by_page_url(site, query, Enum.map(pages, fn page -> page[:name] end))
|
||||
|
||||
Enum.map(pages, fn url ->
|
||||
Map.put(url, :bounce_rate, bounce_rates[url[:name]])
|
||||
end)
|
||||
else
|
||||
pages
|
||||
end
|
||||
end
|
||||
|
||||
defp bounce_rates_by_page_url(site, query, page_urls) do
|
||||
{first_datetime, last_datetime} = date_range_utc_boundaries(query.date_range, site.timezone)
|
||||
|
||||
total_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.entry_page in ^page_urls,
|
||||
group_by: s.entry_page,
|
||||
select: {s.entry_page, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
bounced_sessions_by_url = Repo.all(
|
||||
from s in Plausible.Session,
|
||||
where: s.hostname == ^site.domain,
|
||||
where: s.new_visitor,
|
||||
where: s.start >= ^first_datetime and s.start < ^last_datetime,
|
||||
where: s.is_bounce,
|
||||
where: s.entry_page in ^page_urls,
|
||||
group_by: s.entry_page,
|
||||
select: {s.entry_page, count(s.id)}
|
||||
) |> Enum.into(%{})
|
||||
|
||||
Enum.reduce(page_urls, %{}, fn url, acc ->
|
||||
total_sessions = Map.get(total_sessions_by_url, url, 0)
|
||||
bounced_sessions = Map.get(bounced_sessions_by_url, url, 0)
|
||||
|
||||
bounce_rate = if total_sessions > 0 do
|
||||
round(bounced_sessions / total_sessions * 100)
|
||||
end
|
||||
|
||||
Map.put(acc, url, bounce_rate)
|
||||
end)
|
||||
end
|
||||
|
||||
@available_screen_sizes ["Desktop", "Laptop", "Tablet", "Mobile"]
|
||||
|
@ -68,8 +68,9 @@ defmodule PlausibleWeb.Api.StatsController do
|
||||
def referrers(conn, params) do
|
||||
site = conn.assigns[:site]
|
||||
query = Stats.Query.from(site.timezone, params)
|
||||
include = if params["include"], do: String.split(params["include"], ","), else: []
|
||||
|
||||
json(conn, Stats.top_referrers(site, query, params["limit"] || 5))
|
||||
json(conn, Stats.top_referrers(site, query, params["limit"] || 5, include))
|
||||
end
|
||||
|
||||
|
||||
@ -101,8 +102,9 @@ defmodule PlausibleWeb.Api.StatsController do
|
||||
def referrer_drilldown(conn, %{"referrer" => referrer} = params) do
|
||||
site = conn.assigns[:site]
|
||||
query = Stats.Query.from(site.timezone, params)
|
||||
include = if params["include"], do: String.split(params["include"], ","), else: []
|
||||
|
||||
referrers = Stats.referrer_drilldown(site, query, referrer)
|
||||
referrers = Stats.referrer_drilldown(site, query, referrer, include)
|
||||
total_visitors = Stats.visitors_from_referrer(site, query, referrer)
|
||||
json(conn, %{referrers: referrers, total_visitors: total_visitors})
|
||||
end
|
||||
@ -110,8 +112,9 @@ defmodule PlausibleWeb.Api.StatsController do
|
||||
def pages(conn, params) do
|
||||
site = conn.assigns[:site]
|
||||
query = Stats.Query.from(site.timezone, params)
|
||||
include = if params["include"], do: String.split(params["include"], ","), else: []
|
||||
|
||||
json(conn, Stats.top_pages(site, query, params["limit"] || 5))
|
||||
json(conn, Stats.top_pages(site, query, params["limit"] || 5, include))
|
||||
end
|
||||
|
||||
def countries(conn, params) do
|
||||
|
@ -0,0 +1,25 @@
|
||||
defmodule Plausible.Repo.Migrations.AddEntryPageToSessions do
|
||||
use Ecto.Migration
|
||||
|
||||
def change do
|
||||
alter table(:sessions) do
|
||||
add :entry_page, :text
|
||||
end
|
||||
|
||||
execute """
|
||||
UPDATE sessions SET entry_page = pathname
|
||||
FROM events
|
||||
WHERE events.user_id = sessions.user_id
|
||||
AND events.name = 'pageview'
|
||||
AND events.new_visitor
|
||||
"""
|
||||
|
||||
execute """
|
||||
DELETE FROM sessions WHERE entry_page is null
|
||||
"""
|
||||
|
||||
alter table(:sessions) do
|
||||
modify :entry_page, :text, null: false
|
||||
end
|
||||
end
|
||||
end
|
@ -17,5 +17,21 @@ defmodule PlausibleWeb.Api.StatsController.PagesTest do
|
||||
%{"name" => "/contact", "count" => 1},
|
||||
]
|
||||
end
|
||||
|
||||
test "calculates bounce rate for pages", %{conn: conn, site: site} do
|
||||
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, pathname: "/", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, pathname: "/contact", timestamp: ~N[2019-01-01 02:00:00])
|
||||
|
||||
insert(:session, hostname: site.domain, entry_page: "/", is_bounce: true, start: ~N[2019-01-01 02:00:00])
|
||||
insert(:session, hostname: site.domain, entry_page: "/", is_bounce: false, start: ~N[2019-01-01 02:00:00])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/pages?period=day&date=2019-01-01&include=bounce_rate")
|
||||
|
||||
assert json_response(conn, 200) == [
|
||||
%{"name" => "/", "count" => 2, "bounce_rate" => 50},
|
||||
%{"name" => "/contact", "count" => 1, "bounce_rate" => nil},
|
||||
]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -19,6 +19,22 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
|
||||
]
|
||||
end
|
||||
|
||||
test "calculates bounce rate for referrers", %{conn: conn, site: site} do
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "Bing", timestamp: ~N[2019-01-01 02:00:00])
|
||||
|
||||
insert(:session, hostname: site.domain, referrer_source: "Google", is_bounce: true, start: ~N[2019-01-01 02:00:00])
|
||||
insert(:session, hostname: site.domain, referrer_source: "Google", is_bounce: false, start: ~N[2019-01-01 02:00:00])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/referrers?period=day&date=2019-01-01&include=bounce_rate")
|
||||
|
||||
assert json_response(conn, 200) == [
|
||||
%{"name" => "Google", "count" => 2, "bounce_rate" => 50},
|
||||
%{"name" => "Bing", "count" => 1, "bounce_rate" => nil},
|
||||
]
|
||||
end
|
||||
|
||||
test "filters referrers for a custom goal", %{conn: conn, site: site} do
|
||||
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
|
||||
insert(:event, name: "Signup", hostname: site.domain, referrer_source: "Google", timestamp: ~N[2019-01-01 02:00:00])
|
||||
@ -85,6 +101,25 @@ defmodule PlausibleWeb.Api.StatsController.ReferrersTest do
|
||||
}
|
||||
end
|
||||
|
||||
test "calculates bounce rate for referrer urls", %{conn: conn, site: site} do
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", timestamp: ~N[2019-01-01 02:00:00])
|
||||
insert(:pageview, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/", timestamp: ~N[2019-01-01 02:00:00])
|
||||
|
||||
insert(:session, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello", is_bounce: true, start: ~N[2019-01-01 02:00:00])
|
||||
insert(:session, hostname: site.domain, referrer_source: "10words", referrer: "10words.io/hello",is_bounce: false, start: ~N[2019-01-01 02:00:00])
|
||||
|
||||
conn = get(conn, "/api/stats/#{site.domain}/referrers/10words?period=day&date=2019-01-01&include=bounce_rate")
|
||||
|
||||
assert json_response(conn, 200) == %{
|
||||
"total_visitors" => 3,
|
||||
"referrers" => [
|
||||
%{"name" => "10words.io/hello", "count" => 2, "bounce_rate" => 50},
|
||||
%{"name" => "10words.io/", "count" => 1, "bounce_rate" => nil},
|
||||
]
|
||||
}
|
||||
end
|
||||
|
||||
test "gets keywords from Google", %{conn: conn, user: user, site: site} do
|
||||
insert(:google_auth, user: user, user: user,site: site, property: "sc-domain:example.com")
|
||||
insert(:pageview, hostname: site.domain, referrer: "google.com", referrer_source: "Google", timestamp: ~N[2019-01-01 01:00:00])
|
||||
|
@ -28,6 +28,7 @@ defmodule Plausible.Factory do
|
||||
%Plausible.Session{
|
||||
hostname: hostname,
|
||||
new_visitor: true,
|
||||
entry_page: "/",
|
||||
user_id: UUID.uuid4(),
|
||||
start: Timex.now(),
|
||||
is_bounce: false
|
||||
|
Loading…
Reference in New Issue
Block a user