Skip to content

Commit

Permalink
Add geographic query
Browse files Browse the repository at this point in the history
  • Loading branch information
robbie-c committed Oct 30, 2023
1 parent ba570e1 commit 73f3bdf
Show file tree
Hide file tree
Showing 7 changed files with 231 additions and 43 deletions.
5 changes: 4 additions & 1 deletion frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -3176,7 +3176,10 @@
"InitialUTMCampaign",
"Browser",
"OS",
"DeviceType"
"DeviceType",
"Country",
"Region",
"City"
],
"type": "string"
},
Expand Down
3 changes: 3 additions & 0 deletions frontend/src/queries/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,9 @@ export enum WebStatsBreakdown {
Browser = 'Browser',
OS = 'OS',
DeviceType = 'DeviceType',
Country = 'Country',
Region = 'Region',
City = 'City',
}
export interface WebStatsTableQuery extends WebAnalyticsQueryBase {
kind: NodeKind.WebStatsTableQuery
Expand Down
82 changes: 73 additions & 9 deletions frontend/src/scenes/web-analytics/WebAnalyticsDataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useActions } from 'kea'
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
import { useCallback, useMemo } from 'react'
import { Query } from '~/queries/Query/Query'
import { countryCodeToFlag, countryCodeToName } from 'scenes/insights/views/WorldMap'

const PercentageCell: QueryContextColumnComponent = ({ value }) => {
if (typeof value === 'number') {
Expand Down Expand Up @@ -42,6 +43,12 @@ const BreakdownValueTitle: QueryContextColumnTitleComponent = (props) => {
return <>OS</>
case WebStatsBreakdown.DeviceType:
return <>Device Type</>
case WebStatsBreakdown.Country:
return <>Country</>
case WebStatsBreakdown.Region:
return <>Region</>
case WebStatsBreakdown.City:
return <>City</>
default:
throw new UnexpectedNeverError(breakdownBy)
}
Expand All @@ -53,11 +60,47 @@ const BreakdownValueCell: QueryContextColumnComponent = (props) => {
if (source.kind !== NodeKind.WebStatsTableQuery) {
return null
}
if (typeof value !== 'string') {
return null
const { breakdownBy } = source

switch (breakdownBy) {
case WebStatsBreakdown.Country:
if (typeof value === 'string') {
const countryCode = value
return (
<>
{countryCodeToFlag(countryCode)} {countryCodeToName[countryCode] || countryCode}
</>
)
}
break
case WebStatsBreakdown.Region:
if (Array.isArray(value)) {
const [countryCode, regionCode, regionName] = value
return (
<>
{countryCodeToFlag(countryCode)} {countryCodeToName[countryCode] || countryCode} -{' '}
{regionName || regionCode}
</>
)
}
break
case WebStatsBreakdown.City:
if (Array.isArray(value)) {
const [countryCode, cityName] = value
return (
<>
{countryCodeToFlag(countryCode)} {countryCodeToName[countryCode] || countryCode} - {cityName}
</>
)
}
break
}

return <BreakdownValueCellInner value={value} />
if (typeof value === 'string') {
return <>{value}</>
} else {
return null
}
}

export const webStatsBreakdownToPropertyName = (breakdownBy: WebStatsBreakdown): string => {
Expand All @@ -78,15 +121,17 @@ export const webStatsBreakdownToPropertyName = (breakdownBy: WebStatsBreakdown):
return '$os'
case WebStatsBreakdown.DeviceType:
return '$device_type'
case WebStatsBreakdown.Country:
return '$geoip_country_code'
case WebStatsBreakdown.Region:
return '$geoip_subdivision_1_code'
case WebStatsBreakdown.City:
return '$geoip_city_name'
default:
throw new UnexpectedNeverError(breakdownBy)
}
}

const BreakdownValueCellInner = ({ value }: { value: string }): JSX.Element => {
return <span>{value}</span>
}

export const webAnalyticsDataTableQueryContext: QueryContext = {
columns: {
breakdown_value: {
Expand Down Expand Up @@ -130,7 +175,7 @@ export const WebStatsTableTile = ({

const context = useMemo((): QueryContext => {
const rowProps: QueryContext['rowProps'] = (record: unknown) => {
const breakdownValue = getBreakdownValue(record)
const breakdownValue = getBreakdownValue(record, breakdownBy)
if (breakdownValue === undefined) {
return {}
}
Expand All @@ -147,7 +192,7 @@ export const WebStatsTableTile = ({
return <Query query={query} readOnly={true} context={context} />
}

const getBreakdownValue = (record: unknown): string | undefined => {
const getBreakdownValue = (record: unknown, breakdownBy: WebStatsBreakdown): string | undefined => {
if (typeof record !== 'object' || !record || !('result' in record)) {
return undefined
}
Expand All @@ -157,6 +202,25 @@ const getBreakdownValue = (record: unknown): string | undefined => {
}
// assume that the first element is the value
const breakdownValue = result[0]

switch (breakdownBy) {
case WebStatsBreakdown.Country:
if (Array.isArray(breakdownValue)) {
return breakdownValue[0]
}
break
case WebStatsBreakdown.Region:
if (Array.isArray(breakdownValue)) {
return breakdownValue[1]
}
break
case WebStatsBreakdown.City:
if (Array.isArray(breakdownValue)) {
return breakdownValue[1]
}
break
}

if (typeof breakdownValue !== 'string') {
return undefined
}
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/scenes/web-analytics/WebDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ import { Query } from '~/queries/Query/Query'
import { useActions, useValues } from 'kea'
import { TabsTile, webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
import { PropertyFilters } from 'lib/components/PropertyFilters/PropertyFilters'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'
import { isEventPropertyFilter } from 'lib/components/PropertyFilters/utils'
import { NodeKind, QuerySchema } from '~/queries/schema'
import { DateFilter } from 'lib/components/DateFilter/DateFilter'
import { WebAnalyticsNotice } from 'scenes/web-analytics/WebAnalyticsNotice'
import { webAnalyticsDataTableQueryContext, WebStatsTableTile } from 'scenes/web-analytics/WebAnalyticsDataTable'
import { WebTabs } from 'scenes/web-analytics/WebTabs'
import { TaxonomicFilterGroupType } from 'lib/components/TaxonomicFilter/types'

const Filters = (): JSX.Element => {
const { webAnalyticsFilters, dateTo, dateFrom } = useValues(webAnalyticsLogic)
Expand All @@ -22,6 +22,7 @@ const Filters = (): JSX.Element => {
onChange={(filters) => setWebAnalyticsFilters(filters.filter(isEventPropertyFilter))}
propertyFilters={webAnalyticsFilters}
pageKey={'web-analytics'}
eventNames={['$pageview', '$pageleave', '$autocapture']}
/>
</div>
<div className={'bg-border h-px w-full mt-2'} />
Expand Down
146 changes: 114 additions & 32 deletions frontend/src/scenes/web-analytics/webAnalyticsLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,14 @@ import { actions, connect, kea, listeners, path, reducers, selectors, sharedList

import type { webAnalyticsLogicType } from './webAnalyticsLogicType'
import { NodeKind, QuerySchema, WebAnalyticsPropertyFilters, WebStatsBreakdown } from '~/queries/schema'
import { EventPropertyFilter, HogQLPropertyFilter, PropertyFilterType, PropertyOperator } from '~/types'
import {
BaseMathType,
ChartDisplayType,
EventPropertyFilter,
HogQLPropertyFilter,
PropertyFilterType,
PropertyOperator,
} from '~/types'
import { isNotNil } from 'lib/utils'

export interface WebTileLayout {
Expand Down Expand Up @@ -49,6 +56,13 @@ export enum PathTab {
INITIAL_PATH = 'INITIAL_PATH',
}

export enum GeographyTab {
MAP = 'MAP',
COUNTRIES = 'COUNTRIES',
REGIONS = 'REGIONS',
CITIES = 'CITIES',
}

export const initialWebAnalyticsFilter = [] as WebAnalyticsPropertyFilters

const setOncePropertyNames = ['$initial_pathname', '$initial_referrer', '$initial_utm_source', '$initial_utm_campaign']
Expand All @@ -71,6 +85,7 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
setPathTab: (tab: string) => ({
tab,
}),
setGeographyTab: (tab: string) => ({ tab }),
setDates: (dateFrom: string | null, dateTo: string | null) => ({ dateFrom, dateTo }),
}),
reducers({
Expand Down Expand Up @@ -177,6 +192,12 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
setPathTab: (_, { tab }) => tab,
},
],
geographyTab: [
GeographyTab.COUNTRIES as string,
{
setGeographyTab: (_, { tab }) => tab,
},
],
dateFrom: [
'-7d' as string | null,
{
Expand All @@ -192,8 +213,16 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
}),
selectors(({ actions }) => ({
tiles: [
(s) => [s.webAnalyticsFilters, s.pathTab, s.deviceTab, s.sourceTab, s.dateFrom, s.dateTo],
(webAnalyticsFilters, pathTab, deviceTab, sourceTab, dateFrom, dateTo): WebDashboardTile[] => {
(s) => [s.webAnalyticsFilters, s.pathTab, s.deviceTab, s.sourceTab, s.geographyTab, s.dateFrom, s.dateTo],
(
webAnalyticsFilters,
pathTab,
deviceTab,
sourceTab,
geographyTab,
dateFrom,
dateTo
): WebDashboardTile[] => {
const dateRange = {
date_from: dateFrom,
date_to: dateTo,
Expand Down Expand Up @@ -384,35 +413,88 @@ export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
// },
// },
// },
// {
// title: 'World Map (Unique Users)',
// layout: {
// colSpan: 6,
// },
// query: {
// kind: NodeKind.InsightVizNode,
// source: {
// kind: NodeKind.TrendsQuery,
// breakdown: {
// breakdown: '$geoip_country_code',
// breakdown_type: 'person',
// },
// dateRange,
// series: [
// {
// event: '$pageview',
// kind: NodeKind.EventsNode,
// math: BaseMathType.UniqueUsers,
// },
// ],
// trendsFilter: {
// display: ChartDisplayType.WorldMap,
// },
// filterTestAccounts: true,
// properties: webAnalyticsFilters,
// },
// },
// },
{
layout: {
colSpan: 6,
},
activeTabId: geographyTab,
setTabId: actions.setGeographyTab,
tabs: [
{
id: GeographyTab.MAP,
title: 'World Map',
linkText: 'Map',
query: {
kind: NodeKind.InsightVizNode,
source: {
kind: NodeKind.TrendsQuery,
breakdown: {
breakdown: '$geoip_country_code',
breakdown_type: 'person',
},
dateRange,
series: [
{
event: '$pageview',
kind: NodeKind.EventsNode,
math: BaseMathType.UniqueUsers,
},
],
trendsFilter: {
display: ChartDisplayType.WorldMap,
},
filterTestAccounts: true,
properties: webAnalyticsFilters,
},
},
},
{
id: GeographyTab.COUNTRIES,
title: 'Top Countries',
linkText: 'Countries',
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebStatsTableQuery,
properties: webAnalyticsFilters,
breakdownBy: WebStatsBreakdown.Country,
dateRange,
},
},
},
{
id: GeographyTab.REGIONS,
title: 'Top Regions',
linkText: 'Regions',
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebStatsTableQuery,
properties: webAnalyticsFilters,
breakdownBy: WebStatsBreakdown.Region,
dateRange,
},
},
},
{
id: GeographyTab.CITIES,
title: 'Top Cities',
linkText: 'Cities',
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebStatsTableQuery,
properties: webAnalyticsFilters,
breakdownBy: WebStatsBreakdown.City,
dateRange,
},
},
},
],
},
]
},
],
Expand Down
Loading

0 comments on commit 73f3bdf

Please sign in to comment.