Skip to content

Commit

Permalink
feat(web-analytics): Add overview stats (#17722)
Browse files Browse the repository at this point in the history
* Move web analytics CTEs to their own file

* Add overview stats query

* Add very basic structure of web dashboard

* Add missing entry in SavedInsights

* Move session_cte into from

* Replace dashboard css with "tailwind"

* Make top pages query not a f-string
  • Loading branch information
robbie-c authored Oct 3, 2023
1 parent b6bb8f2 commit 3b4aaf0
Show file tree
Hide file tree
Showing 17 changed files with 482 additions and 236 deletions.
8 changes: 7 additions & 1 deletion frontend/src/queries/nodes/DataTable/queryFeatures.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import {
isEventsQuery,
isHogQLQuery,
isPersonsNode,
isWebOverviewStatsQuery,
isWebTopClicksQuery,
isWebTopPagesQuery,
isWebTopSourcesQuery,
Expand Down Expand Up @@ -47,7 +48,12 @@ export function getQueryFeatures(query: Node): Set<QueryFeature> {
features.add(QueryFeature.personsSearch)
}

if (isWebTopSourcesQuery(query) || isWebTopPagesQuery(query) || isWebTopClicksQuery(query)) {
if (
isWebOverviewStatsQuery(query) ||
isWebTopSourcesQuery(query) ||
isWebTopPagesQuery(query) ||
isWebTopClicksQuery(query)
) {
features.add(QueryFeature.columnsInResponse)
features.add(QueryFeature.resultIsArrayOfArrays)
}
Expand Down
60 changes: 60 additions & 0 deletions frontend/src/queries/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,9 @@
{
"$ref": "#/definitions/TimeToSeeDataSessionsQuery"
},
{
"$ref": "#/definitions/WebOverviewStatsQuery"
},
{
"$ref": "#/definitions/WebTopSourcesQuery"
},
Expand Down Expand Up @@ -399,6 +402,9 @@
{
"$ref": "#/definitions/TimeToSeeDataSessionsQuery"
},
{
"$ref": "#/definitions/WebOverviewStatsQuery"
},
{
"$ref": "#/definitions/WebTopSourcesQuery"
},
Expand Down Expand Up @@ -2293,6 +2299,60 @@
"type": "object"
},
"WebAnalyticsFilters": {},
"WebOverviewStatsQuery": {
"additionalProperties": false,
"properties": {
"dateRange": {
"$ref": "#/definitions/DateRange"
},
"filters": {
"$ref": "#/definitions/WebAnalyticsFilters"
},
"kind": {
"const": "WebOverviewStatsQuery",
"type": "string"
},
"response": {
"$ref": "#/definitions/WebOverviewStatsQueryResponse"
}
},
"required": ["kind", "filters"],
"type": "object"
},
"WebOverviewStatsQueryResponse": {
"additionalProperties": false,
"properties": {
"columns": {
"items": {},
"type": "array"
},
"is_cached": {
"type": "boolean"
},
"last_refresh": {
"type": "string"
},
"next_allowed_client_refresh": {
"type": "string"
},
"result": {
"items": {},
"type": "array"
},
"timings": {
"items": {
"$ref": "#/definitions/QueryTiming"
},
"type": "array"
},
"types": {
"items": {},
"type": "array"
}
},
"required": ["result"],
"type": "object"
},
"WebTopClicksQuery": {
"additionalProperties": false,
"properties": {
Expand Down
14 changes: 14 additions & 0 deletions frontend/src/queries/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export enum NodeKind {
LifecycleQuery = 'LifecycleQuery',

// Web analytics queries
WebOverviewStatsQuery = 'WebOverviewStatsQuery',
WebTopSourcesQuery = 'WebTopSourcesQuery',
WebTopPagesQuery = 'WebTopPagesQuery',
WebTopClicksQuery = 'WebTopClicksQuery',
Expand All @@ -80,6 +81,7 @@ export type AnyDataNode =
| HogQLQuery
| HogQLMetadata
| TimeToSeeDataSessionsQuery
| WebOverviewStatsQuery
| WebTopSourcesQuery
| WebTopClicksQuery
| WebTopPagesQuery
Expand Down Expand Up @@ -293,6 +295,7 @@ export interface DataTableNode extends Node, DataTableNodeViewProps {
| PersonsNode
| HogQLQuery
| TimeToSeeDataSessionsQuery
| WebOverviewStatsQuery
| WebTopSourcesQuery
| WebTopClicksQuery
| WebTopPagesQuery
Expand Down Expand Up @@ -507,6 +510,17 @@ export interface WebAnalyticsQueryBase {
dateRange?: DateRange
}

export interface WebOverviewStatsQuery extends WebAnalyticsQueryBase {
kind: NodeKind.WebOverviewStatsQuery
filters: WebAnalyticsFilters
response?: WebOverviewStatsQueryResponse
}

export interface WebOverviewStatsQueryResponse extends QueryResponse {
result: unknown[]
types?: unknown[]
columns?: unknown[]
}
export interface WebTopSourcesQuery extends WebAnalyticsQueryBase {
kind: NodeKind.WebTopSourcesQuery
filters: WebAnalyticsFilters
Expand Down
5 changes: 4 additions & 1 deletion frontend/src/queries/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import {
WebTopSourcesQuery,
WebTopClicksQuery,
WebTopPagesQuery,
WebOverviewStatsQuery,
} from '~/queries/schema'
import { TaxonomicFilterGroupType, TaxonomicFilterValue } from 'lib/components/TaxonomicFilter/types'
import { dayjs } from 'lib/dayjs'
Expand Down Expand Up @@ -92,7 +93,9 @@ export function isInsightVizNode(node?: Node | null): node is InsightVizNode {
export function isHogQLQuery(node?: Node | null): node is HogQLQuery {
return node?.kind === NodeKind.HogQLQuery
}

export function isWebOverviewStatsQuery(node?: Node | null): node is WebOverviewStatsQuery {
return node?.kind === NodeKind.WebOverviewStatsQuery
}
export function isWebTopSourcesQuery(node?: Node | null): node is WebTopSourcesQuery {
return node?.kind === NodeKind.WebTopSourcesQuery
}
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/scenes/saved-insights/SavedInsights.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,12 @@ export const QUERY_TYPES_METADATA: Record<NodeKind, InsightTypeMetadata> = {
icon: InsightSQLIcon,
inMenu: true,
},
[NodeKind.WebOverviewStatsQuery]: {
name: 'Overview Stats',
description: 'View overview stats for a website',
icon: InsightsTrendsIcon,
inMenu: true,
},
[NodeKind.WebTopSourcesQuery]: {
name: 'Top Sources',
description: 'View top sources for a website',
Expand Down
44 changes: 2 additions & 42 deletions frontend/src/scenes/web-analytics/WebAnalyticsScene.tsx
Original file line number Diff line number Diff line change
@@ -1,49 +1,9 @@
import { SceneExport } from 'scenes/sceneTypes'
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'
import { Query } from '~/queries/Query/Query'
import { NodeKind } from '~/queries/schema'
import { WebAnalyticsDashboard } from 'scenes/web-analytics/WebDashboard'

export function WebAnalyticsScene(): JSX.Element {
return (
<div>
Top sources
<Query
query={{
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebTopSourcesQuery,
filters: {},
},
}}
readOnly={true}
/>
Top clicks
<Query
query={{
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebTopClicksQuery,
filters: {},
},
}}
readOnly={true}
/>
Top pages
<Query
query={{
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebTopPagesQuery,
filters: { x: 1 },
},
}}
readOnly={true}
/>
</div>
)
return <WebAnalyticsDashboard />
}

export const scene: SceneExport = {
Expand Down
21 changes: 21 additions & 0 deletions frontend/src/scenes/web-analytics/WebDashboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Query } from '~/queries/Query/Query'
import { useValues } from 'kea'
import { webAnalyticsLogic } from 'scenes/web-analytics/webAnalyticsLogic'

export const WebAnalyticsDashboard = (): JSX.Element => {
const { tiles } = useValues(webAnalyticsLogic)
return (
<div className="grid grid-cols-12 gap-4">
{tiles.map(({ query, layout }, i) => (
<div
key={i}
className={`col-span-${layout.colSpan ?? 6} row-span-${
layout.rowSpan ?? 1
} min-h-100 flex flex-col`}
>
<Query query={query} readOnly={true} />
</div>
))}
</div>
)
}
56 changes: 55 additions & 1 deletion frontend/src/scenes/web-analytics/webAnalyticsLogic.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,67 @@
import { actions, connect, kea, listeners, path, reducers, selectors, sharedListeners } from 'kea'

import type { webAnalyticsLogicType } from './webAnalyticsLogicType'
import { NodeKind, QuerySchema } from '~/queries/schema'

interface Layout {
colSpan?: number
rowSpan?: number
}
export interface WebDashboardTile {
query: QuerySchema
layout: Layout
}
export const webAnalyticsLogic = kea<webAnalyticsLogicType>([
path(['scenes', 'webAnalytics', 'webAnalyticsSceneLogic']),
connect({}),
actions({}),
reducers({}),
selectors(() => ({})),
selectors({
tiles: [
() => [],
(): WebDashboardTile[] => [
{
layout: {
colSpan: 12,
},
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebOverviewStatsQuery,
filters: {},
},
},
},
{
layout: {
colSpan: 6,
},
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebTopPagesQuery,
filters: {},
},
},
},
{
layout: {
colSpan: 6,
},
query: {
full: true,
kind: NodeKind.DataTableNode,
source: {
kind: NodeKind.WebTopSourcesQuery,
filters: {},
},
},
},
],
],
}),
sharedListeners(() => ({})),
listeners(() => ({})),
])
Loading

0 comments on commit 3b4aaf0

Please sign in to comment.