Skip to content

Commit

Permalink
Merge branch 'master' into fix/validate-tokens-check
Browse files Browse the repository at this point in the history
  • Loading branch information
benjackwhite authored Dec 13, 2024
2 parents c9a941e + 5dd300a commit 3898b26
Show file tree
Hide file tree
Showing 19 changed files with 1,543 additions and 1,253 deletions.

Large diffs are not rendered by default.

135 changes: 113 additions & 22 deletions frontend/src/scenes/dashboard/newDashboardLogic.test.tsx
Original file line number Diff line number Diff line change
@@ -1,41 +1,132 @@
import { NodeKind } from '~/queries/schema'

import { applyTemplate } from './newDashboardLogic'

describe('template function in newDashboardLogic', () => {
it('ignores unused variables', () => {
expect(
applyTemplate({ a: 'hello', b: 'hi' }, [
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
applyTemplate(
{ a: 'hello', b: 'hi' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
])
],
null
)
).toEqual({ a: 'hello', b: 'hi' })
})
it('uses identified variables', () => {
expect(
applyTemplate({ a: '{VARIABLE_1}', b: 'hi' }, [
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
applyTemplate(
{ a: '{VARIABLE_1}', b: 'hi' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
event: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
])
],
null
)
).toEqual({
a: {
event: '$pageview',
},
b: 'hi',
})
})

it('replaces variables in query based tiles', () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.TrendsQuery
)
).toEqual({
a: {
event: '$pageview',
kind: 'EventsNode',
math: 'total',
},
})
})

it("removes the math property from query based tiles that don't support it", () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.LifecycleQuery
)
).toEqual({
a: {
event: '$pageview',
kind: 'EventsNode',
},
})
})

it('removes the math property from retention insight tiles', () => {
expect(
applyTemplate(
{ a: '{VARIABLE_1}' },
[
{
id: 'VARIABLE_1',
name: 'a',
default: {
id: '$pageview',
math: 'dau' as any,
type: 'events' as any,
},
description: 'The description of the variable',
required: true,
type: 'event',
},
],
NodeKind.RetentionQuery
)
).toEqual({
a: {
id: '$pageview',
type: 'events',
},
})
})
})
43 changes: 39 additions & 4 deletions frontend/src/scenes/dashboard/newDashboardLogic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,15 @@ import api from 'lib/api'
import { DashboardRestrictionLevel } from 'lib/constants'
import { lemonToast } from 'lib/lemon-ui/LemonToast/LemonToast'
import { featureFlagLogic } from 'lib/logic/featureFlagLogic'
import { MathAvailability } from 'scenes/insights/filters/ActionFilter/ActionFilterRow/ActionFilterRow'
import { teamLogic } from 'scenes/teamLogic'
import { urls } from 'scenes/urls'

import { dashboardsModel } from '~/models/dashboardsModel'
import { legacyEntityToNode, sanitizeRetentionEntity } from '~/queries/nodes/InsightQuery/utils/filtersToQueryNode'
import { getQueryBasedDashboard } from '~/queries/nodes/InsightViz/utils'
import { NodeKind } from '~/queries/schema'
import { isInsightVizNode } from '~/queries/utils'
import { DashboardTemplateType, DashboardTemplateVariableType, DashboardTile, DashboardType, JsonType } from '~/types'

import type { newDashboardLogicType } from './newDashboardLogicType'
Expand All @@ -35,32 +39,63 @@ export interface NewDashboardLogicProps {
}

// Currently this is a very generic recursive function incase we want to add template variables to aspects beyond events
export function applyTemplate(obj: DashboardTile | JsonType, variables: DashboardTemplateVariableType[]): JsonType {
export function applyTemplate(
obj: DashboardTile | JsonType,
variables: DashboardTemplateVariableType[],
queryKind: NodeKind | null
): JsonType {
if (typeof obj === 'string') {
if (obj.startsWith('{') && obj.endsWith('}')) {
const variableId = obj.substring(1, obj.length - 1)
const variable = variables.find((variable) => variable.id === variableId)
if (variable && variable.default) {
// added for future compatibility - at the moment we only have event variables
const isEventVariable = variable.type === 'event'

if (queryKind && isEventVariable) {
let mathAvailability = MathAvailability.None
if (queryKind === NodeKind.TrendsQuery) {
mathAvailability = MathAvailability.All
} else if (queryKind === NodeKind.StickinessQuery) {
mathAvailability = MathAvailability.ActorsOnly
} else if (queryKind === NodeKind.FunnelsQuery) {
mathAvailability = MathAvailability.FunnelsOnly
}
return (
queryKind === NodeKind.RetentionQuery
? sanitizeRetentionEntity(variable.default as any)
: legacyEntityToNode(variable.default as any, true, mathAvailability)
) as JsonType
}

return variable.default as JsonType
}
return obj
}
}
if (Array.isArray(obj)) {
return obj.map((item) => applyTemplate(item, variables))
return obj.map((item) => applyTemplate(item, variables, queryKind))
}
if (typeof obj === 'object' && obj !== null) {
const newObject: JsonType = {}
for (const [key, value] of Object.entries(obj)) {
newObject[key] = applyTemplate(value, variables)
newObject[key] = applyTemplate(value, variables, queryKind)
}
return newObject
}
return obj
}

function makeTilesUsingVariables(tiles: DashboardTile[], variables: DashboardTemplateVariableType[]): JsonType[] {
return tiles.map((tile: DashboardTile) => applyTemplate(tile, variables))
return tiles.map((tile: DashboardTile) => {
const isQueryBased = 'query' in tile && tile.query != null
const queryKind: NodeKind | null = isQueryBased
? isInsightVizNode(tile.query as any)
? (tile.query as any)?.source.kind
: (tile.query as any)?.kind
: null
return applyTemplate(tile, variables, queryKind)
})
}

export const newDashboardLogic = kea<newDashboardLogicType>([
Expand Down
25 changes: 16 additions & 9 deletions frontend/src/scenes/experiments/ExperimentView/Goal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,11 @@ export function Goal(): JSX.Element {
const [isModalOpen, setIsModalOpen] = useState(false)
const metricType = getMetricType(0)

// :FLAG: CLEAN UP AFTER MIGRATION
const isDataWarehouseMetric =
featureFlags[FEATURE_FLAGS.EXPERIMENTS_HOGQL] &&
(experiment.metrics[0] as ExperimentTrendsQuery).count_query.series[0].kind === NodeKind.DataWarehouseNode

return (
<div>
<div>
Expand Down Expand Up @@ -322,16 +327,18 @@ export function Goal(): JSX.Element {
Change goal
</LemonButton>
</div>
{metricType === InsightType.TRENDS && !experimentMathAggregationForTrends() && (
<>
<LemonDivider className="" vertical />
<div className="">
<div className="mt-auto ml-auto">
<ExposureMetric experimentId={experimentId} />
{metricType === InsightType.TRENDS &&
!experimentMathAggregationForTrends() &&
!isDataWarehouseMetric && (
<>
<LemonDivider className="" vertical />
<div className="">
<div className="mt-auto ml-auto">
<ExposureMetric experimentId={experimentId} />
</div>
</div>
</div>
</>
)}
</>
)}
</div>
)}
<PrimaryMetricModal
Expand Down
70 changes: 32 additions & 38 deletions frontend/src/scenes/experiments/ExperimentView/components.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -493,44 +493,38 @@ export function PageHeaderCustom(): JSX.Element {
)}
{experiment && isExperimentRunning && (
<div className="flex flex-row gap-2">
{!isExperimentStopped && !experiment.archived && (
<>
<More
overlay={
<>
<LemonButton
onClick={() =>
exposureCohortId ? undefined : createExposureCohort()
}
fullWidth
data-attr={`${
exposureCohortId ? 'view' : 'create'
}-exposure-cohort`}
to={exposureCohortId ? urls.cohort(exposureCohortId) : undefined}
targetBlank={!!exposureCohortId}
>
{exposureCohortId ? 'View' : 'Create'} exposure cohort
</LemonButton>
<LemonButton
onClick={() => loadExperimentResults(true)}
fullWidth
data-attr="refresh-experiment"
>
Refresh experiment results
</LemonButton>
<LemonButton
onClick={() => loadSecondaryMetricResults(true)}
fullWidth
data-attr="refresh-secondary-metrics"
>
Refresh secondary metrics
</LemonButton>
</>
}
/>
<LemonDivider vertical />
</>
)}
<>
<More
overlay={
<>
<LemonButton
onClick={() => (exposureCohortId ? undefined : createExposureCohort())}
fullWidth
data-attr={`${exposureCohortId ? 'view' : 'create'}-exposure-cohort`}
to={exposureCohortId ? urls.cohort(exposureCohortId) : undefined}
targetBlank={!!exposureCohortId}
>
{exposureCohortId ? 'View' : 'Create'} exposure cohort
</LemonButton>
<LemonButton
onClick={() => loadExperimentResults(true)}
fullWidth
data-attr="refresh-experiment"
>
Refresh experiment results
</LemonButton>
<LemonButton
onClick={() => loadSecondaryMetricResults(true)}
fullWidth
data-attr="refresh-secondary-metrics"
>
Refresh secondary metrics
</LemonButton>
</>
}
/>
<LemonDivider vertical />
</>
<ResetButton experimentId={experiment.id} />
{!experiment.end_date && (
<LemonButton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export function WebExperimentTransformHeader({

return (
<div className="flex w-full gap-4 items-center">
<div className="flex-1">
<div className="flex-1 overflow-hidden">
<h2>{transform.selector || 'Select element'}</h2>
</div>

Expand Down
3 changes: 0 additions & 3 deletions mypy-baseline.txt
Original file line number Diff line number Diff line change
Expand Up @@ -143,9 +143,6 @@ posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Item "No
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Argument 1 to "float" has incompatible type "Any | None"; expected "str | Buffer | SupportsFloat | SupportsIndex" [arg-type]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Argument 1 to "clean_display" has incompatible type "Any | None"; expected "str" [arg-type]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "FunnelsFilter"; expected "str": "TrendsFilter" [dict-item]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "RetentionFilter"; expected "str": "TrendsFilter" [dict-item]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Argument 1 to "to_base_entity_dict" has incompatible type "Any | None"; expected "dict[Any, Any]" [arg-type]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Argument 1 to "to_base_entity_dict" has incompatible type "Any | None"; expected "dict[Any, Any]" [arg-type]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "PathsFilter"; expected "str": "TrendsFilter" [dict-item]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "LifecycleFilter"; expected "str": "TrendsFilter" [dict-item]
posthog/hogql_queries/legacy_compatibility/filter_to_query.py:0: error: Dict entry 0 has incompatible type "str": "StickinessFilter"; expected "str": "TrendsFilter" [dict-item]
Expand Down
2 changes: 1 addition & 1 deletion posthog/api/survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,7 @@ def validate(self, data):
if response_sampling_start_date < today_utc:
raise serializers.ValidationError(
{
"response_sampling_start_date": "Response sampling start date must be today or a future date in UTC."
"response_sampling_start_date": f"Response sampling start date must be today or a future date in UTC. Got {response_sampling_start_date} when current time is {today_utc}"
}
)

Expand Down
2 changes: 2 additions & 0 deletions posthog/api/test/test_survey.py
Original file line number Diff line number Diff line change
Expand Up @@ -2378,6 +2378,7 @@ def test_can_clear_associated_actions(self):
assert len(survey.actions.all()) == 0


@freeze_time("2024-12-12 00:00:00")
class TestSurveyResponseSampling(APIBaseTest):
def _create_survey_with_sampling_limits(
self,
Expand Down Expand Up @@ -2407,6 +2408,7 @@ def _create_survey_with_sampling_limits(
)

response_data = response.json()
assert response.status_code == status.HTTP_201_CREATED, response_data
survey = Survey.objects.get(id=response_data["id"])
return survey

Expand Down
Loading

0 comments on commit 3898b26

Please sign in to comment.