Skip to content

Commit

Permalink
Add conversion_rate to sources api and source table
Browse files Browse the repository at this point in the history
  • Loading branch information
ro-savage committed Sep 4, 2021
1 parent 01412d0 commit 668afa0
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 8 deletions.
4 changes: 4 additions & 0 deletions assets/js/dashboard/number-formatter.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,7 @@ export function durationFormatter(duration) {
return `${seconds}s`
}
}

export function percentageFormatter(num) {
return +(Math.round(num + "e+2") + "e-2");
}
41 changes: 33 additions & 8 deletions assets/js/dashboard/stats/sources/source-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import * as storage from '../../storage'
import FadeIn from '../../fade-in'
import Bar from '../bar'
import MoreLink from '../more-link'
import numberFormatter from '../../number-formatter'
import numberFormatter, {percentageFormatter} from '../../number-formatter'
import * as api from '../../api'
import LazyLoader from '../../lazy-loader'

Expand All @@ -33,15 +33,22 @@ class AllSources extends React.Component {
return this.props.query.period === 'realtime'
}

hasGoalFilter() {
return !!this.props.query.filters.goal
}

fetchReferrers() {
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/sources`, this.props.query, {show_noref: this.showNoRef()})
.then((res) => this.setState({loading: false, referrers: res}))
.then((res) => this.setState({loading: false, referrers: res}))
}

renderReferrer(referrer) {
const query = new URLSearchParams(window.location.search)
query.set('source', referrer.name)

const showCR = this.hasGoalFilter()
const maxWidthDeduction = showCR ? "10rem" : "5rem"

return (
<div
className="flex items-center justify-between my-1 text-sm"
Expand All @@ -51,7 +58,7 @@ class AllSources extends React.Component {
count={referrer.count}
all={this.state.referrers}
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
maxWidthDeduction="4rem"
maxWidthDeduction={maxWidthDeduction}
>
<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
<Link
Expand All @@ -66,7 +73,8 @@ class AllSources extends React.Component {
</Link>
</span>
</Bar>
<span className="font-medium dark:text-gray-200">{numberFormatter(referrer.count)}</span>
<span className="font-medium dark:text-gray-200 w-20 text-right">{numberFormatter(referrer.count)}</span>
{showCR && <span className="font-medium dark:text-gray-200 w-20 text-right">{percentageFormatter(referrer.conversion_rate)}%</span>}
</div>
)
}
Expand All @@ -76,12 +84,17 @@ class AllSources extends React.Component {
}

renderList() {
const showCR = this.hasGoalFilter()

if (this.state.referrers && this.state.referrers.length > 0) {
return (
<React.Fragment>
<div className="flex items-center justify-between mt-3 mb-2 text-xs font-bold tracking-wide text-gray-500">
<span>Source</span>
<span>{this.label()}</span>
<div className="text-right">
<span className="inline-block w-20">{this.label()}</span>
{showCR && <span className="inline-block w-20">CR</span>}
</div>
</div>

<FlipMove className="flex-grow">
Expand Down Expand Up @@ -149,6 +162,10 @@ class UTMSources extends React.Component {
return this.props.query.period === 'realtime'
}

hasGoalFilter() {
return !!this.props.query.filters.goal
}

fetchReferrers() {
const endpoint = UTM_TAGS[this.props.tab].endpoint
api.get(`/api/stats/${encodeURIComponent(this.props.site.domain)}/${endpoint}`, this.props.query, {show_noref: this.showNoRef()})
Expand All @@ -158,6 +175,8 @@ class UTMSources extends React.Component {
renderReferrer(referrer) {
const query = new URLSearchParams(window.location.search)
query.set(this.props.tab, referrer.name)
const showCR = this.hasGoalFilter()
const maxWidthDeduction = showCR ? "10rem" : "5rem"

return (
<div
Expand All @@ -168,7 +187,7 @@ class UTMSources extends React.Component {
count={referrer.count}
all={this.state.referrers}
bg="bg-blue-50 dark:bg-gray-500 dark:bg-opacity-15"
maxWidthDeduction="4rem"
maxWidthDeduction={maxWidthDeduction}
>

<span className="flex px-2 py-1.5 dark:text-gray-300 relative z-9 break-all">
Expand All @@ -180,7 +199,8 @@ class UTMSources extends React.Component {
</Link>
</span>
</Bar>
<span className="font-medium dark:text-gray-200">{numberFormatter(referrer.count)}</span>
<span className="font-medium dark:text-gray-200 w-20 text-right">{numberFormatter(referrer.count)}</span>
{showCR && <span className="font-medium dark:text-gray-200 w-20 text-right">{percentageFormatter(referrer.conversion_rate)}%</span>}
</div>
)
}
Expand All @@ -190,12 +210,17 @@ class UTMSources extends React.Component {
}

renderList() {
const showCR = this.hasGoalFilter()

if (this.state.referrers && this.state.referrers.length > 0) {
return (
<div className="flex flex-col flex-grow">
<div className="flex items-center justify-between mt-3 mb-2 text-xs font-bold tracking-wide text-gray-500 dark:text-gray-400">
<span>{UTM_TAGS[this.props.tab].label}</span>
<span>{this.label()}</span>
<div className="text-right">
<span className="inline-block w-20">{this.label()}</span>
{showCR && <span className="inline-block w-20">CR</span>}
</div>
</div>

<FlipMove className="flex-grow">
Expand Down
32 changes: 32 additions & 0 deletions lib/plausible_web/controllers/api/stats_controller.ex
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ defmodule PlausibleWeb.Api.StatsController do

res =
Stats.breakdown(site, query, "visit:source", metrics, pagination)
|> maybe_add_cr(site, query, pagination, "source", "visit:source")
|> transform_keys(%{"source" => "name", "visitors" => "count"})

json(conn, res)
Expand All @@ -231,6 +232,7 @@ defmodule PlausibleWeb.Api.StatsController do

res =
Stats.breakdown(site, query, "visit:utm_medium", metrics, pagination)
|> maybe_add_cr(site, query, pagination, "utm_medium", "visit:utm_medium")
|> transform_keys(%{"utm_medium" => "name", "visitors" => "count"})

json(conn, res)
Expand All @@ -249,6 +251,7 @@ defmodule PlausibleWeb.Api.StatsController do

res =
Stats.breakdown(site, query, "visit:utm_campaign", metrics, pagination)
|> maybe_add_cr(site, query, pagination, "utm_campaign", "visit:utm_campaign")
|> transform_keys(%{"utm_campaign" => "name", "visitors" => "count"})

json(conn, res)
Expand All @@ -267,6 +270,7 @@ defmodule PlausibleWeb.Api.StatsController do

res =
Stats.breakdown(site, query, "visit:utm_source", metrics, pagination)
|> maybe_add_cr(site, query, pagination, "utm_source", "visit:utm_source")
|> transform_keys(%{"utm_source" => "name", "visitors" => "count"})

json(conn, res)
Expand Down Expand Up @@ -580,4 +584,32 @@ defmodule PlausibleWeb.Api.StatsController do
query
end
end

defp add_cr(list, list_without_goals, key_name) do
Enum.map(list, fn item ->
without_goal = Enum.find(list_without_goals, fn s -> s[key_name] === item[key_name] end)

item
|> Map.put(:conversion_rate, calculate_cr(without_goal["visitors"], item["visitors"]))
end)
end

defp maybe_add_cr(list, site, query, pagination, key_name, filter_name) do
if Map.has_key?(query.filters, "event:goal") do
items = Enum.map(list, fn item -> item[key_name] end)

query_without_goal =
query
|> Query.put_filter(filter_name, {:member, items})
|> Query.remove_goal()

res_without_goal =
Stats.breakdown(site, query_without_goal, filter_name, ["visitors"], pagination)

list
|> add_cr(res_without_goal, key_name)
else
list
end
end
end

0 comments on commit 668afa0

Please sign in to comment.