From a370e6946cb081fb868a48f86b92e5e99b6e00a7 Mon Sep 17 00:00:00 2001 From: Eric Duong Date: Tue, 19 Mar 2024 22:11:32 -0400 Subject: [PATCH 01/17] feat(data-warehouse): data warehouse table breakdowns (#20980) * create virtual table definition with the shape of events * add conditionals * update schema * add distinct_id field * maths working * add prop math selection * add field * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * typing * clean up snapshot * cleanup * remove unnecessary * more cleanup * add breakdown * format * typing * frontend selection * returning * add condition * typing * add object storage test mixin * remove client fixture * restore main * Update UI snapshots for `chromium` (2) * add disabled state * Update UI snapshots for `chromium` (2) * Update query snapshots * Update query snapshots * Update query snapshots * Update UI snapshots for `chromium` (1) * Update query snapshots * Update UI snapshots for `chromium` (1) * Update query snapshots * Update query snapshots * Update query snapshots --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .../queries/nodes/InsightViz/Breakdown.tsx | 10 ++++- .../PropertyGroupFilters.tsx | 4 +- .../TaxonomicBreakdownButton.tsx | 8 ++-- .../TaxonomicBreakdownFilter.tsx | 6 +-- .../TaxonomicBreakdownPopover.tsx | 4 +- .../taxonomicBreakdownFilterLogic.ts | 10 ++++- .../scenes/insights/insightVizDataLogic.ts | 33 +++++++++++++++ .../insights/trends/trends_query_runner.py | 42 ++++++++++++++----- .../test_session_recordings.ambr | 24 ----------- 9 files changed, 94 insertions(+), 47 deletions(-) diff --git a/frontend/src/queries/nodes/InsightViz/Breakdown.tsx b/frontend/src/queries/nodes/InsightViz/Breakdown.tsx index ca6160110b925..c66ba5e5cd904 100644 --- a/frontend/src/queries/nodes/InsightViz/Breakdown.tsx +++ b/frontend/src/queries/nodes/InsightViz/Breakdown.tsx @@ -5,7 +5,9 @@ import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { EditorFilterProps } from '~/types' export function Breakdown({ insightProps }: EditorFilterProps): JSX.Element { - const { breakdownFilter, display, isTrends, isDataWarehouseSeries } = useValues(insightVizDataLogic(insightProps)) + const { breakdownFilter, display, isTrends, isSingleSeries, isDataWarehouseSeries } = useValues( + insightVizDataLogic(insightProps) + ) const { updateBreakdownFilter, updateDisplay } = useActions(insightVizDataLogic(insightProps)) return ( @@ -14,9 +16,13 @@ export function Breakdown({ insightProps }: EditorFilterProps): JSX.Element { breakdownFilter={breakdownFilter} display={display} isTrends={isTrends} - isDataWarehouseSeries={isDataWarehouseSeries} updateBreakdownFilter={updateBreakdownFilter} updateDisplay={updateDisplay} + disabledReason={ + !isSingleSeries && isDataWarehouseSeries + ? 'Breakdowns are not allowed for multiple series types' + : undefined + } /> ) } diff --git a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx index b924ba3232cc9..ffae45880111d 100644 --- a/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/PropertyGroupFilters/PropertyGroupFilters.tsx @@ -47,7 +47,9 @@ export function PropertyGroupFilters({ } = useActions(propertyGroupFilterLogic(logicProps)) const showHeader = propertyGroupFilter.type && propertyGroupFilter.values.length > 1 - const disabledReason = isDataWarehouseSeries ? 'Cannot add filter groups to data warehouse series' : undefined + const disabledReason = isDataWarehouseSeries + ? 'Cannot add filter groups to data warehouse series. Use individual series filters' + : undefined return (
{propertyGroupFilter.values && ( diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx index 8bb7e127e2e12..7b1ddbc90c3a1 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownButton.tsx @@ -8,10 +8,10 @@ import { taxonomicBreakdownFilterLogic } from './taxonomicBreakdownFilterLogic' import { TaxonomicBreakdownPopover } from './TaxonomicBreakdownPopover' interface TaxonomicBreakdownButtonProps { - isDataWarehouseSeries?: boolean + disabledReason?: string } -export function TaxonomicBreakdownButton({ isDataWarehouseSeries }: TaxonomicBreakdownButtonProps): JSX.Element { +export function TaxonomicBreakdownButton({ disabledReason }: TaxonomicBreakdownButtonProps): JSX.Element { const [open, setOpen] = useState(false) const { taxonomicBreakdownType } = useValues(taxonomicBreakdownFilterLogic) @@ -24,9 +24,7 @@ export function TaxonomicBreakdownButton({ isDataWarehouseSeries }: TaxonomicBre data-attr="add-breakdown-button" onClick={() => setOpen(!open)} sideIcon={null} - disabledReason={ - isDataWarehouseSeries ? 'Breakdowns are not available for data warehouse series' : undefined - } + disabledReason={disabledReason} > {taxonomicBreakdownType === TaxonomicFilterGroupType.CohortsWithAllUsers ? 'Add cohort' diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx index 80658031113ca..a6b1b69744db6 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownFilter.tsx @@ -12,7 +12,7 @@ export interface TaxonomicBreakdownFilterProps { breakdownFilter?: BreakdownFilter | null display?: ChartDisplayType | null isTrends: boolean - isDataWarehouseSeries: boolean + disabledReason?: string updateBreakdownFilter: (breakdownFilter: BreakdownFilter) => void updateDisplay: (display: ChartDisplayType | undefined) => void } @@ -22,7 +22,7 @@ export function TaxonomicBreakdownFilter({ breakdownFilter, display, isTrends, - isDataWarehouseSeries, + disabledReason, updateBreakdownFilter, updateDisplay, }: TaxonomicBreakdownFilterProps): JSX.Element { @@ -49,7 +49,7 @@ export function TaxonomicBreakdownFilter({
{tags} - {!hasNonCohortBreakdown && } + {!hasNonCohortBreakdown && }
) diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx index 24be984cf9b84..abbcb7020c116 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/TaxonomicBreakdownPopover.tsx @@ -19,7 +19,7 @@ export const TaxonomicBreakdownPopover = ({ open, setOpen, children }: Taxonomic const { groupsTaxonomicTypes } = useValues(groupsModel) const { taxonomicBreakdownType, includeSessions } = useValues(taxonomicBreakdownFilterLogic) - const { breakdownFilter } = useValues(taxonomicBreakdownFilterLogic) + const { breakdownFilter, currentDataWarehouseSchemaColumns } = useValues(taxonomicBreakdownFilterLogic) const { addBreakdown } = useActions(taxonomicBreakdownFilterLogic) const taxonomicGroupTypes = [ @@ -30,6 +30,7 @@ export const TaxonomicBreakdownPopover = ({ open, setOpen, children }: Taxonomic TaxonomicFilterGroupType.CohortsWithAllUsers, ...(includeSessions ? [TaxonomicFilterGroupType.Sessions] : []), TaxonomicFilterGroupType.HogQLExpression, + TaxonomicFilterGroupType.DataWarehouseProperties, ] return ( @@ -46,6 +47,7 @@ export const TaxonomicBreakdownPopover = ({ open, setOpen, children }: Taxonomic }} eventNames={allEventNames} taxonomicGroupTypes={taxonomicGroupTypes} + schemaColumns={currentDataWarehouseSchemaColumns} /> } visible={open} diff --git a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts index 9b3c7a622b900..ea80dbafc8733 100644 --- a/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts +++ b/frontend/src/scenes/insights/filters/BreakdownFilter/taxonomicBreakdownFilterLogic.ts @@ -9,6 +9,7 @@ import { TaxonomicFilterGroupType, TaxonomicFilterValue, } from 'lib/components/TaxonomicFilter/types' +import { insightVizDataLogic } from 'scenes/insights/insightVizDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { propertyDefinitionsModel } from '~/models/propertyDefinitionsModel' @@ -35,7 +36,14 @@ export const taxonomicBreakdownFilterLogic = kea ({ values: [propertyDefinitionsModel, ['getPropertyDefinition']] })), + connect((props: TaxonomicBreakdownFilterLogicProps) => ({ + values: [ + insightVizDataLogic(props.insightProps), + ['currentDataWarehouseSchemaColumns'], + propertyDefinitionsModel, + ['getPropertyDefinition'], + ], + })), actions({ addBreakdown: (breakdown: TaxonomicFilterValue, taxonomicGroup: TaxonomicFilterGroup) => ({ breakdown, diff --git a/frontend/src/scenes/insights/insightVizDataLogic.ts b/frontend/src/scenes/insights/insightVizDataLogic.ts index 977b68682704e..a73ffdc89afa9 100644 --- a/frontend/src/scenes/insights/insightVizDataLogic.ts +++ b/frontend/src/scenes/insights/insightVizDataLogic.ts @@ -11,6 +11,7 @@ import { import { dayjs } from 'lib/dayjs' import { dateMapping } from 'lib/utils' import posthog from 'posthog-js' +import { dataWarehouseSceneLogic } from 'scenes/data-warehouse/external/dataWarehouseSceneLogic' import { insightDataLogic, queryFromKind } from 'scenes/insights/insightDataLogic' import { keyForInsightLogicProps } from 'scenes/insights/sharedUtils' import { sceneLogic } from 'scenes/sceneLogic' @@ -32,6 +33,8 @@ import { } from '~/queries/nodes/InsightViz/utils' import { BreakdownFilter, + DatabaseSchemaQueryResponseField, + DataWarehouseNode, DateRange, FunnelExclusionSteps, FunnelsQuery, @@ -79,6 +82,8 @@ export const insightVizDataLogic = kea([ ['query', 'insightQuery', 'insightData', 'insightDataLoading', 'insightDataError'], filterTestAccountsDefaultsLogic, ['filterTestAccountsDefault'], + dataWarehouseSceneLogic, + ['externalTablesMap'], ], actions: [ insightLogic, @@ -214,6 +219,12 @@ export const insightVizDataLogic = kea([ return ((isTrends && !!formula) || (series || []).length <= 1) && !breakdownFilter?.breakdown }, ], + isBreakdownSeries: [ + (s) => [s.breakdownFilter], + (breakdownFilter): boolean => { + return !!breakdownFilter?.breakdown + }, + ], isDataWarehouseSeries: [ (s) => [s.isTrends, s.series], @@ -222,6 +233,28 @@ export const insightVizDataLogic = kea([ }, ], + currentDataWarehouseSchemaColumns: [ + (s) => [s.series, s.isSingleSeries, s.isDataWarehouseSeries, s.isBreakdownSeries, s.externalTablesMap], + ( + series, + isSingleSeries, + isDataWarehouseSeries, + isBreakdownSeries, + externalTablesMap + ): DatabaseSchemaQueryResponseField[] => { + if ( + !series || + series.length === 0 || + (!isSingleSeries && !isBreakdownSeries) || + !isDataWarehouseSeries + ) { + return [] + } + + return externalTablesMap[(series[0] as DataWarehouseNode).table_name].columns + }, + ], + valueOnSeries: [ (s) => [s.isTrends, s.isStickiness, s.isLifecycle, s.insightFilter], (isTrends, isStickiness, isLifecycle, insightFilter): boolean => { diff --git a/posthog/hogql_queries/insights/trends/trends_query_runner.py b/posthog/hogql_queries/insights/trends/trends_query_runner.py index 6cf84dcb5357b..b9acb5c37d000 100644 --- a/posthog/hogql_queries/insights/trends/trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/trends_query_runner.py @@ -61,6 +61,7 @@ HogQLQueryModifiers, DataWarehouseEventsModifier, ) +from posthog.warehouse.models import DataWarehouseTable from posthog.utils import format_label_date @@ -696,18 +697,39 @@ def _is_breakdown_field_boolean(self): ): return False - if self.query.breakdownFilter.breakdown_type == "person": - property_type = PropertyDefinition.Type.PERSON - elif self.query.breakdownFilter.breakdown_type == "group": - property_type = PropertyDefinition.Type.GROUP + if ( + isinstance(self.query.series[0], DataWarehouseNode) + and self.query.breakdownFilter.breakdown_type == "data_warehouse" + ): + series = self.query.series[0] # only one series when data warehouse is active + table_model = ( + DataWarehouseTable.objects.filter(name=series.table_name, team=self.team).exclude(deleted=True).first() + ) + + if not table_model: + raise ValueError(f"Table {series.table_name} not found") + + field_type = dict(table_model.columns)[self.query.breakdownFilter.breakdown] + + if field_type.startswith("Nullable("): + field_type = field_type.replace("Nullable(", "")[:-1] + + if field_type == "Bool": + return True + else: - property_type = PropertyDefinition.Type.EVENT + if self.query.breakdownFilter.breakdown_type == "person": + property_type = PropertyDefinition.Type.PERSON + elif self.query.breakdownFilter.breakdown_type == "group": + property_type = PropertyDefinition.Type.GROUP + else: + property_type = PropertyDefinition.Type.EVENT - field_type = self._event_property( - self.query.breakdownFilter.breakdown, - property_type, - self.query.breakdownFilter.breakdown_group_type_index, - ) + field_type = self._event_property( + self.query.breakdownFilter.breakdown, + property_type, + self.query.breakdownFilter.breakdown_group_type_index, + ) return field_type == "Boolean" def _convert_boolean(self, value: Any): diff --git a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr index d5a3675a2c2ca..c2982e4f8526f 100644 --- a/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr +++ b/posthog/session_recordings/test/__snapshots__/test_session_recordings.ambr @@ -782,30 +782,6 @@ AND "posthog_persondistinctid"."team_id" = 2) /*controller='project_session_recordings-list',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/session_recordings/%3F%24'*/ ''' # --- -# name: TestSessionRecordings.test_get_session_recordings.36 - ''' - SELECT "posthog_persondistinctid"."id", - "posthog_persondistinctid"."team_id", - "posthog_persondistinctid"."person_id", - "posthog_persondistinctid"."distinct_id", - "posthog_persondistinctid"."version", - "posthog_person"."id", - "posthog_person"."created_at", - "posthog_person"."properties_last_updated_at", - "posthog_person"."properties_last_operation", - "posthog_person"."team_id", - "posthog_person"."properties", - "posthog_person"."is_user_id", - "posthog_person"."is_identified", - "posthog_person"."uuid", - "posthog_person"."version" - FROM "posthog_persondistinctid" - INNER JOIN "posthog_person" ON ("posthog_persondistinctid"."person_id" = "posthog_person"."id") - WHERE ("posthog_persondistinctid"."distinct_id" IN ('user2', - 'user_one_0') - AND "posthog_persondistinctid"."team_id" = 2) /*controller='project_session_recordings-list',route='api/projects/%28%3FP%3Cparent_lookup_team_id%3E%5B%5E/.%5D%2B%29/session_recordings/%3F%24'*/ - ''' -# --- # name: TestSessionRecordings.test_get_session_recordings.4 ''' SELECT "posthog_team"."id", From de67c1cc3003bb56c76cf8808bebdbc61337c52f Mon Sep 17 00:00:00 2001 From: Tom Owers Date: Wed, 20 Mar 2024 08:13:55 +0100 Subject: [PATCH 02/17] feat(data-warehouse): Override external data table fields (#20997) * Added the ability to add extra hogql fields to stripe tables * Snapshot and mypy * Update query snapshots * Fixed tests * Pass on the visit_expression_field_type func * Update query snapshots * Added all other stripe definitions * Updated mypy * Update query snapshots * Update query snapshots * Update query snapshots * Update query snapshots * Update query snapshots --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- mypy-baseline.txt | 8 +- posthog/hogql/autocomplete.py | 16 +- posthog/hogql/database/database.py | 6 + posthog/hogql/database/models.py | 11 +- .../test/__snapshots__/test_database.ambr | 16 + posthog/hogql/database/test/test_database.py | 2 +- .../test/__snapshots__/test_resolver.ambr | 315 ++++++++++++++++++ posthog/hogql/test/test_autocomplete.py | 19 +- posthog/hogql/test/test_resolver.py | 10 + posthog/hogql/visitor.py | 3 + .../models/external_table_definitions.py | 285 ++++++++++++++++ posthog/warehouse/models/table.py | 14 +- 12 files changed, 688 insertions(+), 17 deletions(-) create mode 100644 posthog/warehouse/models/external_table_definitions.py diff --git a/mypy-baseline.txt b/mypy-baseline.txt index c095bc7fff997..781ad2980830b 100644 --- a/mypy-baseline.txt +++ b/mypy-baseline.txt @@ -97,7 +97,7 @@ posthog/hogql/database/database.py:0: error: "FieldOrTable" has no attribute "fi posthog/hogql/database/database.py:0: error: "FieldOrTable" has no attribute "fields" [attr-defined] posthog/hogql/database/database.py:0: error: "FieldOrTable" has no attribute "fields" [attr-defined] posthog/hogql/database/database.py:0: error: "FieldOrTable" has no attribute "fields" [attr-defined] -posthog/hogql/database/database.py:0: error: Incompatible types (expression has type "Literal['view', 'lazy_table']", TypedDict item "type" has type "Literal['integer', 'float', 'string', 'datetime', 'date', 'boolean', 'array', 'json', 'lazy_table', 'virtual_table', 'field_traverser']") [typeddict-item] +posthog/hogql/database/database.py:0: error: Incompatible types (expression has type "Literal['view', 'lazy_table']", TypedDict item "type" has type "Literal['integer', 'float', 'string', 'datetime', 'date', 'boolean', 'array', 'json', 'lazy_table', 'virtual_table', 'field_traverser', 'expression']") [typeddict-item] posthog/warehouse/models/datawarehouse_saved_query.py:0: error: Argument 1 to "create_hogql_database" has incompatible type "int | None"; expected "int" [arg-type] posthog/warehouse/models/datawarehouse_saved_query.py:0: error: Incompatible types in assignment (expression has type "Expr", variable has type "SelectQuery | SelectUnionQuery") [assignment] posthog/models/user.py:0: error: Incompatible types in assignment (expression has type "None", base class "AbstractUser" defined the type as "CharField[str | int | Combinable, str]") [assignment] @@ -419,7 +419,6 @@ posthog/api/feature_flag.py:0: error: Item "Sequence[Any]" of "Any | Sequence[An posthog/api/feature_flag.py:0: error: Item "None" of "Any | Sequence[Any] | None" has no attribute "filters" [union-attr] posthog/api/survey.py:0: error: Incompatible types in assignment (expression has type "Any | Sequence[Any] | None", variable has type "Survey | None") [assignment] posthog/api/user.py:0: error: "User" has no attribute "social_auth" [attr-defined] -ee/api/role.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] ee/api/dashboard_collaborator.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] ee/api/test/base.py:0: error: Incompatible types in assignment (expression has type "None", variable has type "License") [assignment] ee/api/test/base.py:0: error: "setUpTestData" undefined in superclass [misc] @@ -594,6 +593,8 @@ posthog/hogql/test/test_resolver.py:0: error: Incompatible types in assignment ( posthog/hogql/test/test_resolver.py:0: error: "TestResolver" has no attribute "snapshot" [attr-defined] posthog/hogql/test/test_resolver.py:0: error: Incompatible types in assignment (expression has type "Expr", variable has type "SelectQuery") [assignment] posthog/hogql/test/test_resolver.py:0: error: "TestResolver" has no attribute "snapshot" [attr-defined] +posthog/hogql/test/test_resolver.py:0: error: Incompatible types in assignment (expression has type "Expr", variable has type "SelectQuery") [assignment] +posthog/hogql/test/test_resolver.py:0: error: "TestResolver" has no attribute "snapshot" [attr-defined] posthog/hogql/test/test_resolver.py:0: error: Item "SelectUnionQueryType" of "SelectQueryType | SelectUnionQueryType | None" has no attribute "columns" [union-attr] posthog/hogql/test/test_resolver.py:0: error: Item "None" of "SelectQueryType | SelectUnionQueryType | None" has no attribute "columns" [union-attr] posthog/hogql/test/test_resolver.py:0: error: "FieldOrTable" has no attribute "fields" [attr-defined] @@ -649,7 +650,7 @@ posthog/hogql/functions/test/test_cohort.py:0: error: "TestCohort" has no attrib posthog/hogql/database/schema/test/test_channel_type.py:0: error: Value of type "list[Any] | None" is not indexable [index] posthog/hogql/database/schema/test/test_channel_type.py:0: error: Value of type "list[Any] | None" is not indexable [index] posthog/api/organization_member.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] -ee/api/feature_flag_role_access.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] +ee/api/role.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] ee/clickhouse/views/insights.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] posthog/queries/trends/test/test_person.py:0: error: "str" has no attribute "get" [attr-defined] posthog/queries/trends/test/test_person.py:0: error: Invalid index type "int" for "HttpResponse"; expected type "str | bytes" [index] @@ -754,6 +755,7 @@ posthog/api/property_definition.py:0: error: Incompatible types in assignment (e posthog/api/property_definition.py:0: error: Item "AnonymousUser" of "User | AnonymousUser" has no attribute "organization" [union-attr] posthog/api/property_definition.py:0: error: Item "None" of "Organization | Any | None" has no attribute "is_feature_available" [union-attr] posthog/api/dashboards/dashboard_templates.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] +ee/api/feature_flag_role_access.py:0: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases [misc] posthog/temporal/tests/batch_exports/test_run_updates.py:0: error: Unused "type: ignore" comment [unused-ignore] posthog/temporal/tests/batch_exports/test_run_updates.py:0: error: Unused "type: ignore" comment [unused-ignore] posthog/temporal/tests/batch_exports/test_run_updates.py:0: error: Item "None" of "BatchExportRun | None" has no attribute "data_interval_start" [union-attr] diff --git a/posthog/hogql/autocomplete.py b/posthog/hogql/autocomplete.py index 80e88cb7a3ea5..a7339a80fafd5 100644 --- a/posthog/hogql/autocomplete.py +++ b/posthog/hogql/autocomplete.py @@ -1,7 +1,7 @@ from copy import copy, deepcopy from typing import Callable, Dict, List, Optional, cast from posthog.hogql.context import HogQLContext -from posthog.hogql.database.database import create_hogql_database +from posthog.hogql.database.database import Database, create_hogql_database from posthog.hogql.database.models import ( BooleanDatabaseField, DatabaseField, @@ -239,6 +239,10 @@ def append_table_field_to_response(table: Table, suggestions: List[AutocompleteC details: List[str | None] = [] table_fields = list(table.fields.items()) for field_name, field_or_table in table_fields: + # Skip over hidden fields + if isinstance(field_or_table, ast.DatabaseField) and field_or_table.hidden: + continue + keys.append(field_name) details.append(convert_field_or_table_to_type_string(field_or_table)) @@ -278,11 +282,17 @@ def extend_responses( # TODO: Support ast.SelectUnionQuery nodes -def get_hogql_autocomplete(query: HogQLAutocomplete, team: Team) -> HogQLAutocompleteResponse: +def get_hogql_autocomplete( + query: HogQLAutocomplete, team: Team, database_arg: Optional[Database] = None +) -> HogQLAutocompleteResponse: response = HogQLAutocompleteResponse(suggestions=[], incomplete_list=False) timings = HogQLTimings() - database = create_hogql_database(team_id=team.pk, team_arg=team) + if database_arg is not None: + database = database_arg + else: + database = create_hogql_database(team_id=team.pk, team_arg=team) + context = HogQLContext(team_id=team.pk, team=team, database=database) original_query_select = copy(query.select) diff --git a/posthog/hogql/database/database.py b/posthog/hogql/database/database.py index 52a65644f76cf..6909211070e59 100644 --- a/posthog/hogql/database/database.py +++ b/posthog/hogql/database/database.py @@ -309,6 +309,7 @@ class _SerializedFieldBase(TypedDict): "lazy_table", "virtual_table", "field_traverser", + "expression", ] @@ -346,6 +347,9 @@ def serialize_fields(field_input, context: HogQLContext) -> List[SerializedField if field_key == "team_id": pass elif isinstance(field, DatabaseField): + if field.hidden: + continue + if isinstance(field, IntegerDatabaseField): field_output.append({"key": field_key, "type": "integer"}) elif isinstance(field, FloatDatabaseField): @@ -362,6 +366,8 @@ def serialize_fields(field_input, context: HogQLContext) -> List[SerializedField field_output.append({"key": field_key, "type": "json"}) elif isinstance(field, StringArrayDatabaseField): field_output.append({"key": field_key, "type": "array"}) + elif isinstance(field, ExpressionField): + field_output.append({"key": field_key, "type": "expression"}) elif isinstance(field, LazyJoin): is_view = isinstance(field.resolve_table(context), SavedQuery) field_output.append( diff --git a/posthog/hogql/database/models.py b/posthog/hogql/database/models.py index e95a26614bed8..95a00595c6472 100644 --- a/posthog/hogql/database/models.py +++ b/posthog/hogql/database/models.py @@ -24,6 +24,7 @@ class DatabaseField(FieldOrTable): name: str array: Optional[bool] = None nullable: Optional[bool] = None + hidden: bool = False class IntegerDatabaseField(DatabaseField): @@ -95,15 +96,11 @@ def get_asterisk(self): for key, field in self.fields.items(): if key in fields_to_avoid: continue - if ( - isinstance(field, Table) - or isinstance(field, LazyJoin) - or isinstance(field, FieldTraverser) - or isinstance(field, ExpressionField) - ): + if isinstance(field, Table) or isinstance(field, LazyJoin) or isinstance(field, FieldTraverser): pass # ignore virtual tables and columns for now elif isinstance(field, DatabaseField): - asterisk[key] = field + if not field.hidden: # Skip over hidden fields + asterisk[key] = field else: raise HogQLException(f"Unknown field type {type(field).__name__} for asterisk") return asterisk diff --git a/posthog/hogql/database/test/__snapshots__/test_database.ambr b/posthog/hogql/database/test/__snapshots__/test_database.ambr index 21c60457a1fd3..db4dfc8f6df9f 100644 --- a/posthog/hogql/database/test/__snapshots__/test_database.ambr +++ b/posthog/hogql/database/test/__snapshots__/test_database.ambr @@ -269,6 +269,14 @@ "distinct_id", "person_id" ] + }, + { + "key": "$virt_initial_referring_domain_type", + "type": "expression" + }, + { + "key": "$virt_initial_channel_type", + "type": "expression" } ], "person_distinct_ids": [ @@ -1112,6 +1120,14 @@ "distinct_id", "person_id" ] + }, + { + "key": "$virt_initial_referring_domain_type", + "type": "expression" + }, + { + "key": "$virt_initial_channel_type", + "type": "expression" } ], "person_distinct_ids": [ diff --git a/posthog/hogql/database/test/test_database.py b/posthog/hogql/database/test/test_database.py index da17e15c03107..99810ca54e58a 100644 --- a/posthog/hogql/database/test/test_database.py +++ b/posthog/hogql/database/test/test_database.py @@ -134,7 +134,7 @@ def test_database_expression_fields(self): query = print_ast(parse_select(sql), context, dialect="clickhouse") assert ( query - == "SELECT number AS number FROM (SELECT numbers.number AS number FROM numbers(2) AS numbers) LIMIT 10000" + == "SELECT number AS number, expression AS expression, double AS double FROM (SELECT numbers.number AS number, plus(1, 1) AS expression, multiply(numbers.number, 2) AS double FROM numbers(2) AS numbers) LIMIT 10000" ), query def test_database_warehouse_joins(self): diff --git a/posthog/hogql/test/__snapshots__/test_resolver.ambr b/posthog/hogql/test/__snapshots__/test_resolver.ambr index 1b086d067a621..74ac2f12adc82 100644 --- a/posthog/hogql/test/__snapshots__/test_resolver.ambr +++ b/posthog/hogql/test/__snapshots__/test_resolver.ambr @@ -582,6 +582,321 @@ } ''' # --- +# name: TestResolver.test_asterisk_expander_hidden_field + ''' + { + select: [ + { + alias: "uuid" + expr: { + chain: [ + "uuid" + ] + type: { + name: "uuid" + table_type: { + table: { + fields: { + $group_0: {}, + $group_1: {}, + $group_2: {}, + $group_3: {}, + $group_4: {}, + $session_id: {}, + $window_id: {}, + created_at: {}, + distinct_id: {}, + elements_chain: {}, + event: {}, + goe_0: {}, + goe_1: {}, + goe_2: {}, + goe_3: {}, + goe_4: {}, + group_0: {}, + group_1: {}, + group_2: {}, + group_3: {}, + group_4: {}, + hidden_field: {}, + pdi: {}, + person: {}, + person_id: {}, + poe: {}, + properties: {}, + session: {}, + team_id: {}, + timestamp: {}, + uuid: {} + } + } + } + } + } + hidden: True + type: { + alias: "uuid" + type: + } + }, + { + alias: "event" + expr: { + chain: [ + "event" + ] + type: { + name: "event" + table_type: + } + } + hidden: True + type: { + alias: "event" + type: + } + }, + { + alias: "properties" + expr: { + chain: [ + "properties" + ] + type: { + name: "properties" + table_type: + } + } + hidden: True + type: { + alias: "properties" + type: + } + }, + { + alias: "timestamp" + expr: { + chain: [ + "timestamp" + ] + type: { + name: "timestamp" + table_type: + } + } + hidden: True + type: { + alias: "timestamp" + type: + } + }, + { + alias: "distinct_id" + expr: { + chain: [ + "distinct_id" + ] + type: { + name: "distinct_id" + table_type: + } + } + hidden: True + type: { + alias: "distinct_id" + type: + } + }, + { + alias: "elements_chain" + expr: { + chain: [ + "elements_chain" + ] + type: { + name: "elements_chain" + table_type: + } + } + hidden: True + type: { + alias: "elements_chain" + type: + } + }, + { + alias: "created_at" + expr: { + chain: [ + "created_at" + ] + type: { + name: "created_at" + table_type: + } + } + hidden: True + type: { + alias: "created_at" + type: + } + }, + { + alias: "$session_id" + expr: { + chain: [ + "$session_id" + ] + type: { + name: "$session_id" + table_type: + } + } + hidden: True + type: { + alias: "$session_id" + type: + } + }, + { + alias: "$window_id" + expr: { + chain: [ + "$window_id" + ] + type: { + name: "$window_id" + table_type: + } + } + hidden: True + type: { + alias: "$window_id" + type: + } + }, + { + alias: "$group_0" + expr: { + chain: [ + "$group_0" + ] + type: { + name: "$group_0" + table_type: + } + } + hidden: True + type: { + alias: "$group_0" + type: + } + }, + { + alias: "$group_1" + expr: { + chain: [ + "$group_1" + ] + type: { + name: "$group_1" + table_type: + } + } + hidden: True + type: { + alias: "$group_1" + type: + } + }, + { + alias: "$group_2" + expr: { + chain: [ + "$group_2" + ] + type: { + name: "$group_2" + table_type: + } + } + hidden: True + type: { + alias: "$group_2" + type: + } + }, + { + alias: "$group_3" + expr: { + chain: [ + "$group_3" + ] + type: { + name: "$group_3" + table_type: + } + } + hidden: True + type: { + alias: "$group_3" + type: + } + }, + { + alias: "$group_4" + expr: { + chain: [ + "$group_4" + ] + type: { + name: "$group_4" + table_type: + } + } + hidden: True + type: { + alias: "$group_4" + type: + } + } + ] + select_from: { + table: { + chain: [ + "events" + ] + type: + } + type: + } + type: { + aliases: {} + anonymous_tables: [] + columns: { + $group_0: , + $group_1: , + $group_2: , + $group_3: , + $group_4: , + $session_id: , + $window_id: , + created_at: , + distinct_id: , + elements_chain: , + event: , + properties: , + timestamp: , + uuid: + } + ctes: {} + tables: { + events: + } + } + } + ''' +# --- # name: TestResolver.test_asterisk_expander_select_union ''' { diff --git a/posthog/hogql/test/test_autocomplete.py b/posthog/hogql/test/test_autocomplete.py index 46eb8a1cd0394..bf92abf6e359e 100644 --- a/posthog/hogql/test/test_autocomplete.py +++ b/posthog/hogql/test/test_autocomplete.py @@ -1,4 +1,7 @@ +from typing import Optional from posthog.hogql.autocomplete import get_hogql_autocomplete +from posthog.hogql.database.database import Database, create_hogql_database +from posthog.hogql.database.models import StringDatabaseField from posthog.hogql.database.schema.events import EventsTable from posthog.hogql.database.schema.persons import PERSONS_FIELDS from posthog.models.property_definition import PropertyDefinition @@ -21,9 +24,11 @@ def _create_properties(self): type=PropertyDefinition.Type.PERSON, ) - def _query_response(self, query: str, start: int, end: int) -> HogQLAutocompleteResponse: + def _query_response( + self, query: str, start: int, end: int, database: Optional[Database] = None + ) -> HogQLAutocompleteResponse: autocomplete = HogQLAutocomplete(kind="HogQLAutocomplete", select=query, startPosition=start, endPosition=end) - return get_hogql_autocomplete(query=autocomplete, team=self.team) + return get_hogql_autocomplete(query=autocomplete, team=self.team, database_arg=database) def test_autocomplete(self): query = "select * from events" @@ -226,3 +231,13 @@ def test_autocomplete_non_existing_alias(self): results = self._query_response(query=query, start=9, end=9) assert len(results.suggestions) == 0 + + def test_autocomplete_events_hidden_field(self): + database = create_hogql_database(team_id=self.team.pk, team_arg=self.team) + database.events.fields["event"] = StringDatabaseField(name="event", hidden=True) + + query = "select from events" + results = self._query_response(query=query, start=7, end=7, database=database) + + for suggestion in results.suggestions: + assert suggestion.label != "event" diff --git a/posthog/hogql/test/test_resolver.py b/posthog/hogql/test/test_resolver.py index de448811b2584..88b7b2c50e0b5 100644 --- a/posthog/hogql/test/test_resolver.py +++ b/posthog/hogql/test/test_resolver.py @@ -10,6 +10,7 @@ from posthog.hogql.context import HogQLContext from posthog.hogql.database.database import create_hogql_database from posthog.hogql.database.models import ( + ExpressionField, FieldTraverser, StringJSONDatabaseField, StringDatabaseField, @@ -252,6 +253,15 @@ def test_asterisk_expander_subquery(self): node = resolve_types(node, self.context, dialect="clickhouse") assert pretty_dataclasses(node) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") + def test_asterisk_expander_hidden_field(self): + self.database.events.fields["hidden_field"] = ExpressionField( + name="hidden_field", hidden=True, expr=ast.Field(chain=["event"]) + ) + node = self._select("select * from events") + node = resolve_types(node, self.context, dialect="clickhouse") + assert pretty_dataclasses(node) == self.snapshot + @pytest.mark.usefixtures("unittest_snapshot") def test_asterisk_expander_subquery_alias(self): node = self._select("select x.* from (select 1 as a, 2 as b) x") diff --git a/posthog/hogql/visitor.py b/posthog/hogql/visitor.py index 9acb6fec87db7..c11856169297f 100644 --- a/posthog/hogql/visitor.py +++ b/posthog/hogql/visitor.py @@ -247,6 +247,9 @@ def visit_window_frame_expr(self, node: ast.WindowFrameExpr): def visit_join_constraint(self, node: ast.JoinConstraint): self.visit(node.expr) + def visit_expression_field_type(self, node: ast.ExpressionFieldType): + pass + def visit_hogqlx_tag(self, node: ast.HogQLXTag): for attribute in node.attributes: self.visit(attribute) diff --git a/posthog/warehouse/models/external_table_definitions.py b/posthog/warehouse/models/external_table_definitions.py new file mode 100644 index 0000000000000..6522271a35783 --- /dev/null +++ b/posthog/warehouse/models/external_table_definitions.py @@ -0,0 +1,285 @@ +from typing import Dict +from posthog.hogql import ast +from posthog.hogql.database.models import ( + BooleanDatabaseField, + FieldOrTable, + IntegerDatabaseField, + StringDatabaseField, + StringJSONDatabaseField, +) + + +external_tables: Dict[str, Dict[str, FieldOrTable]] = { + "stripe_customer": { + "id": StringDatabaseField(name="id"), + "name": StringDatabaseField(name="name"), + "email": StringDatabaseField(name="email"), + "phone": StringDatabaseField(name="phone"), + "object": StringDatabaseField(name="object"), + "address": StringJSONDatabaseField(name="address"), + "balance": IntegerDatabaseField(name="balance"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "currency": StringDatabaseField(name="currency"), + "discount": StringJSONDatabaseField(name="discount"), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "shipping": StringJSONDatabaseField(name="shipping"), + "delinquent": BooleanDatabaseField(name="delinquent"), + "tax_exempt": StringDatabaseField(name="tax_exempt"), + "description": StringDatabaseField(name="description"), + "default_source": StringDatabaseField(name="default_source"), + "invoice_prefix": StringDatabaseField(name="invoice_prefix"), + "invoice_settings": StringJSONDatabaseField(name="invoice_settings"), + "preferred_locales": StringJSONDatabaseField(name="preferred_locales"), + "next_invoice_sequence": IntegerDatabaseField(name="next_invoice_sequence"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, + "stripe_invoice": { + "id": StringDatabaseField(name="id"), + "tax": IntegerDatabaseField(name="tax"), + "paid": BooleanDatabaseField(name="paid"), + "lines": StringJSONDatabaseField(name="lines"), + "total": IntegerDatabaseField(name="total"), + "charge": StringDatabaseField(name="charge"), + "issuer": StringJSONDatabaseField(name="issuer"), + "number": StringDatabaseField(name="number"), + "object": StringDatabaseField(name="object"), + "status": StringDatabaseField(name="status"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "currency": StringDatabaseField(name="currency"), + "customer_id": StringDatabaseField(name="customer"), + "discount": StringJSONDatabaseField(name="discount"), + "due_date": IntegerDatabaseField(name="due_date"), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "subtotal": IntegerDatabaseField(name="subtotal"), + "attempted": BooleanDatabaseField(name="attempted"), + "discounts": StringJSONDatabaseField(name="discounts"), + "rendering": StringJSONDatabaseField(name="rendering"), + "amount_due": IntegerDatabaseField(name="amount_due"), + "__period_start": IntegerDatabaseField(name="period_start", hidden=True), + "period_start_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__period_start"])]), name="period_start_at" + ), + "__period_end": IntegerDatabaseField(name="period_end", hidden=True), + "period_end_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__period_end"])]), name="period_end_at" + ), + "amount_paid": IntegerDatabaseField(name="amount_paid"), + "description": StringDatabaseField(name="description"), + "invoice_pdf": StringDatabaseField(name="invoice_pdf"), + "account_name": StringDatabaseField(name="account_name"), + "auto_advance": BooleanDatabaseField(name="auto_advance"), + "__effective_at": IntegerDatabaseField(name="effective_at", hidden=True), + "effective_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__effective_at"])]), name="effective_at" + ), + "subscription_id": StringDatabaseField(name="subscription"), + "attempt_count": IntegerDatabaseField(name="attempt_count"), + "automatic_tax": StringJSONDatabaseField(name="automatic_tax"), + "customer_name": StringDatabaseField(name="customer_name"), + "billing_reason": StringDatabaseField(name="billing_reason"), + "customer_email": StringDatabaseField(name="customer_email"), + "ending_balance": IntegerDatabaseField(name="ending_balance"), + "payment_intent": StringDatabaseField(name="payment_intent"), + "account_country": StringDatabaseField(name="account_country"), + "amount_shipping": IntegerDatabaseField(name="amount_shipping"), + "amount_remaining": IntegerDatabaseField(name="amount_remaining"), + "customer_address": StringJSONDatabaseField(name="customer_address"), + "customer_tax_ids": StringJSONDatabaseField(name="customer_tax_ids"), + "paid_out_of_band": BooleanDatabaseField(name="paid_out_of_band"), + "payment_settings": StringJSONDatabaseField(name="payment_settings"), + "starting_balance": IntegerDatabaseField(name="starting_balance"), + "collection_method": StringDatabaseField(name="collection_method"), + "default_tax_rates": StringJSONDatabaseField(name="default_tax_rates"), + "total_tax_amounts": StringJSONDatabaseField(name="total_tax_amounts"), + "hosted_invoice_url": StringDatabaseField(name="hosted_invoice_url"), + "status_transitions": StringJSONDatabaseField(name="status_transitions"), + "customer_tax_exempt": StringDatabaseField(name="customer_tax_exempt"), + "total_excluding_tax": IntegerDatabaseField(name="total_excluding_tax"), + "subscription_details": StringJSONDatabaseField(name="subscription_details"), + "__webhooks_delivered_at": IntegerDatabaseField(name="webhooks_delivered_at", hidden=True), + "webhooks_delivered_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__webhooks_delivered_at"])]), + name="webhooks_delivered_at", + ), + "subtotal_excluding_tax": IntegerDatabaseField(name="subtotal_excluding_tax"), + "total_discount_amounts": StringJSONDatabaseField(name="total_discount_amounts"), + "pre_payment_credit_notes_amount": IntegerDatabaseField(name="pre_payment_credit_notes_amount"), + "post_payment_credit_notes_amount": IntegerDatabaseField(name="post_payment_credit_notes_amount"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, + "stripe_charge": { + "id": StringDatabaseField(name="id"), + "paid": BooleanDatabaseField(name="paid"), + "amount": IntegerDatabaseField(name="amount"), + "object": StringDatabaseField(name="object"), + "source": StringJSONDatabaseField(name="source"), + "status": StringDatabaseField(name="status"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "invoice_id": StringDatabaseField(name="invoice"), + "outcome": StringJSONDatabaseField(name="outcome"), + "captured": BooleanDatabaseField(name="captured"), + "currency": StringDatabaseField(name="currency"), + "customer_id": StringDatabaseField(name="customer"), + "disputed": BooleanDatabaseField(name="disputed"), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "refunded": BooleanDatabaseField(name="refunded"), + "description": StringDatabaseField(name="description"), + "receipt_url": StringDatabaseField(name="receipt_url"), + "failure_code": StringDatabaseField(name="failure_code"), + "fraud_details": StringJSONDatabaseField(name="fraud_details"), + "radar_options": StringJSONDatabaseField(name="radar_options"), + "receipt_email": StringDatabaseField(name="receipt_email"), + "payment_intent_id": StringDatabaseField(name="payment_intent"), + "payment_method_id": StringDatabaseField(name="payment_method"), + "amount_captured": IntegerDatabaseField(name="amount_captured"), + "amount_refunded": IntegerDatabaseField(name="amount_refunded"), + "billing_details": StringJSONDatabaseField(name="billing_details"), + "failure_message": StringDatabaseField(name="failure_message"), + "balance_transaction_id": StringDatabaseField(name="balance_transaction"), + "statement_descriptor": StringDatabaseField(name="statement_descriptor"), + "payment_method_details": StringJSONDatabaseField(name="payment_method_details"), + "calculated_statement_descriptor": StringDatabaseField(name="calculated_statement_descriptor"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, + "stripe_price": { + "id": StringDatabaseField(name="id"), + "type": StringDatabaseField(name="type"), + "active": BooleanDatabaseField(name="active"), + "object": StringDatabaseField(name="object"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "product_id": StringDatabaseField(name="product"), + "currency": StringDatabaseField(name="currency"), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "nickname": StringDatabaseField(name="nickname"), + "recurring": StringJSONDatabaseField(name="recurring"), + "tiers_mode": StringDatabaseField(name="tiers_mode"), + "unit_amount": IntegerDatabaseField(name="unit_amount"), + "tax_behavior": StringDatabaseField(name="tax_behavior"), + "billing_scheme": StringDatabaseField(name="billing_scheme"), + "unit_amount_decimal": StringDatabaseField(name="unit_amount_decimal"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, + "stripe_product": { + "id": StringDatabaseField(name="id"), + "name": StringDatabaseField(name="name"), + "type": StringDatabaseField(name="type"), + "active": BooleanDatabaseField(name="active"), + "images": StringJSONDatabaseField(name="images"), + "object": StringDatabaseField(name="object"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "__updated": IntegerDatabaseField(name="updated", hidden=True), + "updated_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__updated"])]), name="updated_at" + ), + "features": StringJSONDatabaseField(name="features"), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "tax_code": StringDatabaseField(name="tax_code"), + "attributes": StringJSONDatabaseField(name="attributes"), + "description": StringDatabaseField(name="description"), + "default_price_id": StringDatabaseField(name="default_price"), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + }, + "stripe_subscription": { + "id": StringDatabaseField(name="id"), + "plan": StringJSONDatabaseField(name="plan"), + "items": StringJSONDatabaseField(name="items"), + "object": StringDatabaseField(name="object"), + "status": StringDatabaseField(name="status"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "currency": StringDatabaseField(name="currency"), + "customer_id": StringDatabaseField(name="customer"), + "__ended_at": IntegerDatabaseField(name="ended_at", hidden=True), + "ended_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__ended_at"])]), name="ended_at" + ), + "livemode": BooleanDatabaseField(name="livemode"), + "metadata": StringJSONDatabaseField(name="metadata"), + "quantity": IntegerDatabaseField(name="quantity"), + "__start_date": IntegerDatabaseField(name="start_date", hidden=True), + "start_date": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__start_date"])]), name="start_date" + ), + "__canceled_at": IntegerDatabaseField(name="canceled_at", hidden=True), + "canceled_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__canceled_at"])]), name="canceled_at" + ), + "automatic_tax": StringJSONDatabaseField(name="automatic_tax"), + "latest_invoice_id": StringDatabaseField(name="latest_invoice"), + "trial_settings": StringJSONDatabaseField(name="trial_settings"), + "invoice_settings": StringJSONDatabaseField(name="invoice_settings"), + "payment_settings": StringJSONDatabaseField(name="payment_settings"), + "collection_method": StringDatabaseField(name="collection_method"), + "default_tax_rates": StringJSONDatabaseField(name="default_tax_rates"), + "__current_period_start": IntegerDatabaseField(name="current_period_start", hidden=True), + "current_period_start": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__current_period_start"])]), + name="current_period_start", + ), + "__current_period_end": IntegerDatabaseField(name="current_period_end", hidden=True), + "current_period_end": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__current_period_end"])]), + name="current_period_end", + ), + "__billing_cycle_anchor": IntegerDatabaseField(name="billing_cycle_anchor", hidden=True), + "billing_cycle_anchor": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__billing_cycle_anchor"])]), + name="billing_cycle_anchor", + ), + "cancel_at_period_end": BooleanDatabaseField(name="cancel_at_period_end"), + "cancellation_details": StringJSONDatabaseField(name="cancellation_details"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, + "stripe_balancetransaction": { + "id": StringDatabaseField(name="id"), + "fee": IntegerDatabaseField(name="fee"), + "net": IntegerDatabaseField(name="net"), + "type": StringDatabaseField(name="type"), + "amount": IntegerDatabaseField(name="amount"), + "object": StringDatabaseField(name="object"), + "source_id": StringDatabaseField(name="source"), + "status": StringDatabaseField(name="status"), + "__created": IntegerDatabaseField(name="created", hidden=True), + "created_at": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__created"])]), name="created_at" + ), + "currency": StringDatabaseField(name="currency"), + "description": StringDatabaseField(name="description"), + "fee_details": StringJSONDatabaseField(name="fee_details"), + "__available_on": IntegerDatabaseField(name="available_on", hidden=True), + "available_on": ast.ExpressionField( + expr=ast.Call(name="fromUnixTimestamp", args=[ast.Field(chain=["__available_on"])]), name="available_on" + ), + "reporting_category": StringDatabaseField(name="reporting_category"), + "__dlt_id": StringDatabaseField(name="_dlt_id", hidden=True), + "__dlt_load_id": StringDatabaseField(name="_dlt_load_id", hidden=True), + }, +} diff --git a/posthog/warehouse/models/table.py b/posthog/warehouse/models/table.py index 5fbe84b3f34d9..91c6f61709d6e 100644 --- a/posthog/warehouse/models/table.py +++ b/posthog/warehouse/models/table.py @@ -7,6 +7,7 @@ BooleanDatabaseField, DateDatabaseField, DateTimeDatabaseField, + FieldOrTable, IntegerDatabaseField, StringArrayDatabaseField, StringDatabaseField, @@ -27,6 +28,7 @@ from uuid import UUID from sentry_sdk import capture_exception from posthog.warehouse.util import database_sync_to_async +from .external_table_definitions import external_tables CLICKHOUSE_HOGQL_MAPPING = { "UUID": StringDatabaseField, @@ -98,6 +100,13 @@ class TableFormat(models.TextChoices): __repr__ = sane_repr("name") + def table_name_without_prefix(self) -> str: + if self.external_data_source is not None and self.external_data_source.prefix is not None: + prefix = self.external_data_source.prefix + else: + prefix = "" + return self.name[len(prefix) :] + def get_columns(self, safe_expose_ch_error=True) -> Dict[str, str]: try: result = sync_execute( @@ -126,7 +135,7 @@ def hogql_definition(self) -> S3Table: if not self.columns: raise Exception("Columns must be fetched and saved to use in HogQL.") - fields = {} + fields: Dict[str, FieldOrTable] = {} structure = [] for column, type in self.columns.items(): # Support for 'old' style columns @@ -153,6 +162,9 @@ def hogql_definition(self) -> S3Table: fields[column] = hogql_type(name=column) + # Replace fields with any redefined fields if they exist + fields = external_tables.get(self.table_name_without_prefix(), fields) + return S3Table( name=self.name, url=self.url_pattern, From 1a6948eb048284a5da621fd784713d0966758443 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Wed, 20 Mar 2024 07:50:47 +0000 Subject: [PATCH 03/17] feat: support padding for mobile replay styling (#21020) * feat: support padding for mobile replay styling * fix * schema --- .../__snapshots__/transform.test.ts.snap | 149 +++++++++++++++++- ee/frontend/mobile-replay/mobile.types.ts | 16 ++ .../schema/mobile/rr-mobile-schema.json | 16 ++ ee/frontend/mobile-replay/transform.test.ts | 32 ++++ .../transformer/wireframeStyle.ts | 15 ++ 5 files changed, 225 insertions(+), 3 deletions(-) diff --git a/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap b/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap index 45a3fe627f7fa..889de23db5174 100644 --- a/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap +++ b/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap @@ -1381,7 +1381,7 @@ exports[`replay/transform transform incremental mutations de-duplicate the tree "node": { "attributes": { "data-rrweb-id": 99571736, - "style": "color: #000000;width: 150px;height: 19px;position: fixed;left: 10px;top: 584px;align-items: flex-start;justify-content: flex-start;display: flex;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", + "style": "color: #000000;width: 150px;height: 19px;position: fixed;left: 10px;top: 584px;align-items: flex-start;justify-content: flex-start;display: flex;padding-left: 0px;padding-right: 0px;padding-top: 0px;padding-bottom: 0px;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", }, "childNodes": [], "id": 99571736, @@ -1404,7 +1404,7 @@ exports[`replay/transform transform incremental mutations de-duplicate the tree "node": { "attributes": { "data-rrweb-id": 240124529, - "style": "color: #000000;width: 48px;height: 32px;position: fixed;left: 10px;top: 548px;align-items: center;justify-content: center;display: flex;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", + "style": "color: #000000;width: 48px;height: 32px;position: fixed;left: 10px;top: 548px;align-items: center;justify-content: center;display: flex;padding-left: 32px;padding-right: 0px;padding-top: 6px;padding-bottom: 6px;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", }, "childNodes": [], "id": 240124529, @@ -1427,7 +1427,7 @@ exports[`replay/transform transform incremental mutations de-duplicate the tree "node": { "attributes": { "data-rrweb-id": 52129787, - "style": "color: #000000;width: 368px;height: 19px;position: fixed;left: 66px;top: 556px;align-items: flex-start;justify-content: flex-start;display: flex;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", + "style": "color: #000000;width: 368px;height: 19px;position: fixed;left: 66px;top: 556px;align-items: flex-start;justify-content: flex-start;display: flex;padding-left: 0px;padding-right: 0px;padding-top: 0px;padding-bottom: 0px;font-size: 14px;font-family: sans-serif;overflow:hidden;white-space:normal;", }, "childNodes": [], "id": 52129787, @@ -5421,6 +5421,149 @@ exports[`replay/transform transform inputs input - url - https://example.io 1`] } `; +exports[`replay/transform transform inputs input gets 0 padding by default but can be overridden 1`] = ` +{ + "data": { + "initialOffset": { + "left": 0, + "top": 0, + }, + "node": { + "childNodes": [ + { + "id": 2, + "name": "html", + "publicId": "", + "systemId": "", + "type": 1, + }, + { + "attributes": { + "data-rrweb-id": 3, + "style": "height: 100vh; width: 100vw;", + }, + "childNodes": [ + { + "attributes": { + "data-rrweb-id": 4, + }, + "childNodes": [ + { + "attributes": { + "type": "text/css", + }, + "childNodes": [ + { + "id": 101, + "textContent": " + body { + margin: unset; + } + input, button, select, textarea { + font: inherit; + margin: 0; + padding: 0; + border: 0; + outline: 0; + background: transparent; + } + .input:focus { + outline: none; + } + img { + border-style: none; + } + ", + "type": 3, + }, + ], + "id": 100, + "tagName": "style", + "type": 2, + }, + ], + "id": 4, + "tagName": "head", + "type": 2, + }, + { + "attributes": { + "data-rrweb-id": 5, + "style": "height: 100vh; width: 100vw;", + }, + "childNodes": [ + { + "attributes": { + "data-rrweb-id": 12359, + "style": "width: 100px;height: 30px;position: fixed;left: 0px;top: 0px;", + "type": "text", + "value": "", + }, + "childNodes": [], + "id": 12359, + "tagName": "input", + "type": 2, + }, + { + "attributes": { + "data-rrweb-id": 12361, + "style": "width: 100px;height: 30px;position: fixed;left: 0px;top: 0px;padding-left: 16px;padding-right: 16px;", + "type": "text", + "value": "", + }, + "childNodes": [], + "id": 12361, + "tagName": "input", + "type": 2, + }, + { + "attributes": { + "data-render-reason": "a fixed placeholder to contain the keyboard in the correct stacking position", + "data-rrweb-id": 9, + }, + "childNodes": [], + "id": 9, + "tagName": "div", + "type": 2, + }, + { + "attributes": { + "data-rrweb-id": 7, + }, + "childNodes": [], + "id": 7, + "tagName": "div", + "type": 2, + }, + { + "attributes": { + "data-rrweb-id": 11, + }, + "childNodes": [], + "id": 11, + "tagName": "div", + "type": 2, + }, + ], + "id": 5, + "tagName": "body", + "type": 2, + }, + ], + "id": 3, + "tagName": "html", + "type": 2, + }, + ], + "id": 1, + "type": 0, + }, + }, + "timestamp": 1, + "type": 2, +} +`; + exports[`replay/transform transform inputs isolated add mutation 1`] = ` { "data": { diff --git a/ee/frontend/mobile-replay/mobile.types.ts b/ee/frontend/mobile-replay/mobile.types.ts index f994dae58e076..a4b447d75235c 100644 --- a/ee/frontend/mobile-replay/mobile.types.ts +++ b/ee/frontend/mobile-replay/mobile.types.ts @@ -121,6 +121,22 @@ export type MobileStyles = { * @description maps to CSS font-family. Accepts any valid CSS font-family value. */ fontFamily?: string + /** + * @description maps to CSS padding-left. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px + */ + paddingLeft?: string | number + /** + * @description maps to CSS padding-right. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px + */ + paddingRight?: string | number + /** + * @description maps to CSS padding-top. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px + */ + paddingTop?: string | number + /** + * @description maps to CSS padding-bottom. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px + */ + paddingBottom?: string | number } type wireframeBase = { diff --git a/ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json b/ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json index 4dc7ea0dda0e2..49a0a0e613b48 100644 --- a/ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json +++ b/ee/frontend/mobile-replay/schema/mobile/rr-mobile-schema.json @@ -334,6 +334,22 @@ "enum": ["left", "right", "center"], "type": "string" }, + "paddingBottom": { + "description": "maps to CSS padding-bottom. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px", + "type": ["string", "number"] + }, + "paddingLeft": { + "description": "maps to CSS padding-left. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px", + "type": ["string", "number"] + }, + "paddingRight": { + "description": "maps to CSS padding-right. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px", + "type": ["string", "number"] + }, + "paddingTop": { + "description": "maps to CSS padding-top. Expects a number (treated as pixels) or a string that is a number followed by px e.g. 16px", + "type": ["string", "number"] + }, "verticalAlign": { "description": "vertical alignment with respect to its parent", "enum": ["top", "bottom", "center"], diff --git a/ee/frontend/mobile-replay/transform.test.ts b/ee/frontend/mobile-replay/transform.test.ts index 9e0ddf6b0e05e..788bb65655d3d 100644 --- a/ee/frontend/mobile-replay/transform.test.ts +++ b/ee/frontend/mobile-replay/transform.test.ts @@ -58,6 +58,7 @@ describe('replay/transform', () => { beforeEach(async () => { posthogEEModule = await posthogEE() }) + test('can process unknown types without error', () => { expect( posthogEEModule.mobileReplay?.transformToWeb([ @@ -535,6 +536,37 @@ describe('replay/transform', () => { }) describe('inputs', () => { + test('input gets 0 padding by default but can be overridden', () => { + expect( + posthogEEModule.mobileReplay?.transformEventToWeb({ + type: 2, + data: { + wireframes: [ + { + id: 12359, + width: 100, + height: 30, + type: 'input', + inputType: 'text', + }, + { + id: 12361, + width: 100, + height: 30, + type: 'input', + inputType: 'text', + style: { + paddingLeft: '16px', + paddingRight: 16, + }, + }, + ], + }, + timestamp: 1, + }) + ).toMatchSnapshot() + }) + test('buttons with nested elements', () => { expect( posthogEEModule.mobileReplay?.transformEventToWeb({ diff --git a/ee/frontend/mobile-replay/transformer/wireframeStyle.ts b/ee/frontend/mobile-replay/transformer/wireframeStyle.ts index 4160070cbcfcd..ccd06bfcc662c 100644 --- a/ee/frontend/mobile-replay/transformer/wireframeStyle.ts +++ b/ee/frontend/mobile-replay/transformer/wireframeStyle.ts @@ -130,9 +130,24 @@ function makeLayoutStyles(wireframe: wireframe, styleOverride?: StyleOverride): }` ) } + if (styleParts.length) { styleParts.push(`display: flex`) } + + if (isUnitLike(combinedStyles.paddingLeft)) { + styleParts.push(`padding-left: ${ensureUnit(combinedStyles.paddingLeft)}`) + } + if (isUnitLike(combinedStyles.paddingRight)) { + styleParts.push(`padding-right: ${ensureUnit(combinedStyles.paddingRight)}`) + } + if (isUnitLike(combinedStyles.paddingTop)) { + styleParts.push(`padding-top: ${ensureUnit(combinedStyles.paddingTop)}`) + } + if (isUnitLike(combinedStyles.paddingBottom)) { + styleParts.push(`padding-bottom: ${ensureUnit(combinedStyles.paddingBottom)}`) + } + return asStyleString(styleParts) } From 7076cd4bc14e52b27bca99ba2419f600b6cc1649 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Far=C3=ADas=20Santana?= Date: Wed, 20 Mar 2024 10:58:35 +0100 Subject: [PATCH 04/17] feat: Support for s3 compatible destinations in batch exports (#20894) * feat: Backend support for endpoint_url * feat: Frontend support for endpoint_url * fix: Update story * fix: Remove deprecated comment Co-authored-by: ted kaemming <65315+tkaemming@users.noreply.github.com> * fix: Update S3 test and README * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * Update query snapshots * Update query snapshots --------- Co-authored-by: ted kaemming <65315+tkaemming@users.noreply.github.com> Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .../batch_exports/BatchExportEditForm.tsx | 9 ++ .../batch_exports/BatchExports.stories.tsx | 1 + .../batch_exports/batchExportEditLogic.ts | 1 + frontend/src/types.ts | 1 + posthog/batch_exports/service.py | 1 + .../temporal/batch_exports/s3_batch_export.py | 7 + .../temporal/tests/batch_exports/README.md | 11 +- .../test_s3_batch_export_workflow.py | 147 ++++++++---------- 8 files changed, 90 insertions(+), 88 deletions(-) diff --git a/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx b/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx index 106da039cd22d..7fbbc8cc29d69 100644 --- a/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx +++ b/frontend/src/scenes/batch_exports/BatchExportEditForm.tsx @@ -267,6 +267,15 @@ export function BatchExportsEditFields({ )}
+ Only required if exporting to an S3-compatible blob storage (like MinIO)} + > + + + bool: @contextlib.asynccontextmanager async def s3_client(self): """Asynchronously yield an S3 client.""" + async with self._session.client( "s3", region_name=self.region_name, aws_access_key_id=self.aws_access_key_id, aws_secret_access_key=self.aws_secret_access_key, + endpoint_url=self.endpoint_url, ) as client: yield client @@ -306,6 +310,7 @@ class S3InsertInputs: encryption: str | None = None kms_key_id: str | None = None batch_export_schema: BatchExportSchema | None = None + endpoint_url: str | None = None async def initialize_and_resume_multipart_upload(inputs: S3InsertInputs) -> tuple[S3MultiPartUpload, str]: @@ -321,6 +326,7 @@ async def initialize_and_resume_multipart_upload(inputs: S3InsertInputs) -> tupl region_name=inputs.region, aws_access_key_id=inputs.aws_access_key_id, aws_secret_access_key=inputs.aws_secret_access_key, + endpoint_url=inputs.endpoint_url, ) details = activity.info().heartbeat_details @@ -555,6 +561,7 @@ async def run(self, inputs: S3BatchExportInputs): team_id=inputs.team_id, aws_access_key_id=inputs.aws_access_key_id, aws_secret_access_key=inputs.aws_secret_access_key, + endpoint_url=inputs.endpoint_url, data_interval_start=data_interval_start.isoformat(), data_interval_end=data_interval_end.isoformat(), compression=inputs.compression, diff --git a/posthog/temporal/tests/batch_exports/README.md b/posthog/temporal/tests/batch_exports/README.md index 12bf2f6177cd3..c3f63f176ebde 100644 --- a/posthog/temporal/tests/batch_exports/README.md +++ b/posthog/temporal/tests/batch_exports/README.md @@ -6,7 +6,8 @@ This module contains unit tests covering activities, workflows, and helper funct BigQuery batch exports can be tested against a real BigQuery instance, but doing so requires additional setup. For this reason, these tests are skipped unless an environment variable pointing to a BigQuery credentials file (`GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/project-credentials.json`) is set. -> :warning: Since BigQuery batch export tests require additional setup, we skip them by default and will not be ran by automated CI pipelines. Please ensure these tests pass when making changes that affect BigQuery batch exports. +> [!WARNING] +> Since BigQuery batch export tests require additional setup, we skip them by default and will not be ran by automated CI pipelines. Please ensure these tests pass when making changes that affect BigQuery batch exports. To enable testing for BigQuery batch exports, we require: 1. A BigQuery project and dataset @@ -24,7 +25,8 @@ DEBUG=1 GOOGLE_APPLICATION_CREDENTIALS=/path/to/my/project-credentials.json pyte Redshift batch exports can be tested against a real Redshift (or Redshift Serverless) instance, with additional setup steps required. Due to this requirement, these tests are skipped unless Redshift credentials are specified in the environment. -> :warning: Since Redshift batch export tests require additional setup, we skip them by default and will not be ran by automated CI pipelines. Please ensure these tests pass when making changes that affect Redshift batch exports. +> [!WARNING] +> Since Redshift batch export tests require additional setup, we skip them by default and will not be ran by automated CI pipelines. Please ensure these tests pass when making changes that affect Redshift batch exports. To enable testing for Redshift batch exports, we require: 1. A Redshift (or Redshift Serverless) instance. @@ -41,14 +43,15 @@ Replace the `REDSHIFT_*` environment variables with the values obtained from the ## Testing S3 batch exports -S3 batch exports are tested against a MinIO bucket available in the local development stack. However there are also unit tests that specifically target an S3 bucket. Additional setup is required to run those specific tests: +S3 batch exports are tested against a MinIO bucket available in the local development stack. However there are also unit tests that specifically target an S3 bucket (like `test_s3_export_workflow_with_s3_bucket`). Additional setup is required to run those specific tests: 1. Ensure you are logged in to an AWS account. 2. Create or choose an existing S3 bucket from that AWS account to use as the test bucket. 3. Create or choose an existing KMS key id from that AWS account to use in tests. 4. Make sure the role/user you are logged in as has permissions to use the bucket and KMS key. -For PostHog employees, check your password manager for these details. +> [!NOTE] +> For PostHog employees, your password manager contains a set of credentials for S3 batch exports development testing. You may populate your development environment with these credentials and use the provided test bucket and KMS key. With these setup steps done, we can run all tests (MinIO and S3 bucket) from the root of the `posthog` repo with: diff --git a/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py index 17b1aaaa94d4a..5e741f9223321 100644 --- a/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py @@ -1,12 +1,10 @@ import asyncio -import contextlib import datetime as dt import functools import gzip import json import os from random import randint -from unittest import mock from uuid import uuid4 import aioboto3 @@ -337,6 +335,7 @@ async def test_insert_into_s3_activity_puts_data_into_s3( data_interval_end=data_interval_end.isoformat(), aws_access_key_id="object_storage_root_user", aws_secret_access_key="object_storage_root_password", + endpoint_url=settings.OBJECT_STORAGE_ENDPOINT, compression=compression, exclude_events=exclude_events, batch_export_schema=batch_export_schema, @@ -345,11 +344,7 @@ async def test_insert_into_s3_activity_puts_data_into_s3( with override_settings( BATCH_EXPORT_S3_UPLOAD_CHUNK_SIZE_BYTES=5 * 1024**2 ): # 5MB, the minimum for Multipart uploads - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.run(insert_into_s3_activity, insert_inputs) + await activity_environment.run(insert_into_s3_activity, insert_inputs) await assert_clickhouse_records_in_s3( s3_compatible_client=minio_client, @@ -368,7 +363,14 @@ async def test_insert_into_s3_activity_puts_data_into_s3( @pytest_asyncio.fixture async def s3_batch_export( - ateam, s3_key_prefix, bucket_name, compression, interval, exclude_events, temporal_client, encryption + ateam, + s3_key_prefix, + bucket_name, + compression, + interval, + exclude_events, + temporal_client, + encryption, ): destination_data = { "type": "S3", @@ -378,6 +380,7 @@ async def s3_batch_export( "prefix": s3_key_prefix, "aws_access_key_id": "object_storage_root_user", "aws_secret_access_key": "object_storage_root_password", + "endpoint_url": settings.OBJECT_STORAGE_ENDPOINT, "compression": compression, "exclude_events": exclude_events, "encryption": encryption, @@ -479,19 +482,14 @@ async def test_s3_export_workflow_with_minio_bucket( ], workflow_runner=UnsandboxedWorkflowRunner(), ): - # We patch the S3 client to return our client that targets MinIO. - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.client.execute_workflow( - S3BatchExportWorkflow.run, - inputs, - id=workflow_id, - task_queue=settings.TEMPORAL_TASK_QUEUE, - retry_policy=RetryPolicy(maximum_attempts=1), - execution_timeout=dt.timedelta(minutes=10), - ) + await activity_environment.client.execute_workflow( + S3BatchExportWorkflow.run, + inputs, + id=workflow_id, + task_queue=settings.TEMPORAL_TASK_QUEUE, + retry_policy=RetryPolicy(maximum_attempts=1), + execution_timeout=dt.timedelta(minutes=10), + ) runs = await afetch_batch_export_runs(batch_export_id=s3_batch_export.id) assert len(runs) == 1 @@ -523,10 +521,10 @@ async def s3_client(bucket_name, s3_key_prefix): using a disposable bucket to run these tests or sticking to other tests that use the local development MinIO. """ - async with aioboto3.Session().client("s3") as minio_client: - yield minio_client + async with aioboto3.Session().client("s3") as s3_client: + yield s3_client - await delete_all_from_s3(minio_client, bucket_name, key_prefix=s3_key_prefix) + await delete_all_from_s3(s3_client, bucket_name, key_prefix=s3_key_prefix) @pytest.mark.skipif( @@ -595,20 +593,20 @@ async def test_s3_export_workflow_with_s3_bucket( ) workflow_id = str(uuid4()) + destination_config = s3_batch_export.destination.config | { + "endpoint_url": None, + "aws_access_key_id": os.getenv("AWS_ACCESS_KEY_ID"), + "aws_secret_access_key": os.getenv("AWS_SECRET_ACCESS_KEY"), + } inputs = S3BatchExportInputs( team_id=ateam.pk, batch_export_id=str(s3_batch_export.id), data_interval_end=data_interval_end.isoformat(), interval=interval, batch_export_schema=batch_export_schema, - **s3_batch_export.destination.config, + **destination_config, ) - @contextlib.asynccontextmanager - async def create_minio_client(*args, **kwargs): - """Mock function to return an already initialized S3 client.""" - yield s3_client - async with await WorkflowEnvironment.start_time_skipping() as activity_environment: async with Worker( activity_environment.client, @@ -621,18 +619,14 @@ async def create_minio_client(*args, **kwargs): ], workflow_runner=UnsandboxedWorkflowRunner(), ): - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_minio_client, - ): - await activity_environment.client.execute_workflow( - S3BatchExportWorkflow.run, - inputs, - id=workflow_id, - task_queue=settings.TEMPORAL_TASK_QUEUE, - retry_policy=RetryPolicy(maximum_attempts=1), - execution_timeout=dt.timedelta(seconds=10), - ) + await activity_environment.client.execute_workflow( + S3BatchExportWorkflow.run, + inputs, + id=workflow_id, + task_queue=settings.TEMPORAL_TASK_QUEUE, + retry_policy=RetryPolicy(maximum_attempts=1), + execution_timeout=dt.timedelta(seconds=10), + ) runs = await afetch_batch_export_runs(batch_export_id=s3_batch_export.id) assert len(runs) == 1 @@ -641,7 +635,7 @@ async def create_minio_client(*args, **kwargs): assert run.status == "Completed" await assert_clickhouse_records_in_s3( - s3_compatible_client=minio_client, + s3_compatible_client=s3_client, clickhouse_client=clickhouse_client, bucket_name=bucket_name, key_prefix=s3_key_prefix, @@ -708,18 +702,14 @@ async def test_s3_export_workflow_with_minio_bucket_and_a_lot_of_data( ], workflow_runner=UnsandboxedWorkflowRunner(), ): - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.client.execute_workflow( - S3BatchExportWorkflow.run, - inputs, - id=workflow_id, - task_queue=settings.TEMPORAL_TASK_QUEUE, - retry_policy=RetryPolicy(maximum_attempts=1), - execution_timeout=dt.timedelta(seconds=360), - ) + await activity_environment.client.execute_workflow( + S3BatchExportWorkflow.run, + inputs, + id=workflow_id, + task_queue=settings.TEMPORAL_TASK_QUEUE, + retry_policy=RetryPolicy(maximum_attempts=1), + execution_timeout=dt.timedelta(seconds=360), + ) runs = await afetch_batch_export_runs(batch_export_id=s3_batch_export.id) assert len(runs) == 1 @@ -787,18 +777,14 @@ async def test_s3_export_workflow_defaults_to_timestamp_on_null_inserted_at( ], workflow_runner=UnsandboxedWorkflowRunner(), ): - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.client.execute_workflow( - S3BatchExportWorkflow.run, - inputs, - id=workflow_id, - task_queue=settings.TEMPORAL_TASK_QUEUE, - retry_policy=RetryPolicy(maximum_attempts=1), - execution_timeout=dt.timedelta(seconds=10), - ) + await activity_environment.client.execute_workflow( + S3BatchExportWorkflow.run, + inputs, + id=workflow_id, + task_queue=settings.TEMPORAL_TASK_QUEUE, + retry_policy=RetryPolicy(maximum_attempts=1), + execution_timeout=dt.timedelta(seconds=10), + ) runs = await afetch_batch_export_runs(batch_export_id=s3_batch_export.id) assert len(runs) == 1 @@ -875,18 +861,14 @@ async def test_s3_export_workflow_with_minio_bucket_and_custom_key_prefix( ], workflow_runner=UnsandboxedWorkflowRunner(), ): - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.client.execute_workflow( - S3BatchExportWorkflow.run, - inputs, - id=workflow_id, - task_queue=settings.TEMPORAL_TASK_QUEUE, - retry_policy=RetryPolicy(maximum_attempts=1), - execution_timeout=dt.timedelta(seconds=10), - ) + await activity_environment.client.execute_workflow( + S3BatchExportWorkflow.run, + inputs, + id=workflow_id, + task_queue=settings.TEMPORAL_TASK_QUEUE, + retry_policy=RetryPolicy(maximum_attempts=1), + execution_timeout=dt.timedelta(seconds=10), + ) runs = await afetch_batch_export_runs(batch_export_id=s3_batch_export.id) assert len(runs) == 1 @@ -1283,14 +1265,11 @@ def assert_heartbeat_details(*details): data_interval_end=data_interval_end.isoformat(), aws_access_key_id="object_storage_root_user", aws_secret_access_key="object_storage_root_password", + endpoint_url=settings.OBJECT_STORAGE_ENDPOINT, ) with override_settings(BATCH_EXPORT_S3_UPLOAD_CHUNK_SIZE_BYTES=5 * 1024**2): - with mock.patch( - "posthog.temporal.batch_exports.s3_batch_export.aioboto3.Session.client", - side_effect=create_test_client, - ): - await activity_environment.run(insert_into_s3_activity, insert_inputs) + await activity_environment.run(insert_into_s3_activity, insert_inputs) # This checks that the assert_heartbeat_details function was actually called. # The '+ 1' is because we increment current_part_number one last time after we are done. From 53355af5be43715eeb0270eca69b21b5d9790c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1s=20Far=C3=ADas=20Santana?= Date: Wed, 20 Mar 2024 11:23:05 +0100 Subject: [PATCH 05/17] feat: Start tracking records exported (#21008) * feat: Start tracking records exported * fix: Move records_completed inside try * test: Add more destination tests * fix: Redshift returns int * fix: Also return records_total from HTTP * Update query snapshots * Update query snapshots * Update query snapshots * Update query snapshots * fix: Return inside context manager Co-authored-by: Brett Hoerner * fix: Return inside context manager Co-authored-by: Brett Hoerner --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Brett Hoerner --- posthog/batch_exports/service.py | 6 ++++-- posthog/temporal/batch_exports/batch_exports.py | 6 +++++- .../batch_exports/bigquery_batch_export.py | 6 ++++-- .../temporal/batch_exports/http_batch_export.py | 6 ++++-- .../batch_exports/postgres_batch_export.py | 6 ++++-- .../batch_exports/redshift_batch_export.py | 17 +++++++++++++---- .../temporal/batch_exports/s3_batch_export.py | 6 ++++-- .../batch_exports/snowflake_batch_export.py | 6 ++++-- .../test_bigquery_batch_export_workflow.py | 2 ++ .../test_http_batch_export_workflow.py | 2 ++ .../test_postgres_batch_export_workflow.py | 2 ++ .../test_redshift_batch_export_workflow.py | 2 ++ .../test_s3_batch_export_workflow.py | 3 +++ .../test_snowflake_batch_export_workflow.py | 2 ++ 14 files changed, 55 insertions(+), 17 deletions(-) diff --git a/posthog/batch_exports/service.py b/posthog/batch_exports/service.py index f026fce03fb3f..c26be9a77ed1a 100644 --- a/posthog/batch_exports/service.py +++ b/posthog/batch_exports/service.py @@ -439,14 +439,16 @@ def create_batch_export_run( return run -def update_batch_export_run_status(run_id: UUID, status: str, latest_error: str | None) -> BatchExportRun: +def update_batch_export_run_status( + run_id: UUID, status: str, latest_error: str | None, records_completed: int = 0 +) -> BatchExportRun: """Update the status of an BatchExportRun with given id. Arguments: id: The id of the BatchExportRun to update. """ model = BatchExportRun.objects.filter(id=run_id) - updated = model.update(status=status, latest_error=latest_error) + updated = model.update(status=status, latest_error=latest_error, records_completed=records_completed) if not updated: raise ValueError(f"BatchExportRun with id {run_id} not found.") diff --git a/posthog/temporal/batch_exports/batch_exports.py b/posthog/temporal/batch_exports/batch_exports.py index 8fa61a370d5c3..c776e1f245ef3 100644 --- a/posthog/temporal/batch_exports/batch_exports.py +++ b/posthog/temporal/batch_exports/batch_exports.py @@ -534,6 +534,7 @@ class UpdateBatchExportRunStatusInputs: status: str team_id: int latest_error: str | None = None + records_completed: int = 0 @activity.defn @@ -545,6 +546,7 @@ async def update_export_run_status(inputs: UpdateBatchExportRunStatusInputs) -> run_id=uuid.UUID(inputs.id), status=inputs.status, latest_error=inputs.latest_error, + records_completed=inputs.records_completed, ) if batch_export_run.status in (BatchExportRun.Status.FAILED, BatchExportRun.Status.FAILED_RETRYABLE): @@ -664,13 +666,14 @@ async def execute_batch_export_insert_activity( ) try: - await workflow.execute_activity( + records_completed = await workflow.execute_activity( activity, inputs, start_to_close_timeout=dt.timedelta(seconds=start_to_close_timeout_seconds), heartbeat_timeout=dt.timedelta(seconds=heartbeat_timeout_seconds) if heartbeat_timeout_seconds else None, retry_policy=retry_policy, ) + update_inputs.records_completed = records_completed except exceptions.ActivityError as e: if isinstance(e.cause, exceptions.CancelledError): @@ -690,6 +693,7 @@ async def execute_batch_export_insert_activity( finally: get_export_finished_metric(status=update_inputs.status.lower()).add(1) + await workflow.execute_activity( update_export_run_status, update_inputs, diff --git a/posthog/temporal/batch_exports/bigquery_batch_export.py b/posthog/temporal/batch_exports/bigquery_batch_export.py index 9f39a302e9365..a0469de79bb9e 100644 --- a/posthog/temporal/batch_exports/bigquery_batch_export.py +++ b/posthog/temporal/batch_exports/bigquery_batch_export.py @@ -193,7 +193,7 @@ def bigquery_default_fields() -> list[BatchExportField]: @activity.defn -async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): +async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs) -> int: """Activity streams data from ClickHouse to BigQuery.""" logger = await bind_temporal_worker_logger(team_id=inputs.team_id, destination="BigQuery") logger.info( @@ -230,7 +230,7 @@ async def insert_into_bigquery_activity(inputs: BigQueryInsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows", count) @@ -354,6 +354,8 @@ async def flush_to_bigquery(bigquery_table, table_schema): jsonl_file.reset() + return jsonl_file.records_total + @workflow.defn(name="bigquery-export") class BigQueryBatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/batch_exports/http_batch_export.py b/posthog/temporal/batch_exports/http_batch_export.py index fde06c6c48d85..8aca65c80ff38 100644 --- a/posthog/temporal/batch_exports/http_batch_export.py +++ b/posthog/temporal/batch_exports/http_batch_export.py @@ -152,7 +152,7 @@ async def post_json_file_to_url(url, batch_file, session: aiohttp.ClientSession) @activity.defn -async def insert_into_http_activity(inputs: HttpInsertInputs): +async def insert_into_http_activity(inputs: HttpInsertInputs) -> int: """Activity streams data from ClickHouse to an HTTP Endpoint.""" logger = await bind_temporal_worker_logger(team_id=inputs.team_id, destination="HTTP") logger.info( @@ -180,7 +180,7 @@ async def insert_into_http_activity(inputs: HttpInsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows", count) @@ -303,6 +303,8 @@ async def flush_batch_to_http_endpoint(last_uploaded_timestamp: str, session: ai last_uploaded_timestamp = str(inserted_at) await flush_batch_to_http_endpoint(last_uploaded_timestamp, session) + return batch_file.records_total + @workflow.defn(name="http-export") class HttpBatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/batch_exports/postgres_batch_export.py b/posthog/temporal/batch_exports/postgres_batch_export.py index ef528b96f35ad..5dbfc6faa4acf 100644 --- a/posthog/temporal/batch_exports/postgres_batch_export.py +++ b/posthog/temporal/batch_exports/postgres_batch_export.py @@ -234,7 +234,7 @@ class PostgresInsertInputs: @activity.defn -async def insert_into_postgres_activity(inputs: PostgresInsertInputs): +async def insert_into_postgres_activity(inputs: PostgresInsertInputs) -> int: """Activity streams data from ClickHouse to Postgres.""" logger = await bind_temporal_worker_logger(team_id=inputs.team_id, destination="PostgreSQL") logger.info( @@ -262,7 +262,7 @@ async def insert_into_postgres_activity(inputs: PostgresInsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows", count) @@ -359,6 +359,8 @@ async def flush_to_postgres(): if pg_file.tell() > 0: await flush_to_postgres() + return pg_file.records_total + @workflow.defn(name="postgres-export") class PostgresBatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/batch_exports/redshift_batch_export.py b/posthog/temporal/batch_exports/redshift_batch_export.py index e47dcd2924517..bc1549cef838f 100644 --- a/posthog/temporal/batch_exports/redshift_batch_export.py +++ b/posthog/temporal/batch_exports/redshift_batch_export.py @@ -171,7 +171,7 @@ async def insert_records_to_redshift( schema: str | None, table: str, batch_size: int = 100, -): +) -> int: """Execute an INSERT query with given Redshift connection. The recommended way to insert multiple values into Redshift is using a COPY statement (see: @@ -206,15 +206,20 @@ async def insert_records_to_redshift( template = sql.SQL("({})").format(sql.SQL(", ").join(map(sql.Placeholder, columns))) rows_exported = get_rows_exported_metric() + total_rows_exported = 0 + async with async_client_cursor_from_connection(redshift_connection) as cursor: batch = [] pre_query_str = pre_query.as_string(cursor).encode("utf-8") async def flush_to_redshift(batch): + nonlocal total_rows_exported + values = b",".join(batch).replace(b" E'", b" '") await cursor.execute(pre_query_str + values) rows_exported.add(len(batch)) + total_rows_exported += len(batch) # It would be nice to record BYTES_EXPORTED for Redshift, but it's not worth estimating # the byte size of each batch the way things are currently written. We can revisit this # in the future if we decide it's useful enough. @@ -230,6 +235,8 @@ async def flush_to_redshift(batch): if len(batch) > 0: await flush_to_redshift(batch) + return total_rows_exported + @contextlib.asynccontextmanager async def async_client_cursor_from_connection( @@ -264,7 +271,7 @@ class RedshiftInsertInputs(PostgresInsertInputs): @activity.defn -async def insert_into_redshift_activity(inputs: RedshiftInsertInputs): +async def insert_into_redshift_activity(inputs: RedshiftInsertInputs) -> int: """Activity to insert data from ClickHouse to Redshift. This activity executes the following steps: @@ -306,7 +313,7 @@ async def insert_into_redshift_activity(inputs: RedshiftInsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows", count) @@ -383,13 +390,15 @@ def map_to_record(row: dict) -> dict: return record async with postgres_connection(inputs) as connection: - await insert_records_to_redshift( + records_completed = await insert_records_to_redshift( (map_to_record(record) for record_batch in record_iterator for record in record_batch.to_pylist()), connection, inputs.schema, inputs.table_name, ) + return records_completed + @workflow.defn(name="redshift-export") class RedshiftBatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/batch_exports/s3_batch_export.py b/posthog/temporal/batch_exports/s3_batch_export.py index 365613fd49ae4..4d99cbeffd7c3 100644 --- a/posthog/temporal/batch_exports/s3_batch_export.py +++ b/posthog/temporal/batch_exports/s3_batch_export.py @@ -388,7 +388,7 @@ def s3_default_fields() -> list[BatchExportField]: @activity.defn -async def insert_into_s3_activity(inputs: S3InsertInputs): +async def insert_into_s3_activity(inputs: S3InsertInputs) -> int: """Activity to batch export data from PostHog's ClickHouse to S3. It currently only creates a single file per run, and uploads as a multipart upload. @@ -424,7 +424,7 @@ async def insert_into_s3_activity(inputs: S3InsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows to S3", count) @@ -509,6 +509,8 @@ async def flush_to_s3(last_uploaded_part_timestamp: str, last=False): await s3_upload.complete() + return local_results_file.records_total + @workflow.defn(name="s3-export") class S3BatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/batch_exports/snowflake_batch_export.py b/posthog/temporal/batch_exports/snowflake_batch_export.py index c97b495979a2b..be94eca89a799 100644 --- a/posthog/temporal/batch_exports/snowflake_batch_export.py +++ b/posthog/temporal/batch_exports/snowflake_batch_export.py @@ -388,7 +388,7 @@ async def copy_loaded_files_to_snowflake_table( @activity.defn -async def insert_into_snowflake_activity(inputs: SnowflakeInsertInputs): +async def insert_into_snowflake_activity(inputs: SnowflakeInsertInputs) -> int: """Activity streams data from ClickHouse to Snowflake. TODO: We're using JSON here, it's not the most efficient way to do this. @@ -430,7 +430,7 @@ async def insert_into_snowflake_activity(inputs: SnowflakeInsertInputs): inputs.data_interval_start, inputs.data_interval_end, ) - return + return 0 logger.info("BatchExporting %s rows", count) @@ -553,6 +553,8 @@ async def worker_shutdown_handler(): await copy_loaded_files_to_snowflake_table(connection, inputs.table_name) + return local_results_file.records_total + @workflow.defn(name="snowflake-export") class SnowflakeBatchExportWorkflow(PostHogWorkflow): diff --git a/posthog/temporal/tests/batch_exports/test_bigquery_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_bigquery_batch_export_workflow.py index a7629309d940a..b2c46f6344dbc 100644 --- a/posthog/temporal/tests/batch_exports/test_bigquery_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_bigquery_batch_export_workflow.py @@ -453,6 +453,7 @@ async def test_bigquery_export_workflow( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 ingested_timestamp = frozen_time().replace(tzinfo=dt.timezone.utc) assert_clickhouse_records_in_bigquery( @@ -566,6 +567,7 @@ class RefreshError(Exception): run = runs[0] assert run.status == "Failed" assert run.latest_error == "RefreshError: A useful error message" + assert run.records_completed == 0 async def test_bigquery_export_workflow_handles_cancellation(ateam, bigquery_batch_export, interval): diff --git a/posthog/temporal/tests/batch_exports/test_http_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_http_batch_export_workflow.py index c2aa9646b6ddb..6267577472125 100644 --- a/posthog/temporal/tests/batch_exports/test_http_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_http_batch_export_workflow.py @@ -370,6 +370,7 @@ async def test_http_export_workflow( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 await assert_clickhouse_records_in_mock_server( mock_server=mock_server, @@ -425,6 +426,7 @@ async def insert_into_http_activity_mocked(_: HttpInsertInputs) -> str: run = runs[0] assert run.status == "FailedRetryable" assert run.latest_error == "ValueError: A useful error message" + assert run.records_completed == 0 async def test_http_export_workflow_handles_insert_activity_non_retryable_errors(ateam, http_batch_export, interval): diff --git a/posthog/temporal/tests/batch_exports/test_postgres_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_postgres_batch_export_workflow.py index e19e9c391dcfc..c486cc2747fcc 100644 --- a/posthog/temporal/tests/batch_exports/test_postgres_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_postgres_batch_export_workflow.py @@ -385,6 +385,7 @@ async def test_postgres_export_workflow( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 await assert_clickhouse_records_in_postgres( postgres_connection=postgres_connection, @@ -494,6 +495,7 @@ class InsufficientPrivilege(Exception): run = runs[0] assert run.status == "Failed" assert run.latest_error == "InsufficientPrivilege: A useful error message" + assert run.records_completed == 0 async def test_postgres_export_workflow_handles_cancellation(ateam, postgres_batch_export, interval): diff --git a/posthog/temporal/tests/batch_exports/test_redshift_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_redshift_batch_export_workflow.py index 301183f2998f4..173bed3a69bb3 100644 --- a/posthog/temporal/tests/batch_exports/test_redshift_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_redshift_batch_export_workflow.py @@ -433,6 +433,7 @@ async def test_redshift_export_workflow( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 await assert_clickhouse_records_in_redshfit( redshift_connection=psycopg_connection, @@ -559,3 +560,4 @@ class InsufficientPrivilege(Exception): run = runs[0] assert run.status == "Failed" assert run.latest_error == "InsufficientPrivilege: A useful error message" + assert run.records_completed == 0 diff --git a/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py index 5e741f9223321..e04e345d11245 100644 --- a/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_s3_batch_export_workflow.py @@ -791,6 +791,7 @@ async def test_s3_export_workflow_defaults_to_timestamp_on_null_inserted_at( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 await assert_clickhouse_records_in_s3( s3_compatible_client=minio_client, @@ -875,6 +876,7 @@ async def test_s3_export_workflow_with_minio_bucket_and_custom_key_prefix( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 100 expected_key_prefix = s3_key_prefix.format( table="events", @@ -950,6 +952,7 @@ async def insert_into_s3_activity_mocked(_: S3InsertInputs) -> str: run = runs[0] assert run.status == "FailedRetryable" assert run.latest_error == "ValueError: A useful error message" + assert run.records_completed == 0 async def test_s3_export_workflow_handles_insert_activity_non_retryable_errors(ateam, s3_batch_export, interval): diff --git a/posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py b/posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py index f43803d46cbd4..f8c12a3d1369f 100644 --- a/posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py +++ b/posthog/temporal/tests/batch_exports/test_snowflake_batch_export_workflow.py @@ -455,6 +455,7 @@ async def test_snowflake_export_workflow_exports_events( run = runs[0] assert run.status == "Completed" + assert run.records_completed == 10 @pytest.mark.parametrize("interval", ["hour", "day"], indirect=True) @@ -695,6 +696,7 @@ async def insert_into_snowflake_activity_mocked(_: SnowflakeInsertInputs) -> str run = runs[0] assert run.status == "FailedRetryable" assert run.latest_error == "ValueError: A useful error message" + assert run.records_completed == 0 async def test_snowflake_export_workflow_handles_insert_activity_non_retryable_errors(ateam, snowflake_batch_export): From c96f6b59fa71b6f0c23db4c8b3f79a42af3b80ec Mon Sep 17 00:00:00 2001 From: Ben White Date: Wed, 20 Mar 2024 12:02:46 +0100 Subject: [PATCH 06/17] fix: Email token validation response (#21036) --- posthog/api/authentication.py | 1 + posthog/api/test/test_authentication.py | 1 + 2 files changed, 2 insertions(+) diff --git a/posthog/api/authentication.py b/posthog/api/authentication.py index 10538c1d77ceb..069acac50c95f 100644 --- a/posthog/api/authentication.py +++ b/posthog/api/authentication.py @@ -385,6 +385,7 @@ def get_object(self): def retrieve(self, request: Request, *args: Any, **kwargs: Any) -> Response: response = super().retrieve(request, *args, **kwargs) response.status_code = self.SUCCESS_STATUS_CODE + response.data = None return response diff --git a/posthog/api/test/test_authentication.py b/posthog/api/test/test_authentication.py index ef83517c918c7..5501e7c43bce1 100644 --- a/posthog/api/test/test_authentication.py +++ b/posthog/api/test/test_authentication.py @@ -459,6 +459,7 @@ def test_can_validate_token(self): response = self.client.get(f"/api/reset/{self.user.uuid}/?token={token}") self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.content.decode(), "") + self.assertEqual(response.headers["Content-Length"], "0") def test_cant_validate_token_without_a_token(self): response = self.client.get(f"/api/reset/{self.user.uuid}/") From c55d50154f9fad9189664ca5d9d0c9388cca8c6f Mon Sep 17 00:00:00 2001 From: Ben White Date: Wed, 20 Mar 2024 12:20:45 +0100 Subject: [PATCH 07/17] chore: Remove link to support (#21013) --- ...es-other-password-reset--initial--dark.png | Bin 13189 -> 14460 bytes ...s-other-password-reset--initial--light.png | Bin 23644 -> 24879 bytes ...es-other-password-reset--success--dark.png | Bin 12758 -> 14405 bytes ...s-other-password-reset--success--light.png | Bin 25197 -> 26737 bytes ...-other-password-reset--throttled--dark.png | Bin 13757 -> 13999 bytes ...other-password-reset--throttled--light.png | Bin 27101 -> 27912 bytes .../scenes/authentication/PasswordReset.tsx | 6 +----- 7 files changed, 1 insertion(+), 5 deletions(-) diff --git a/frontend/__snapshots__/scenes-other-password-reset--initial--dark.png b/frontend/__snapshots__/scenes-other-password-reset--initial--dark.png index 9f1a52a1f90fa10773ee6979a2835f99f901f4df..6cb6eb6d746fc527112c981bf39847d3bfd78fbb 100644 GIT binary patch literal 14460 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 literal 13189 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�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

ugHWUqfPdDRT#3}P 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 diff --git a/frontend/__snapshots__/scenes-other-password-reset--success--dark.png b/frontend/__snapshots__/scenes-other-password-reset--success--dark.png index fb0de961d18c11755675b7982e7b9fff7cd86676..4bfe10a123e4fdd3561c6d5679cd550c6b6def94 100644 GIT binary patch literal 14405 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 h?+MEP4Url literal 12758 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 G2~7YP!b=qZ diff --git a/frontend/__snapshots__/scenes-other-password-reset--throttled--dark.png b/frontend/__snapshots__/scenes-other-password-reset--throttled--dark.png index 04c2aa439c2b4d074e3b925be2a0e2db79d89fc2..bb0239106bde1e4d0fb54ae704fd6b9d337ddf24 100644 GIT binary patch literal 13999 zcmeHuc~n#Px9>qzYEh~6Dg^|zqO>BSG8v|5tzwFR7MVw7NSP8Lgeinztx^?1L1su$ z6l7MW5T>Ar$QUF{0YVrP2t$~X5JHlBV!Ph&-GAO%Z>{^@efOCS7@FKJSmHZACo@F*@$ag4L$A)KQ4`slW(1LdMWD1O;&ACB_e-nnKE^2} z8FH%ql~q)PQ7O0?c2QYdZh~2gMnso?bz8J>am1g%kcd;_)N$5{UU&Bxe2O(&e`k-i z0%Cgmd3@02M7hQ=TS{-20x~rVawK0J`aK%0Z(-1|On|AUKg$b3!sZplPxN+Q4J&kC zo_iT8-q5o&`LRTWfWM(IOKl_IqTRYpJV(i+@Z84+o%a;+gzPXH$r=vvAEnJLuzyKB zgk8Vxi$JhxpW3!QPd~D7`)zeP ze|16ur>E=Fxj=R7Hk!}MiuhO~Jt`|J*XKw8;BW?HpEs9{7HcMG5uFzs1{k~{f7B?E znK!dslxTUGD;`gxyqt?OEx*0aqMiwN+;{t@48qprqf_cGq{70&6Dc}1%k?g3?g0{; ztrh(49*aA=X^b4JcL{$Nwi&P&0PGYHGCP5RD|=+wsP0rbW<@?Ui+pvs_ST51bwJX6 zl);sGMa0!9i14M~q;9f$w4YOknkl5jIa=$nHQ{M3Nn-z&ls9wd2DvjS|;T;8TxVPog9ir$~DIIEgbJ%cJQT8nTW`Rwg<# zAX+wf$5ryg15GvZugqG%3#=)vWX?`mBpuYCF0{Lh|Bcs;n(xi;kFATzNy$ z(QQ@*qh=Dkp5>;KlcAA*L7_E0i_tK&GA{8Y|%+GByFuqx-`uMD_N0jdumV5 z6Aj1u@Syl}Td!SEmNWdiiTf(LQ5(yh8TwjZaN-#l)D%Be%V>m2kAtqK%~*Jo?^|)g zJEH>rpxu&QgkMH?B*rPB?#yG>5;5+pjX}_DASIbmTCm7VO;hCCx75PN?`(0(aLIRM zGldIvqbLd^63;b^ShW#wIBugAftiB=*&u91&5kSZ>jU{M%&yE-9qh`>zWxDbuDUS6 zWo2F~a*{FR+Uhgt+R7}{D(ePih1V*pIe}RcRHvFw&7?|Bq`HfXX2vAXY#ImFf}beP zgKJl4lt4kzwSiEQsUhgTr^tPXldMxV3hQGx zS1a|h1-z46qujZ%@DR`ewfFa~Ps1F~ZR+}}H@de}V>sDQR;D;@cQ{!0gC`v#bUuZz zSe%bHWmZH`>J&OLV^|L}VLHWo;{82X;>V944`CKQlR&v*Cf#jqZ5u<`KSr#-jSUv6 zBfE@wkr+x)_c>3O+S=N){=5Y|I`&ZTZ7Y+N4VQ@ivWl4$=V||Zd?JyIrEKwA6um!o z`W6%w&C~erVwWg!>D1uQVLDNl7)W+s0ckx^op%y9`{XW)ML4=0!#Mt|7j{+r-;=MlmSumr6 z!*>~soHQJ(S#SR!s}O~ZhcIV%s~DVg4y^KS1kp z3r#X?yqW(6$1r0M2s15LYKO_Irf%&VWaBYaRaIExV?(th6vfmy6WJIs;2K7cTI?(H z0X=_~#?Q+{blX^)W>;5NYqV|7mk}P3`SgHVTAq@=-whBUI|Hg|K3F3ipsCOb49Ewr zCEI_dZ!L!khDjBukGo`bLzCo^6wRxDq`aEzG^IxN^|#q&@{F7UJ7g`sAFqw|d1nWO z6`mkemX{wLm&dLqsZ(}osuA{EnV2!Qn7*Wiassm(O2@l?TuRhPH}#)8fUR5PF&|R) z9vaFrtiRccpV5?I&--*{ z4a^VTB$!nWHtN)G3mP=!IIUc5)`%F9M_hXg3iyrZs~nAGESQ>`$P8bJ0WMT1TiuHS1r0U@~7( z)T6`#7DknGZ>_JdPXpE%+@6XWByc*PnyWq?toO;pFAln4T*WN8)cO|!Nwy*g-+~B9 z=ot6%W42@T`sdaY+q^;OR*Rf{ZkDYGc-U%!xs?A)YPI^}H9op5X1Ib3=rx~19}A8x7Kt(-kDg}g%D zAv~S{brnpsQK{=gslBx=xMQ~c1q3v*O6Q0kbY-9}ODqyt8i39f-NRb5Fah!dNO9DsUKZM#4-m<6^nhr7-n0P&cL1$bk_j|B_^a zXrWc<+-;B5gre!_<+~QEnGBcA($Z3mHjpWp#TAsRiBa=pD{Bl5H%F@GmzJKIk8@!ipvVGmjKKW=@Ax_yjIYZG zhPrTE!5L3OY|fyWZBpK=pwThMb(()~!^&&75s9Fad?`2nif~!vw(62=UPgF&bg*sw z-Q5bBkA?f;U)2CU1151lBDQJ2;5VKpvf+eSji&$TTuUssxbNJ0cOe zbTAS|H9sbI#X^IxP*ia9^Yd$BK3Ru27HVNY^P0j_)PtrcAd8jAmj%9aXIPyPGuKfY z%M&DCZPD6F0`BC+*u9I6P_Iu<4n-}?t<_~BKHXPA^i(wF_qp%n{jrIs#%}_eX7N`JAKDX>J|p4X%k^V*j#sJf;D< z|MQyQXfv3vwk$NZEV-}&c0fBsw;hs|!kt6-A0Z$9o* z?R=?Fhwr-j?c>;rw&N=He>5xlBaxIOYpc#;$BObU184i6>ymFyet^x*I*g)h#~;zd zr#QiU`jl?a^PYOWrkM;u*^1tZL3u)V_d z=U4-EkpDWho4~NOfYi8pOcjT(v}AU>JwaKiCBDsEk2b!Y*Lk&T9Yc+LAK1oJ;SIY` zH-9S}$@2nvzrv%4pkZ63WU?_s9?vZuAX7^JEW=Ip;o&07w9`l6_%o*lUDh)3hHC1| zOJOq|?q&sq{O!Q3`d^6xHLtt9ufXEy_>&`CniusLvH6tS=+lqFXgphS%f^?L^K@=B zfOAY<_0g{*n;yTqQY#vYe6H0h8+OTerR!=cn657eQ*PMUfN{dgo`Qk`$M}Ybw-5Gf z&h%Q~(u|jCbgfOq;TahjpiW1osj=!Q4g9epa;q#bYxs91PP%aL^-l@z|7;1axSA{n z#55Y@JU@fxatn$|tkG3C;kLtbpj%GH^G+g=lo!?aM5cRz^&e%viMiE(WAJ6fcuwD- z{yP5$p!il{Q4#eT^za_8?5~WTCn-DDsI-0>!&Bxw;kMFtphfxI?Kc~8O=Mg5!6$cQ z<@o&XfF3xQS3lJAIPb}3vxS)5fZcvHnyYj==PkJv%(ssK+pfL5zbPX!-3AmX;X;9H zC&1@^-#-x9sSW5PK#hSPl7X+lLT};LKDnljDau~pml^AGQd{}|w5a=6MP&VPysz~u zF2xW&VM=_(#gFttrf3frtLEdZrs6R#gJS8u^=obgN@ofgK0Cnj_m`r09XS?=Ne*0` zI4Zi>~2$E-miOiA;!pBrX8} zw6djw-hO^SkN(I>cU~w-Cqa!_2h@uSZ=Sy&z3@nh*Op}09`MEpOg?{j3I~J2Vy2Ur z!@e5HFwi_u4x*f=o)NBLwN!s>A{{sKTz*Wh|=Z?2K z4s?vV^`Aa$>B(=)A*!a;?lG|La%#5G4awGkv9egbkTAPpO=!jrqRZ0(C_Qb7SM99^ zU#^3uqpfORTa)FBPk=GZ`3F+V(g1#~?WR#?(kV*>UMpw5s>ElbR!KZp=F>AKm+yC; z8mwA|xoN=^@!;tqk>n~XOR1sSaOn-^ayHt#P&w1Sed|r(x6&2;wfbKM8eZ$$?8P|` zt2Gb@t8d0;Cq7q)oU+~XSSxhlo_4xuN>`WFD`8ia_%4_!p?S7iAubNCV71}dCtYxJ z!A=)U!B*T>$|9G0;>7Ptlz0U?Ww*C15(_^vcSE=OiY3`9Hgvt$x18s*4ZQ}!8tdP; z?gVmh((Q|)rzCRCVQr$D&D&B$u6FrE!cu$8;8JC8H??L;jW%wM`(^Rdo)ZHUL7Ss* z!xT|#(F6e2O^JZ)H4P$Q^CCXoMt82k`S_y|%s~HcS2*moGhAc_feWeDUhSHUtWp6g z6^~lvo@G8X|J9b!qsjV1JPp>newqFnEPeHV=v-@b*e5AeR;~~W22X`;VQ#Tx4vD<;U9ftbvC! zd2fX;3roeegj}DJDqW8{SFHiYV1o55 z(Ge`=nSu1xbfOg7d-OmDR|WAIS<#2Aw`>U(zBCsG9t41t1h7W)=HYynZtg9T&ybPG zz+&;!=K~$86f5?>Zs3>a^Wk;0aoA$5%8ip@Cj6^pFzIhv1^o=LL+e*1cPs{q^kHD- z4;QuI=RAZ2qxd&be~AW7ZF)Ep??@E}rhw|#N-4^ezaQIRXx&>KUQu_jVbama$HW=p3>}N#4L&Pq$Crg5I#UjGEGDVX6CH;&?Hjqo z=&6#;|7juqL$~T4&ZLZ#EfO7_5Je{Zn3B-{Ac-KMAt>!pv`y{7szx>{MB6ECpiXk_ zZ7GOY?pz1}gh%`}@u|Ia`H`QNUuhW~%%9j*)(udkI}*55RaHM{ZFWFEr^w*@1z>eCfuoKs8f5xJ;I+iV#i=D^V}MtT2GRKTyc z{S~8UM01^@nR}pxvD`wVjo{XtFQ)O11D&)rhJtqUmr3@O5U=lSSZh8o@8cTs%P^sa zOrg+u7-_D0ArtYdf_<%vXQ|Z9vVeoNOMGT zu5FTsN9#9V_PI;gf>bpRE+(N);f)~HxTt_yJfXj$6kKr}Y4Yg}Lj`sc>L*+V#D6n* zVFys8XfK`jaX<68_F;2^+uKqUZ`}mbX-qvcm7W~k*pj5)p=-YAx=)hmHQmNIAOTa^ zqf*oJ^D4QUV7ay=v@iB2Y~03+XS;nd;ak5LQ{H0`9aKUT$|%pO&_p7a09#X&E3RAv zfFAxANp{w{ORlpGN2+hRf{>UV`~jc7){M-r3;g`GdR-AZ+Q)T0*#y<+Hs_aa|F*<8 zwV{H?YAsoQD+30Nco2M_n#exsMR#Xq{AkX}2ImI?Z)o;srReEFOoQIE`y9gcNrd5+ zNyRB~WSMhxl9aJ@*~MBm{X<(1;jy;qAb(u@UP|@5Vv2Xw9yB5Nx{Dl~IQ2KvM~ak{ zb(c#xZB3?Q*~8a8f7SJJ@EfF`rpr5n#NcB@ASCl1BzV2kCR=qZ4mM0j+^^>>$DNy~ zH`_Y?hW*XIpnO1MtyFZ~$L;p&*|Jg0G0^x=&S!;OR}LG(zEV@P^9BWYOj>G;=^q^z zK~v7yn`9l~KySS|R95)>Omo5(C1AZs?gJEv+4es;tbY=g;M48RdNq?Un2#E$Oy$xx zEvsQ`2%&e0f?8(A&G~gHrJ_J@IwmERMBuBo$+m=7+$HEs@bH@080pnVhPLo(KWF9z zwVL+UG%xu^Qt<^!MlTpX_iXQhB=k3}ZF}=QsHk72l|W|r?-Ce8EZ>U(;GnCt7vP`m z_5U_%`$tv(sOo>&E>k5k1%PLW_x+mQ!+*HLS6)QSv6q%OL4e(^ep}lD|3it_%OvNk z>5@vZnjG-ZPT!NMhubfcbIxBuoyIt-0YjMj=l7vIyNxViHWvfg{;p99Pc#-)U44D$ zKcTegbBEqog-S-VMW(+ddNGUVq}gC`i1^?(06RTUG4||2HnpPV&&}d~7ag%o{dsI4 z`o6&+yaTP=J1V}4fWlM7YP*1kGsy3O9=WAD_8)yZ`1C!4h$YT-z%D|i#Pqa&AQdIi z*W`mq>@^Lr48PgbCoFCMlbIn_ll7=N^MZ8TMso{^OOOFBfv}yUaMDc=YPn~uYR8-U z3>80W<8N;N`Yr?=hGD8VbzF0I&MrO0(cB)>IF3C2p9j)*BMdO2Ek;zIi}q^?4n&6y zsM}@AGJ4W3GR~exSJ`5NyXn=%6v-ijs8{M3+w_Sh6x@r|r5rpdzAK#G004h5g$6u! z1em-yt{!gVndmh!IW19hdxk3@n;rEh(ebH(POEss`xq;=8w2U)q34a0%GaV<(=ba2~_UmX&1yo~YM%zY^A znp@ok(OC+gKlf`MCbBu6^DqK(Gmrn_L~x2O<1rncab)^Q2@C`VIJal zA0rN$oe2!;u1B~n;fL@V5mU#xkelRpv=A?P$L3fECaaDR&`~qbVm*f+gfHIdhs-ow z3>FZa*MxE-t3x&1L~oapnwAxt3QnkOIW><M(fwLcB)XT8XQGL9S#@z>Qav!xRdKIC(-vCJKr*Q%QK;mOs{;rAe6oWX*9& z6FjO#F^^nK(y(ELOy|h^ylOZaA<4`*n^Ss%EcW*|Tcy)T?6qcHpP#+2W}Ye2jTL)@ z`V|$e-n|$LZh^we=ROT*)i9c}g6O|%v^@~+>Ex+sKX_o{6E=NUvHPm6ZE%kj*p|^D z8BM%_{ zNiNKo0-qCBxR0Su0lFxSFyfo1{5@zj^MZ}^mx71z?_3gZa3@CIktRxx5 zl1m_jVNZ_%Keun|@o794M8xY7I6)ri^n_GG7kr**F7IIOfh9J$8y;3ChliFblTQ;y z;J!dW;C%Z;YG?Pt4p+g-zUGS0!yV!v#QK}zLz@E^AXBwFXn`G@4@Z)9w&+`(?$5SX z({4tH(vGo4*7Wy4reDxV<9Ry-WeTUX{Hr(}qQeoVVXAC6ker?6s)#2Y6594(13YAL<)99Q3BOwGmKIoZn>$kHe4sw8ai z&v4>fQ}35mWfa+k^Jwx$7=&E(b8c+^bYuR(vh}G>w;ocb10?tgm%Jy&QfJe_)zUWc zfMf}Tjqk0kcLEMDsV8W(O;L>^2ULYqkceqo{MItH`Rs)AJkzkXEXnfn4YA6?n>+du z2sch@?8y!#IR%&vc2z*=?#fERsZ!k9!7tMdh^ya_my`)E=B|luet|* z1vEWG$)5(AzD_$^rwiR^-qL`R{-^$u>eI69M1{rj^-G7#)NN9s5^PZ8`Xh*v zX|VxWF!Ek+z~0tGc6q2lZicU*-M%liyG_XGOWVvS++ypLfSpL)n9zU)^b)`R`Hwhk zxhZ+GpLFI!H(%!T-rT_6eGRUhKrg+PAG&kK=E zEjHZUsnNQiKYJ{JdkggXqx?f@BWk`K`HfIxxy8z9a#!+{qEBDXu!%9erS#Tzz(XMI zZ`JacjO&)73hpEtUXsVI_oZ$|fp@3fYItQ&&JJ335)2<$3l{xxNy+SuQEUU+L%bgD zjHt)W*`jh|`2z%}iG8J2QQ^4EID27!XhmF<1uvSCJ+m{ub z#p(*F{BvdM)F%>1fJ3)-W zpbX;c^%2n+O6~@V9+s9anHZHEbuO{bEG#^xv+OYXIlF-@asgq(xN(3i!Fl%wv8HrU zO~gF@{BB)1mm`qL(E*b?un>)=H#}OsN1$}g8@%o~l{uJSZ6s)J4op9ruSH<6ULoCg z0vNxtS4`~M5b!M`4!N8fz44FV?Ab!Y9zU5 zJ6LYFr%M?fd|{=PC*Xgl;W$U(hW#-xP!gH$Z1T%($kpn%6`Ae;p22|aF=p423o9$p z>-_JwaF{UaEpOG>pe;k)R-ZhjKA))9XDmya?s-1aICIlZ~quD+x_&&chdo#F0Z{`qYe?21N(x8|0v68#&)-`*1j(77sYb05{gXZ$F29d zY^@#N07hUvEM~8Ri&G)5;Z+_SSdZgvO*y%&j&6^!0!5P}yVKm>cF*p?SG*{y4NyDQ z!i(Z+s}#KZ+;3?u6LX~`PYyW$Vulh}!fLf2Ah4_k?)k6jo7EOy-s#M}(NSj@v&k`( zFcsDM7wB^R4CPX^&eH4J2;!V0@Qrl6dxw_UjkSUi1^tx;dGvhu21PvE_bJ!5dB6S{!A)@1hZ8YZfSw~sn0&U_#(%#1lZE~pA7`#Yf}!cI9RGEN zgu$kaK(zmFp4z*u`vw#m{UW~wKCm+b-MRM8zz0YD#_G9U)-DiCrTE+$mM-?sJ1E8n zM(#at|J@;1SFPj6{Y5-%6(Sf(Md5#A)C{a|2R0a>d;ZMM155^}kQi~**lN+y0ApbBmvedEi=yP{WmXCl{1`0VlL1xC@gYJEl)^WZW)$G}l~ zx>qfuFl-DFS~fMD7J# zJpvZehs$SXBB!{6eyna{CSDd$JqGd}0GvNUXk%Te9RvBc7G7}PlOpz&pK*&Urdt$$Leeb%z-XHh7KfY_$$~skbs!rA3``ORg zd!KXboTKg5?{<9$LC{va(sIq(a3`wl2 zAuey8hRpMfO=3y7XSEG-I{mc&@Tq-lWpm=;$pZ(jMt$csyZ+?&l-w~K=e6zk?lv}i zi_JYK`}*j&KEHVH^7Oz%-{8M?%AR;~@t2oRZ?E69?i2LgrmR2yxR=xJr=MMyXL&+< zxNiH2G;{tmKhbRZmsLvefMDKCJ-1z#UgOeL+ONAo7JL`9qXbWplO90Uw$M7zr2M`q zMS6Pfh|KrW_B(AzdZX2wbym{TpB_USrS1A3AsNs-@sZslJ>7Bk+r!@!_6;Q89OJ)( z42Jab6TH|nWXq~vt}SZ(&vbDEfw>}@zDdV^%1qWbOj5Hxst!T(hJcJ-dMA{Io(QXD z@$zk(SGTr{Ea`#k$`{;n>bq@JL_@_Vtsp4P5OCWNZb4sGyCFTrPXDjDqualSQhuul z=$}=T>sObB#tN@d<+U#~T1?a;quO=0X(XBpP`f?&TS?2a<&vR=R6X~dO%>w{#1_qJ zahtBekt>~}8rM|q(T6)4>z1zx#49tsQJoKbdnlCSlhyMdj7g??PKVteX~py3-7=jk zR~cETj`&QgLw=?;Q01W%rQ%8{4R4V*rXmvA{0|#1eXe_$;%?hq5$d&x_v^`K|Bgrg ztMjG}RCv$xTl|$N^wa}qNA(`J0CGWgE+aYVbNS{2qZk-s)Q5^ON}@Q*jlr?PgRl|l z*sc4Uq2}b7CY)&I+Q@;!^5T5gaEXJv=eEYB6hs(zHKKlZ+)R%@e<&W?)>GJ#wql0O zH`l4g3S}XmxE~;y6j4th!-?X*&O=f)f>4oZ(C(rHt_xY?cyE2*28-oSq5Z{o2jSq) zU{wlET)uocODi-qG_`CFf`(9a>y)9PaL)ws^T}w-t~G4a(<{QQTs5pNG2-k1YHig) zSIm^ePZ^I&mea+Y!kOHd-?IEBt0gW=*E|`2?afEJI~O^&1AS&`^%m>gQPS%+bd~sK z`OUogpqQdyQENh=CAdF2h9e8q=wneid^o9K;*}w zb+BgjytD7<5o0hF%Tb?f(34aJ)K6&e?0cA!XH2^fkD5?4Ffj1Cre^r)ZNjmKNgK(Pmba7_{90f%wq6@;=HJ+od;`UkBA*G7KeI^ojR!D z-5sy|JJ?;I#S}Rp-x!QGEi!i>6OS?IOQx_4)I*lSmMxRFbuG+!2_rnh@H9^zdP#~> zu!4L@czyv2)}Z6_z88gvaBA+_9`W`!6rN@-qokMo(`Kd7d@hm8y?N|}XUwPlrlJ*t zXu%9T06;#K5fm8Mexv$u+ut`3uJ1@4dZdG8F1WU5nIe4yc$ixWqCNUe=+gMj4TO9o z(=^9$?sw|aTNa$M63A~TBi@rLSv9=T0zC8Lu`h6Nx0(c-u*M~U5+~jW=Rysc?o^3i zsq&WkzG9iUvQD5OuMzc%V}05phH08weQ8M^glw|VJZGcHxseM?;&FK;h4^*Ufp@ol zjP6$=-2}Qu9s026qZ^x9>Wx^RtTnE}THq-A z5&bJR4%4UgKRs9*Go=i7~G0wcE?xl>9zC z@J&k$fKon7W3AKv>&zC0l$2y(MWe52LTeQncz53Y&2Y~nJVQy#Ty(1f3G3`=YHD{t zc5Gnj;P4I^oiL8o-9me`zW^xaV;$o{XEFzkYj>x!k@2h5O;;qhU~wjGz)8Fo8zJZ){PkqG2tqSbP@)n$Sb8Ght57|z z1O8-lE-^!`Ao|6=vCirMzk%}28%RY?SeHohn=Y=**#i@ljF9AdrlM=&aRPlwl=y`j zy}@;(Hs;EBKdf$W^xP$Uj4+-c5{)DfL$PP`LJ|(>L(O8T*sJ!i1}Y+Y9d-B&i7F)w z>T9*pWxe4aWDrHW&~o8Po8|%k9_SX0rQXpkn1)4;zcb^Fje8_672QnphVAZ0+!u{<o{`{Y0&ZwH3MgZH<#JwB?xF2A40Q^jaQ*Wv& zDsn86E~`@x{1w{tR|J4^sxW!p7u)u;4oSE5tZBd^d&2x^?CgWv69r47bTZKL^P0l_ z(S+|F|ES}*+l&nGvL%gFMoM&$QV=pR8A|E7BqQYtM{~V~#6{bMzsykr79Mc5K7fk> z_{+PNHa*alad&r*rb$G_w=YIj!^!Ra-o${#HK{^6a6>D9(17=aM9_z;8Ivo$DmI59KN)z(_A71BTL=YA2*%I`9V&^l;r90S#AI4cst_yu ztZ=cjrlt$K9+2z)ujE=x(VpiNOPHO{-|9$FDpf(zSsL5wHXw^|&*KfcQW9EI#o=lj zjfQu$8d;k7EeE|TaW0-!{}B&t z5ew2MnC<(qCT8fQu$i%`X3~qY;aAJ7#^ZCAF7c)jv{nMu6^C1DE4O5SNEdXWH|WWS zS+2@U`AlBV*PQ!S<4jh*S8l?GpLO6;36Cx(>9za6kvcn2R9Jp6n1@pp6tQnZv>Dxe2GF|U=AG2sn$S=wUn+60UwgaxR!l=f zcyRFCz4sjq*y^jo4t)f(NT?DfHOyDWFY$>z}XF9`>0JTr@ed&;${v$QIZ5v)3MzIs*%uJ)jG zCLafr(da}(useO?docFI4?bX`==(I9W2=!Auo&yS+;+Agv2AQlfc12(LV5T8GJ|QH zYRAXUF>!Gu)X8Xps<#{Pm5M`ml)~MAKvWhM`ZowERYD4dA|=)w7{Y5OKqa^%$0YDd z0AR@K?pFc@5S9GzB-cqsnx%hBdjFdf{n&mGpQlk?q_w1%{Y!aNF>&YbSGaTc0QUS=ZRg_&hGDsBo|ry*SpFc_zu)`uJe% za~ugr@s(-5_i~GD$`wOn!k;L}3@5n1H83o!WK|hD;wK&ESkMLEQyJwraA95(9FSj} zzuT`a2c`W1ZM3?-Yu5H65=<#a#q-dU3m|o{DJ_e}xILnV zYQd^O4?bWc08;*^)IA~ zN#V4;H;ei2xPJ4UnMKTdnrT;0D37{WR==WXLd{+}knA}vnR_A!R4b)iuy$jnc1ps0 zIB_?B=~FsPINq0`(%_mQFZ>SJu)$q_-!HlG%nb^pfDt$*a}5lxfnRd0F5 z%v2@+`Of84lRgfjyo*xj>fhyx8)7yU{YcNY4nQF@GdBMtETE_zcF=P_Sp7T!U$XyZ z(hM>c%_Cl-+3%b(My;+cxX5MXFWpbKlzeE4+NPW#ec9yRJrE?kw~E1y92WPJqJlCW zll*Y+Nm_xA6GOnU76+GixhiR)k;L@@8E;a$B6vyrcUEWm=v78iH3|l6wuY632DNWr zMsFEz;se>Im$;6Vmo{A*q2_9a@d|}9ziR0M&D;l%Lc%{UVjFxKc!(i1kub8C`N=q= zlt)pk@okxYsh5Pi;x4Jjm-9aqBT&G3O3#1@Zb%-0!c1*5@qAUNR1LHKBv|he{!qGb z%tmo!pj#*e{fTYltx)qF01aqfZlJrVbUc>!CR_j7biCd$zqQ*;ug2j}L*6D33)_7i zvb6oafoS4r*Qm>W8*O1DzEj_&+L+ zuEU1;ydIo*qMM1%%%51|CC%)lFkCKl1a|j85&W*!wqKEI5b>wod6W zyPl>lYfF6oR-iU%tyg(^S4L^#;wj((7X!1N&k~A;tuvI_(Q?q@ zcz^ljrYlb<)==}Pk+^wNzq-t7y*Q&Leyu6Hmp*J!TY;}Lw52X~0H%S(Fw7>t-0nvK ze3@PmXYGaQT=BP_9z_> zf#Q+^X6;8NZhPtarIhPI08Qn|C_b@pvdM z9~5eye#N`5wuj*1Kgg4+VDslsVJI<<)Z{P-D+@nA8qkCe;k=KO3(tzAEjdKldoVv~U&8YSw z9s+cF2<(@ra)4g{VH`hH75i0nZ`GJ8EQvJpj{BVdy!Tf`+vk$dFb81D?2!6*1eB+H zy+-^VPwr?=cLTAZ*Ec%J_gVWHuC#Pli2(tkVb-$%`zY!bC>orDq!g2-m@MSJg$+yOPSpAWIb6R-0GttGI@KlkQ-pJx1(lBCo6I;)`=EU6n7euk@g=hae) zP7iPtMb;o7!&B-Bal0~MKi_T5H;F@84Pk-64__;tt1LIfNRD0BYP@qVC;JW_W{VopOl_SHM`|t5O)Z*qcLvZ76+ah^ z;OrS{8l6^yx7E4{WfRZ$)3_rF5;HJbjfvHrEe-2%4RKkl@6^4x|q6K zqA8c}CYN!kU__X7rvGcw<>r%EU*>txBC?_gfS3w4Qko7oOIEl~9vZI54H>_$fuZthB*AG-tDA-9!>1X6-sL#&WIZ81ZvY zpIX9r(XjUtdj*F&psFrzJ?3EY8zJCJGLLesAi@SjyN|R~IF!xKBWvs}E0wC$(7q}x zrO6aTJIdb!jQk?!scZ1DDd0$QsEhIEKi)tlA8Yo>sTQ;gG?KJRh6T2_#C|?wHu)s{ z6al}M(8qg@5HZQbWSb2M0|D=TX}#E;+_n;<$7d+Tq}Oi>9|{WYDteLQ_XN3cr{G5j z@(Kb0#>@9s^diYXNHqFhsJBjFa%-dC$+t%C!CsS)-l~v0St~ap5yV?hpX=HLK}f8$ z7LppYOTqD!Q89N0sf*`U)FaB0w4WD!PFcZu=TN5YQoZf6#CP2_LSGbDBpM3F`uZ{* z=%v_;AuP`&tjba5Ma*i6YQC1rm*jv&kvKyKf4pUHNlCaJ>w`wPX|t7gf_gss`qnb; zsyqaxpg}qbg`9Usr?sOVR7wPxmBW(EE#*L+XxgI!TT+cbHg|hq9Oq_FEV*z7IEBh3 z$pwyX6)4;&221w>B)E#<19T2!#*KaVP{Z9_=;zu^PNE~jL&QN_8k1+J*|S)xP&7BH z+av7rRQa;z0Q@U0#xgPVRMTvp1HCuqYS1Z-ysg}1ZIK$ukrf_BPLs!#@{Q%L&RFxCMnGJv3F`#1ZWA*&l{zT$%=6;6Xr5^XtJnZbCoufc3B zsZF4fZ(QpnnzTIQ;OT30P<(R*=l+QD=|_F`qI2)!Z1e_hNuV4jN+-z8yE97`+TWL4)CwBg;JWjzq5j#{GEpFa->(2 z+JLJUP+C%Z=}RZ;nb`dMdZ^$UTR8|?P5CDkYF7Hq7bz7sWUWiV;A4%x;noR*s2-Sp z&}@ABpXd7vw)vcPli6r>3#41OL2~g@=WE{ebMx1z;oBa7|8x&PzciEiiT0POL|=EO zDA*|Ms*|MIs-{&&d1p{?kp8PG!<)oa1p`iBN16FHXvf#3S+TaPRb-2X$y17^mL0Q^ z{YUz|XEyq-<0<35LSpwgGDT}BXGIiDfU*feUH*_Y+Gj!Y4Qt~Ig;M30JaP~8DQwc>=#;R(~VI7p# zEVrz8R@Bm;bBtI*c?NAk-pmZ|{*(DIez8b$ap629jdr0e*QXRZ$-g*gmYQ#9wwQQQ zwB3#P1tu*wNnz6b{ZQ>w&1b4+!JB;$hK7BGaBE~uE0nSigrG;(3FJ3Wmlej=zrd9_ zph%1#Wt-0}8UyzGzqQ}ypX!bt5b6BD0e0 zN;ZwaX&`GmBc>UyQ+IJ8ZRO7HX_GvV=j(z9z(-+i;1^By%~oS=yLuyS%fvWq3&co$ z`OG}orkLOoUw;yi?(1U%MZVUOYpg@Hs3hVwhQO@;mGLvxAa-TR zR{rGAzCC6^9YKUv*U-*9_v7q|*81KNbmQuxAaVA=(H#A%oRBO771$-F%$AOb_YgO;bFgP6ZP=*&O~GYnq!r7upSnbE`vW@Su>avPkEvdZ$~S2xAeFOr`Z z8}m~qY~g)vxodMygN#h#YwcySxoxThq~$&) zGxE_vRy)?KTALGV7%zEU)Hk4|loTAAd&(U34(SuYQu9-s#JNDP%P&U?H zlG|yoD_G>DOax=;Vv<|`Cv3)6xzyJBIW#T)eaERSEhMd+<5d=1ujGjLrb1!1lX$t> zZOt#7xK=DbIrJ8h7{9p8&)jcrI-f#$bCnR^o$4N39bv~kOpGRHEN8bSM70f3-tY+V zBq?TqEyB?Z-h#Dlz}bl+ddGRq;OM?LvHYcH<_(~#1Ob|AR#&ksG787mPd!y+B{=wM zYj~iY5}zHCtUT+!ja=|t%g0l>yq3Pqp_P@f`>}?0K;Q7=0=B!nIjYm0*Y;@95ML6I z3HPq93}1{It57ovJE?xDJ`$$^@2V%NwS3rAQA1XRY{QpBHEsK}hL_3PpqG|WTt6@q z17F|uP%GZghBby?u0$G<;!2GrD^DZcR&r98X`8(sx$eHe4}Uq4Ydbl!x}c%{O0yb{ z4~R*M5*rUp4|jXER_Ch>n(fma2$M89nUA35a<55R-_p#xAD^#rt4kFPl&cPfsY*jY zDeIuALSB(#f;=INzdd-^ASr(8kNUG1>b>nXhVl4ny*t=)4}W9P3juI4;@ae=L+CDx zm#=kj4RJOWD0kr)o^D=m!AcT+S>xnXxDoM|)x_aP*RM0~T$P)dEuq2%=Q;80VKG(m z?$I($ILAEbJ%W`O#dGy^M`;tgk-RoDeuy{FjQ{!xYo}Ge_S-b?(2Y=YLG-30zzA_s z4&KBlTidMX;ZL+X3TC>xEayJFxZmy;PGRr4H>iG8-!eYLSA2z89S?Aw$c1|bmg7ol z<;UV#yR$Xc60I!~7pegTs5)EzrA;TolLP{J_UNEt7=pHO~a$&T4qS7%y0>YkXB5S=e}Bjb`R1Q|#9Vr!ZyLzg_Aq?6ulWn|t2`X-;Tl&gDv zLxT~S%kc2vZcoyeZh1+h!zkkKB05Hux$xnseBV?FoExGDK-J(Rt&7Q#y!z3AUXaXw ztCoz)t|i+6hA&GwyZvT}o5%<`J+#)(S#W!K)p}bd2JgqTSIw$drwTR(QZ&0~>y)O7sG{o(ue-B;UFI}+IZ5z2DC<|!95AK&0=@-9pV zgTR=xoIRT*l|@_q61q;q{77_IAepLxbINM`tjMWbq#XkKqe+$`fG8BrUF`nomMIC9 zavvvoVKC9!H;p5Qv^Q$tlmJtC$x2rRDOn?wnH)^n3&l}WsSyHxC{*`4~c~s7>Qh1&* zR@esU98I#Q-p|4gT%dghL8=_^V91*JdSX{JC33{_Bgl21y1Fk&x8j1Mf2$jV^Z#J0 z?vZst!C3{P;-E~Km*g)Xb_#Wr+?1J0@ z{ggF#5Ayl*Y!X>w2-$yi&Sc2v7c+X?QHzi;M{_x7`h@i9 zk00JdO2luM7%X~8G4!%kBoHB~O9?sD7Cf#)|)=M8j47B#b zRej+`n1r1W@?Cd`QGvyXGJ#c|S<@L(a_V0h4{q(-GgYJd%o*5Y(NbX$M_xgT6?Y@7% zVyL@wo5(f{!*=2>{cMb3o8Uh;eE5Dde4!AA%;1Xv-dOi1EWbl+0>chsxS!9N+)bJu z3V6P+hpYOTQmk+N!zPpLnzN@G5?Ev z)2|vtg757YJ=y#HK9jWH^a>afEk^{m7wg}-QO%h8TA8AVckT%2DA^EtM)BHdpKe{n zjqv8!kH+^_<Z zU>o4;*q0jRk=`j>6yz1Ww1wUNzMg(y!!C*F z>Qj$1d@cpXSlrT&N6F7*?<@qn`i;k%Z2~rk?@%CGv zQ-=*%NAZFJ(P@)iQpQ@-FI7cE$D`-(#kw>iXeU5tTQQNgD5W57VI% zWaF&ma`G$P%2vLNyY|1CxLGhZwPXKnUAr)Dgk-b&ly7rXSoO6CZ9(moD=CDf3dv?Y z>AYv8HAk{{wILS%Dkqx(Eqk)DO>%E~FWKH!zT(^a#o3CVL{xIuZc?ZDmsk4>P3)^p zk0q_$?tB~QobjiJu9PWBCvoy+rqesOn&{02``pJ!p{tg1m2+PUW=dL4O4D1UiVj_oNd72Jg7c?!WCqbfgiM<6W>7&Q!h;(WBQ-5tgy; z=I4FavCr07!E_;a56*JV)Yvm^2L->$#Wd!mvS83ujfwpmCxtIvm#zJoB2_sJHBaC| zq!f?I<`+jhrpU|14{0!}6F2T!3A`97Rtgs|u-s|m`{jMbw|9>eL)TW`tM7xIzPP^A z$-%}`9+i_%L{zcnV!sZL;5moNgc1_KO_5 z_sHzRg$tIPKcqQhO9JKV|H6OAcyYPYB@~~rz2>rI>M%If-)`EEt(dtDrd^fe`Z1F} zaC+8UJ_WQaX|c`Xlxls`FP~x8D%C9NX6;w6Ui%!NqsN-cbonWh*|k`+z3cY$5vMBY zMsm_06C;Wx#cb6(C;D#J?<`*GcaQ7$u3?cEtELwun@cF#*1gMParQWJXdHbcO7|E} zW%yRTSn%L!*S=yWOLDxT%Xoq6P?8iWi$-(v-XHlXkL)=C`}FvPyZbt!it&t+_U+K!8;~$8Z`!n zng!0^Td-z1S(8-+sHeKPL!UWg9Gx(^p8Afq`=%XwCq>e~NG(%N|HQ3v$vbziz-!^T zW}qH%G}=fTM@gyCql_kwWl51*BzSX~NxZ2ppKmX>NZ}fyNKZBju2`nr{n~6y-ZIM% zSY=fm4xX+EUR@?D1PrkY)J!ieUX+a_mS%j_`o z_Gy(3qx|`B?XXKSYe30s@C**hu3M~c4lD=bDjzeRlTR+6&THS=gZ2Hh!houyUgJuyQ#ce|9*8*}lds8|bRxsQBg2 z$69-}%a#vXP4UR=_W1O;du^_(ixU;RWxo7KV-y8yCswjzvW2Joj%?}tAPmE3W+?*D z1#^bDofp-X|9D!l(!Fn@vY}O3qndd&TfSdqk5RRTqWP4CLqN}p&^7LoYy7AAmCLT& zkz#^;_1sO=W2P$_?SxgmKGqy`=!gp!3z**8M<~}S;dlj)-ybn*`|I~T(UJyrFBo5pTs_V$sP%c7&!`vFX^T)@d)duv zHd>}!GtxXh*5`Wo9YI}5#)Jh2r{rpqU z2JFgr8_q&MV$8;=w0O&NC52dSPxIZYjZ5k|NAwSU9vt4acH#S#oXc)uCK)nhU1udP zQvA+@*d-nQ+V)3E-z71R9>jMQIW(@(_*`Z8{0isUDn?Gs@4>Sbx{+E-&)>N9oJ`gT zXz`=kChn89lEhtny#MK+_qMo{w5!Onbe5ivFu4(`%@>61l3G`76C9$nj^$aG+euPS z%M0>2FE1ym$l{9o-Ezi@TjiRq-1B0}JO{>MWfCJ)`U|_4f7!xI4xka_qos@tiN4=H z9HQC<`1M-gBi?w^vCj*d4u&x87rMKmTuSm2kK!60^V}}b1jF85=qqYZRBde#^)_)MbIC9J9HS*h@9&9} z-V{;)YfZEcZ{DbHURS5%Px6^JKX&xIujR}}!v<&1Pt6_KmGd!RY42}|-bI0ZiaVnn z6Z^c*g>q)f6~CrN>CGtjT4 zr*@_$h;_NZP7A-X`=QgoOqoGfhJD^ykKXAVx?t$)M5c0wYOq#JObm{w7|NX5A+~4| zslM8jTVfd3$*Y=OHRsV-sxl^ArebgniLlxiGUZ9FC1i0|r@WtSdZy^o(e~uf`F-v+ z7^bhj-d%iWad{;Z*~<4`HV)gUX4+}W?TM^DVwrlreIt!~rboy-`uCiqXD9MPO&kKo za(}pez*``c#}+D{$`6+D{oX=|W*d;)Nl*0VSvN$8n!5>x1YPHGXfFCQEL95~iZ;h4 zQf>9EjlU&13EyEEIQsFS4~@J0Y*lmdLw!0WLWIEDQCH-kW2wXnUCs%T2$_3X&0YFn z9Q@aBQTPE5ZwUYytl~~R@vbynwES{Z(?n+S)qYylGCOdllq!U?h0h`FEwCA@jS*3G zeWo8aq$#x+s>~N&N-icC)s`BHEYCdOs>Lv1?bSHyU27gXr*XuX+fblzV`e+?kIk-1 ztFy&JF(dZ4_ltqFNzQ`4ybINKxk)@=O|ey`r0rErVa1ux;iU0N9@Y7;-*%%nEtxo$ z=n`ienzZ|fb_bc~7vOr~3#uPeGfOqyr8C(-ZYy)`I%e%z>eScrr~k}AjCMiMc!KcS z*Uys8S9Qe`#r;2N1Dw)Z8VVq7$`9x=P{mQ(tH#mLS(zUxaVpO%=`z&Fuw*({IMgMl zTdDh5X=pIkMwD)SI4HHOd7_4*C6DtNi3n?oQ03jwPQ|d0U<=exrN!^Itk~Rb*R7ad zbY}Ci3wl1;vu5X%Y8a~{7 zYm6Dy&io;vFMX}@HAzGM1Bwofk+y%Cn{~n5ic9oby_TTrof1-^Hw(qolpHV|Bdq8w zi9@4-AYXAD=fm4n)0J!KM;tBI=Qu47Ew!D~(K&-Fo-S@3Um1y#P7Ws3ET1GCfR#XS zDe&vO)V|7bQLhz3FSHfOX4qk$0Z)D#iN6?UyaH9rS!0bScD}tcBH4`k_>mwnyAjQu zqAG2Fc$(RK3j(a`yxuuon||wIQe48GlyD+ij*{IHpnKY+8oe)!>RkLPt}t<5Ljhm9 zq`X848mFZ#={oC8Nl@^)k>VO@28nySUR`*UTQP1f|3jeboPM9fa?Jv?4Vib+iB+s# zd--LZ+eZHVu8qa?jsDZ0PNNMdAN0XbsPwa__BzYM0YJZqkHe%fKR}p% znOnQ+mOc`tnXTKQwVY&P9_H6!q;0u!rNnQlw_?hEv=)G2DE~CzMcU2L*HPoV za1l^M2IlCVdZe(o#ThlSDXBnTKsNXt9-9J>9oT|yFhkKZsFH$t^;K=)FtlMqap>)tR8de z?YrnoPA$*SaejP2OWn3IBva;%^^d>q5|4H5%7``F)G-0ah7!i-c);H!w91oZdV1`0 zuZwAi+*M(pqh3zYR5LX-WzVH93%R0mzL5eFP%Y~~nEOzY&zC1QA(IMftgnaQ!`ilJ zRO_f4$Pa7pF$_G@W6nDh$Q*)my~p0Dq}-@uMOh9fqGZtHV&vp}Z=rA+4s#oR4I$SD zLUv8t<*T2JS(UZTH%e69lE1xcr-)KhCC$3rj39mTWXtddP5B5BWj%UF^1gF^`0{=? zd-5L)Qu$CySP0wa$&K^0<|Pe}II4ZHmY~++?PEqMNs?-7zt!)vCcq^tlw%t)nqIp| z3^~o=L!q3ohO>>F7NO7Awq|_b7bfquN#xx_aLqE7?qecV(o)qao1_;9A+{iRcj?K8oPuN_-y*9Dv)bF^Opb_%%xylLCJ$5(|lh`H7 zYnx+=3HeX^2n)?p!V053R#X#9%Xmgu*ocXlS??=VyokE5g;({wVBkV?=0b0xcSdC; zY^!hcZmnIET^b(tv@rp!TNI`W{QB{^b@;ypy6?i#TKw0Gw&fI`b4pOQKNcaPlFu;4EZ(6Z z6CtXM8@XuE`TEYtV~OR!49asmnv+1?%^SksVGnw$1Gj&Xz?eO+Z;U8;55KBy#lqhM z*FAek*|k#%nsYWo0khCk^7wSzAVDb){&2nM2W-QYOYo{$jDKJjEx_f%U;P99m8t0g z{Pwd$|LuztVR8(P$EebxTNN0>>q#F}BKc^r}gCiv}&<0h?uWB`@M+zD|q6 zz;yht(s?m0V^*2PV}Ia?6Qq!&2?t%57L5Xz!Sm%#@4A+q#-BCgwTvDO?P~rblVzVv z-Jrh>z3gFugV+N}EbUuH6#GoQ(I>TQ!(uiL_D)k}u7tn5OWJmEGGF2R@;X%UUspo> zDF(t!4$Wy*>WKTacjb-pHw+HNM9H)4(&WRVG1w2`hu14`!`VMP#?!V2k{eax*H*VH zFO{mVsk#P!#%0|a^!>9#>rP0++Qh36pOH<^Q*dDlOOEmZ?D8j?lnkE_lqj&{F2S-d=V61OxTlBshV1_%Gy8@ zg+_e{W|_qwkGTVTh56saod!hmi~Y@bBXvsb*<+79k}^a+<70R$zS;e`CS}}co#poX zQVdJS5ezF_so1nX{3oC7r!FXYc0Uv@)6I08eAZdk5-T0LyF2gGjiSeDV>RUqyhGNG z4Ri;x2%hb4$tt57e|$ZEQ+Us0H1y@;hhagn*ngz1CR^>}nJRfD?24nAPR%j$3=*Gl zvm-7_=fy7(_?Bg!tFHwWmkQ!~iU4g*J_5bv`iWEU>I$>8@>oxI!eWK%o8IMpoKFU| z<(5|B{uleojJzt96ptC$Ogn7E3g^BHhbj0=k*aNM+uCHatm%wdvkym{84*ESFUpEB z{TX3WR69L@&lVMxz`{34p>=uJo}_u2L<_+8mGx?MtX_0~{n{%fb_+k-W56X~a3!J_ z!|MJCg^9_0IkrZB*_L#swmbjUpk#X$gB3w7f>MYpl_D>NWJIie7A@F_g;)OTo3)p3 zSyoj}MHzoA{HSWDJLc1x*&LJ@=b5-NoywdR#OjFWK(;HqpSae~beb6-^3N)_N(dOJ z$kk%E3r*#LuIGjnT=)is>C$5V)EH`hxs^y>Z+fnl)2Atx*>(&oJ+l4@4ETGr%&j*xNg^6XQxVBMw~kF1;+ux?x#$u4Lce?|jYl zkv1H{)a+hQ#W7}4`qJV@iHt9L?%ixn_gzVEKEU_0&jot2?rJE&bvkT<>-b0hm+a)0 z{%ga5sl#sZ3x;g#Fwwl;ieWba?1lgO=DlrjFEH;tz+bUtnYFGowlsQm2G$4VY{J6( z|6bIDeDVd$;I+Cf3J#t3y{jXIqmMbi3zLu===H7lqTLmTD~dpcvy}>eb}jUnDp0wc zm@qHMOYNWsx_2FOB2P{-gL%ZuiB@jrrPfCy$R$^kDI2jmReiM7Ki-;8B|R(EwGZe} zRp+%e?@=6IrpJ0(`%~|cKZ<}3dI~LCdR;L`BF@C*S%25X>bN3)z?iBE?^B{YW%2Y8 zy&2TgZZeLLi{=F|i`(npf41H~$1z@9@c4jACEr_NEH4hKINCn&n1k1u0*?V7&VlF_ zr&#O1tN0}7J-3_*LIT*M{)N1O?tra@O$M-*zs~fj#jB^k$tyNZ%w43P%W@m?hvUbp z4_FwMuXJw6Ow}I{kr`0W8D6pyQL=U}dc2k8+Wl>J>9Bx8$pOwQB-jf>Mjw>HEo;?V|b&fCQAmL1Zw*V>~#|mzgeu zK#|?!BGh+g4=GrNAzpkTgK#D{WQ`kXP$C;(`*+VkZfUH7Cxz!Al2_r?S2jU<+{V%M zr$~i;i;j<$lAWt?xxAv8${{`dqU%i(9?_8=Ft6#`Wyy;ZlAo&(z+Owh2snEvaPz?n zs)X)`f&Ay<3an&Rx9J?KA_pr8KmUlhUqh1P_cV-^1oIO9 zLC5@$K0Z2dz2U79^P{(5%l*vR zFY|qUGjKLd_mtCT4*H?f=yf7*p4(nO5PebCFsdun)>nvgPu$ujakqlQt zH>0>EPkuWeut(iCz(~#8&erB->4uqPI*8^CA8ij^8IdWc-O2JO z{dzNJcdVls8kSIB+i=+T&D{aRP51fQNn7~@*cyV2qPFO@GV+Z^H~dE}a32Vn;@zmsJ4^PIK(uaHv8CSOcw0iVg|H3fZdJ zO7~+12`03oJ$gB2d3xl{yTmMLW6;vB@B+m!i<2|uw9ttUFVANfCMSRrdOAvL$%5$k zx$YM={u&bxf;lzdcbIKI160KH)9>w4e?7ggZMY^l)0VtBh zq`!YGtO(Fn)By|sx}YkKz4&35*5ZdwI*6d-Ljg3|0X|Up(ZBW>wXIGSkU+1@xRz0xUJ1nq@NDt%l;??~oZKASf_6ynEJ{Lh3 zo_*70=-QKQD);vDKLhPXlFE0`fUZyUm%8_tc_tzapeA@Zg+4=BriF!pq(jRsY7lXy z=G&?a-yIPPo-RU?(Z1Dw_W?fWr_w!SP=?!BtJbA%F(Z*0`5v@oP!T(8L9BI2!4K!# zR{2hJ9^E2#`z%-uzZWEvGgE?2(v$Fpy*BuE8S>hK6W6PjJ?>G%>UE{n64V1`lkgNR z)vgSKMEddu&{%W!t4*fHvuwY^WZYWIy{^%Y7VZxj(%+wPIi3g$5!(+g-F1~u*Y8O- zYb_5(*YDDh*&}6`d#9tNrDc&p)8R~)7`YDA{j#xSc{teR-TkfjJcpdTvX;E^;RN`W z&a%qKcmCWYH2QA4e9}}!Fl#c#o;zvItl>o)s-K9n@xeuCCwf)7Zr-u~O4;gsHM67j zy^y^o6>nx{HV&K82BK~CS*l$e4~d?Y0gMz$*Wv2GUw4YeZr&;4IN6qrq!tI0noScjp6~% z`W+e`dAlb34ls8xDgU#)Ch`^xJJPwn@Dh^%{7{^`|8pe!!03H@6Ff;Vr!* zgi*8HnW`IQeCZ8ub&B3}B0d@!Oe*UGR zgC-s@_*Ble68WRezic^Rkx8w~~O_^JX zms9P6)9uNjIOA=v_c=cH8mdf(pLUX7={Pg;SUvCfRCm_310i#NnwXnQ;dbe)&4GEw zzf>Et`RngeDbFQ^wSs(?hJzW8MU+p1QXi#XuDAT{HBN3{_9qLov zY*-yKTDzN%WPO&00;F&$f#dP$0NvZXGy4t!Ei)J{R{Jpjsb0@~d@VG((Jv1rz$9)b zN^X$|xeaCdBt-a`SNV{?at9e&z!v9W-5Yv&I__#m)Bdjy880>Dc;r^wF0; zIYjM6Jy=%YAa>VSZ{R}ccbb={eP;ES7_Qg_r<@3ExI{c6pT17C>t_C&O z*4nuCLNgTe{ctXw-YNTS|8{pMdpY-j*+Br%KBG;Yo*%r)Tz}Y-${k{7PI*I}YLepP zHoaY{Tq0@xl~`KIfKG2jbbKi8-!$M&PS!mTD2X<4aiWU}4M!3uqT*pu_LI-b+!to` z5|s3_(uaZ)c1JMqz+~bboJK%LHZAk+U~k4O67Ft3h!vV74B!{dZ2fwSF;nvr@?SUO zZ2g;SmWLCe`IRiTRY+&K2Y+o!OVvCc>cN&DzTtkZUGcUdvd^#^U?Zu%p1Lg7r zUcChG$!Uj%HYRl%O%ofiLWI?QZ$YDc#^ZA0%woael!f8Y@l#aMDd!=7jnRE`yJ!sZq9K>xuxTUkbjiwpP&M@zUVs zS=*(HeX~PVaoRL^r+5`lt1K?8-6-@Y3aF~~L0&X#@0u8x>c>&7qFH@8>C9>Oyi&6z zn5M-p+dD$5ZrL_IPk;65)nee<%Di9Ytf<%9m($NJ&sh<3%lh5Y2f|#!s_v9~4e2>_ z1lfI_o(!7Vu3#R2qzz8S08tWl&1`IyG(J@kugqL#;+rF?2hxUy}--5gI zf^?!sF|JR|#l9%;{a*9#Nio2Hhmoor8w8WXCZEh(4FN0?$mcHO>vyV-zD|wQs}1R^ zm*B57qAQNlUcFT|95m$tO&Vnre5keoF=ma)L6ccHBKQg8S(zy<-#(^BOEPLXUhqmJ zBx0)$-ahZz2BSSzo9g%2Qr-94m7vhi@h&OxfCXf>9JqTuf=+2~M@!^c)^}{ctI89( z_L{tvk#vF%r;nmX8hGP+O=JW@3Uamsxp9td`T5k^gZLPEZ&q?wt|z|s#2FlFD<3b4 z#;~BD)-xM*c&GOqyWE6c6h^oDfM>bwW~fJqX%u&R_YOR9A92y)Sh{YSNc66aM{kQN z8%fImII?@`z>_*n9*eO)4od9bi0K!NS(k%)XR*UM>`py^9NQ)1-@u>A2w)7Shf=hb zZCsD`A&@3USp4WDz0su{wbJoKZ$p?`(UQ=b?L&D?QrthQ)Jnk>4j2w}F$04Pz(nLz zNjQ;~5d#hs*f+7j(Wd|<(Aj(weC z#*%4Cg?g^gBO)XRs7|^RqAR_f*|q4zIo)two*i-@>`#_A|9%N3NHjpysZW_nK6-{4 zGjEJJQ=y^G?4E+q7^t57h9!uAs1Z0z$&7g!?nU6&w4+$vVLAxvFMbGG{Pwgr&l-Xt zkA<)Kw~ikOo_-3Q?-T-RW_E#NN?`uTTD@s7Y7U0RUOEDEUr;zMh%7+ zK!eoMDaI6G-`vW{Y&BN*H9Tx*%saBA8k7hg0tQZbrM)Pe&;{fRS>2{W%jcA zg&gx08c)_`XJ;3m^6VHOxP&=>p7!r^iqnJ+t~OVZS{vR3NmARN_Rl3@5|2Lwcua-&nD6|qS_Q)83 z_0f=9(xGk%m`IVZy7*otl5Zw(z4<8iUHJQNJ?8f9(0&u3j1|E`I$r9+y*VMZ_}cyg ziT|Lc_URGb3>QkuI^6-NnNeKq^Ly4#Y-QY}CqMwZ;^^7`21f*+tTK1!oWXu&Hxm@f zvGwmex_9qhq}N|Dg0&4l`WM7ak|I=x*Y`uNYMKbNgY3a4@fI^ZikpuiPM|X6+qE`f z6n1h(RKf9*y8rZZ0OTi<)&2CM4rsag4gRxR0m?baw-Yx3(+j%M6Yeri2Q@6>C^R}< z#3{hoeYQ#l27ss!xO0_Pl(hh=_IZ@FcS4PM6usY90rbUfF{?e$T`_8%Q}o|TedA7* zgMMuNtDuB7H35Mu*|JZE*$e)R)F|!BH;e@L{N~M~9#8yd`kh*3%i9RcL*^qZ{fqG> zWzdGyVEKX&HQ8}M^9-zS^U0iCBKoWpP3Gk+K9yj^tm-J>9H7A{3+ydBs1t@*qzZap zXew2V+F%qavkJ4fSLHu?RR*D>?3`lg*6A=N`vU37inl-4Aqtg_r&Mh4YI<@gox5FQ z3$wD8-QFjInzRZngei%e>dhwcXNlJoHAW{6=h_puj;)=1_jkQojv> z)l&iAqv+b1wm98a@`mxNs8^GvOL7ctZCq7Y>DHAFZO-)K?2rZmjQWIQhDpy+U4V0} z9|YraYGl%?^{MWGcmT?>szbil4uq}EE4lZ|Kufqubc9*d22lWXy~ccjjhh`)BUD=4 zfQ4OA<}SpM+bZZOiTpLXj@snwRKywRJPxCvOROh@GTI!CAM&G)p@oKBC2ievEX};Y z4xcxntFwC53-)HTb)ZHbxU6I|4Db}_Rp~|~ZQ(PwuOl2T=F@a2aJr~budz3Sl7zfW zm=_AmgLorURMW7;!aAWLCvK~87rWe9TI~#VFd-Wsi>l@~EWZWX+0#zQA0D z+2;gflmY-VwoxKXQ(WD*2`IPC6vN$)Nj~nTPt=#MQ8KsnGlj5NG{B@|2pUA{@Kypt zb?_yp@TbbB5@|{8vr;;2T;Dt_l&5hksiva6Z@SiNR%mG%jHQqNHQBW*_9_*JlCSie ziiM8W?nm2a?eP>CqB<&mA~2&P?Dz7aPjA%!>I8ipwP6H#5SLCL0fnPB3D^T#iV3lM zTLcY36o^hUYwRhFm1M6mDT)9J-`x6m7^iUS!;5L)J?W;}x?*>J2EtHbbN9;w91(zs z93%h&;B*ziPR)3SwSswX-yaGDa1=O1i*3M=X%4g3=>U0lm2OIVe%gVE_@xn8vp$F< z0-BIzX}~#Cgtr)k%u(s2V!<7|pow&ptAgm;Pi1}1iPcbeS^4=LNPB@-REBOc974|Q zx1l430;vKBTWZ7Al&_x&j;48^Y!ihB7Z!&M`~rw3^uKY9#zh}<8gGk-Jg;kBnAvy| z|AHxijIK@yD%t=K>;ulLPXIa>N8`>02rIjF+cc(d$*c#-_bO~@Ypay0)u{!hTDe+I zdxg+@hLiq3ucqpPE{YX4fkp_&q}MKZrjx$c+~zb)G=1U@32jY@u(dm(3(=&s?8zLR zN|*&t;}u)^;@(Sq%YxQBrFlnjBC1|VoY^XQpl>a3UZPP-tuqam&PB^=c;?G}AOceL z4+Sa6vqv(+OZNo7sxLB-nQVn}wwr>*oi3>2v4tT+09zd0t}>A80RVMKU%Jq(%f$|L zJ4sjIkRnRAo=i?oj-06Bt(;)=>?Jv&VTNuPs#vbad-7vGc^Zr4;Ye)EXI7G^;-Q(4z6D4o$Vy`4J z<^CvgXKJ;3=c1nj;M0PB4$9c&k!;x|V3kugt!v{P`7pMA3ZU0PNbsovA}FDSa>jsA zWU=PAr>fBN+bKiuA{EJM&#&2XsJ*WesP9?9!dT&F&5b{R69Fv62)ZTn&n*&;kob`2 z>Jd*|%aBwlt}}{Ah}N4!h3#E*yNHoZQ?t zq#TY!>1c1L1Tu%hH}FQP5P^3+ zDJ_i$i3WA`^dLS=gV8^v^P6x|$tKxOp@e-GJ;|BuKKFtep!emDbD#f&Dz%;T_3#|$ zK+9?^8}Ld5IZOu$pr8s}-FDxNLugmyKguoxaZe1o`9xod%Pj0kHJn5C0F7r>nXF|$ z9612eY7!JmAhTT+4_8VDWzX7iy-C~i`)xR{R{L_M2Iceln+3wz`0b}KnN3KF0)+=Y zo;G5w{pH>yfbTA~{QBA#jqUoN!60g;MP3_|hbBnM1yw+eJsOSgzN#C;8gPs@=tRjZ zpk(QBS7a_5M2M<(pplgtseTfa17{?N!avG=Fx4h~hc9SJ;ELB4TIopP1=2}E=M+s1 zIJ0jNfdF3ProH+tH5cZ0#g|`2TT-OV6ScQzm(jY`;2bMMTA>-{#nqAE+z592GJ+{k zK2lQ2zQz|LL}Db?K5udIzL(d&tX$5v|FQIFGt#P{m=`B|q>o&D+ywG?0#G7nD0F>Z zSa86BOeaf*;F_Yx#vNYuwc_oAh7S(>_2b?X&(3(=zj(ZV{#nIS9rH_CS?RYG{*=8S zZLS`ers;OF|M1&q#wPwiFg`$6Js}v`{fYIQ9t|(uf@SfP&~*hA+$?v9{;*f3l$tKXliCNv^t*AbUq} z119P$bRFB~>OQb(f7ykeo}LMyH*loP9FEa-idi$?3{HOnoB6N;9Q5$gYFOC7l`juV ze8yU!1!vZ_3SigwAUo2cwW#qx;=;lL(iZ>?;Rtcy+#M_Y4lL?o_$}hb)>GI8M|dyz z^*`feTsg}eZ`Y@!Xhlmx7t?ccaw5dNv5V5--8EDjdxo?=#7v;yB-zyjjb-QLx6nZA zU6u1rhLds)@=Gw#dIL^a$MjGKChY-bHv?EAK}|U1sd$9C4|Dv+2m;|>Aq{X=V5YEs zSK!)wqx(>({zFTN9zGxhe9MVU$GUKVz|X&IF}AZyLAo!f`npI@|5E>u9=>r`6OspH zjm z_&-)c;(CP6-IM6f=EIPcLox?iUJ~Hic&%XnTIdRyznt;CR9;0nxm;$y1r0#8ky*`}z!Oa~dM1XgDJv0_IPC5*>JkTWYg z`(%wj%=#=CLlBs(?xp5ii-t8h zl)C`Z1Yxv@TGs2AoZ6o><(I6BU2lbCeqAHd)+^woGXc43An^z;5f3`qC`=9MQcFKB?N$CkMqF@9u}Jc9A+jmVx`mMx~Ihan05?S(4pPt&N3_ zrXg*X+a$0>>n20MdTSyp>SH@W|U zknOs116pYqT;u{&QH`N8@E&nAMv8BLdFKn>iEs536ZE<9`+E$V5F1BJj^0Sk$FY`~ z%; zWApYDz)6pZQgFta$!o1-M|e|f7n*3LH^|lvNU}zt1IgEG17xl>cq_T;tfCC3)>x!u zwYRI%lJ1N&IL&{3UorP?JGHM&t~9h;4Zsv{F7P|7OjF<>7P13op-+#g`51i2CV*AR z=p5MdS@w#c{EH+W$+apyNn7nL*Hd&D2HwBc7`CSC4-0_ws(1w_y{wtnS5uLTlL@Dz z51Dyzvjbf)Y~_6Nq#K}MxGBnZbrrk=Jy^MDNnim+aB_J}9lj#k!~0wJ_{{ye1v!?e z?ud>6>v2SoKb6skhC`8{*m33vBzgO<^K;HegS8y@E`7V)+k+sX3I&`Gmqm?R6;pJ+j7*GCZnzWcah^d|SSflX zgk@3k&%Z0_Z-~_oquFM3ZoOjAh-KzrNY6b zsLMly_zF@yC7Z$FUTC%FDIjU(gozD)$<@{XN)#cQ+J%^cT$YU|BZ6hifa%x@V~UwB zPdI^&4jwF*SUc+h9@PZesfuU6Btzb57`SA#B$Q*tc!K*nwJ^;wL%u7*Xd-kjBByb1 z`B9oeQyP)5k$>*~TKykcfI-moD*#aAN1y7BlXw#(1<)muK!Yuv9jS*9K@9Ne^>$wA zJ3-W!tB^#R2bJm7KfMx-llj9Hc1NmEcJtY%nQ=f_) zh|rFYxqf(Y4&7x?4SDzkeVf+SRz>j9EaUHmc;`eQw0E1;9n%W-e>%Wi#q zyLaB0!HJ7tm4NVYdoLvH=$z66TxBXL&A7D!ySpQYe4h(<;N0cP3{3S(P zcJb`F0KA_lDQ%vAIrU^Z;v+1UxZN?!8O>+Y5D;J$1w? z4@eD?5*0&M=3sIh0W*CqC{Cz=aR%MY26M8&kIS0wQ$)u)Z`C5Mhv7s7c7JTUkX&w0 zQLkT8WzxU41%OXo<2S&vmtcY2HNmDgqlcoS3^I^23|-+<*UzF+2N18+In#3&x-1uM zX1qvL-mVkgl@l2uU$)yfq4V|o9;tDV{5zt+FkEjVaCdCQaHu(@+q&rHg^3~%-@(9B zTDku8UBaJT3194oNw{arE^3}ll^slIv{pb$bc*@?-G!A)J=|R3B#%qB8*(EmI#k|Y zGt!Ew=BI%2=zmZ2S}tSwbvr8*)RHl{Nx1_`2agsHxlN#vyY=uh&Op=y6$+{wB%6RD zGyhaS8D^zK?LISWX6UEq!Tb@PhO>UMJ9d4EWx;vN{NY0&XdRvPuC&H_ox+K|9x zP9peO$X6Z&3Z^!dTY{=e#;*Eq^puTw%h3}vO~FudPH5i2P^n$*DHsPB*^1VvjijT4 zr~z~YCYKSWgKmce^CVdkfk~B5{K@n%XkqD=p$N_ryDd6aAo6gG@h}p(H4!|N01+XV zLzVqli)b>Ah}18Khd+H_)jBp4kyC}tTolDvPbpa56%A(e zs1%~+i-aI(WR6g4P!M&xWkVnCAWaIG2O$OSN)ssv2FKZLsBzpl@s4&#q{=c4kJXJs z6f!*O9PFziU|T3|HBdM%gFKajMmQSs`l5#27hr)A!$ngwUYigUx?Dwb`u5?as4Z>s zhsnh*BMR(rv?S`9z)=@!7zBB6nGH$W1k{Ljh^s4sMBWD-4s{BoGm$uhbR^Q*_

A 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 literal 27101 zcmeFZcT|(<+AsWAMrOvs%yyJf+BilXMPQUBH9BrZL_k46x{AQi6%0rT!Ew|X3utIk zqtc`XDWMb8QKW<@Ewm7n5+Oj477|GET{nBb=R51X`~By9XMN|avo>qZL^0)l?)xgg zay|L;w7K!dueX1VVc15K6MwhFurJ_`tKWUO4j!09whcV23bZu-1uJY-7{jn1Fq6L@ zIeRT-f*n$0O{m3767GGy?!?xZ$Gd)$o7X$P>HJS#yPUU}ZLBGu(ZhLt{Noz+Y1W@# z<@~8}DYT^K;=p$ulr=ZkWc?DMzVFBFmOl=EH|X}=4pv9<6Jxq36U z8XgZ{{|Z|LkB{ruM#2Mj`uys{@PPgF^Qs@v<9Cn#{qlcr-M_;IQsLhz^S`HDXqy_( zSww`t>dWir4EHOx4ZpczTS?-vq*`WKsT5TshoT)#Yp}@q<51vy;s;V$`((}gJvw&` zF7Ik=wzAo_x@&3ihUU4slS#|M)jNKGlBl)n2dv1>!S~BY`Pyqwkwtf<``50;&ZpB} z=lAf{^LRgEn2~=Nk#60ya3D;4AR(=q=vm#^SkNrnoo;HRx766?UShjxZ=blwMt?M2 zk=k4=aych`U3mV@y~IlYVInNFHmrQ2n5X7BP$OJNbt6pG3hI^#qb<0`pvgY&@0&&W z9ix@pH0CVRklw3}Vfo4H2u>JQ|7P&TpG!EM*xQkAl7Y=dUSFQGcC)Bw?p&Lq8|Z#( zQ*BV0uin*PDatMcw@vsO*17_>Pw%`-UUOVr4qZOnQ}=KuU*%>PyDHeh4O?udbDDE| zF|5dM!>V-{>>^+I@gzTPqs%~hbCZnqwoS{8+8ldN!5dA5Yp`(LYH4NS0oT!WwXGJn zos*xCq^q#=LpbOb+NKGqtP5p328})NmA&6wzH<9|-iB4>p}d)mXJ+;}5jx|xX^F1i z+b%xrSL1muNy|GPm#E|KoD-~wVae)8BUeQ-y4<*g6A=p+kJrwX6$_M8DlFE?_U%$m zogJQ0BB@*K#ZQmy^_=N)v+Mux5@+&C8urvD_{K4+&$}wyQl4LRR+zxgggnKLX^h$9 z@z*qEkBNKBe*UV(@w}MXMb~JL(UwHdu9}b_HX&hdoN%se_6sA?C)W_)8Sg~cx88Dp ziV<-sK9U_?6 zY+9wGJv+*1wjhN%-QpNs%p@|Bb(qoAs^a~nOA>*^Fxqh8nqBpiqg2LMd*&MN>ugj> z=1|8am&A+_o#lmZw`qPV-ZD_Dp5PeZDN2p7FRU7GDmFxa+q;-=Zv}6y;XhD=TYVTi zKl_i<;kSCn$?Tz>^jjR-ceFltJ?X66Y8$7;op!owYlg|Dv%SNtndWMK?_-sjI&EQD z+b;@CLqYhIyfC?~ZqsCfAXffrquaL1J8?I-jO2upB}&XFBY9-{RoKXTV`Y__B#0+d z!4&)8%)rvYz|ofeFg~$|?oe*eT3ilfa!vba&VjG!c}B}B9J)_W@dl;kp|*uPA6zn< zre(47fo_{%r6o4HTu$!j+WijV@+93=Do5*gO}3u%G_P-racK@@8dB1uje>66I*l$f zR3HgL|3SFZt`@j6l15%$Uo+Dm$V|9NvUMW30m{L*OO~_eCYPeqLK7s0my-?Ps=~w~ z@egZcHodj7kQ%{~n_*|0(GGulFMFwLLeM<^NNY*lk|d5e9vyK$bt=2K{9s(x9n%+%0>-76@X$1bK&ZiB^SR|bM`P$o&&Es0CeRyGKqrAad$1_U{ zlY#AwS8Y#@E*aRmJ@ez#5!?84?d1K+qOi_Em39fgcC81*7dI`ePLpz;oU$m6X)EDA z5sq07b2|jx>CtYGiivnqL=Mr7T94ZXX^osZuxRArL$V8?m*@PH5Xt1>BP(v?@P{Pz`rHq8 z#RhAAT-=V_-=^V?l1R6JBirPq9I^C=LjUYL1@=;(H$fIQOJ%+(Ph{mJTbpRTRc?&1 zctK^7y8U!(GM=7KT=Lo~(?ei6c{{0NhaY~4K{{I!48;&?%1-Z|^gf_^eOdAGGG|zM z$KY0q>a&F;`F(3A!hWG_P&o8>t{n>jMARaeu~)G*(c(4?(Psv|AQ9}Z_0Duc$a?`Do2T_-EKvb%2X z;&JOl7Qb?fjkF-NaQ=xcl{6;`n|R*P+^P@RRD8@F?ef)6?~E8pTBgM{GOnGkk3#l7 z^)WvR>HpCBKZ@A1pm~nWo~f7@uYwwlxo7Dje`j&Cm#wsbzR1k0^>h33kXMJl&ZqJE zI8oZWJx8IC3TL?y-x(d#EIhv6oUTf9if3GJBOjph~s= z>V59QPZM3W4mp!O9+c!)VT$oiLF`P;*LK10MxF!tcE@eL`nj*% z-=(Ux{`#7eFF4bE#+Mc+cXo+H7WeP=s@N%*y~}o^bKT1+Yg;EfFa4{Z%3z&+l~7Jh zxp{i{^PSee@1g7Iq6=(LB8I1W*NtUe$b3?ZDj!+|v?bF#i8g#1y=JMUFt^N}pC>Imd3#bn17dS*pw=OXJ*m9j^6afhp7*DTakL?@*wflI z+IC7&)@lltJ@Qw|*+C2|i)hvL0`ka?56>XE%l8Q`zx;~WwE7h;@%gQgb>2^5TSLaj zINAOjBR8&JSH7KSVKOE=P07K=cqTezy}H7oA8JDt7H$dhOdQRc;@$h#&++gb186m*9!cRa0daIoU%1O{fBB=h{J| zs3(U3$k|i*A-Zy+W->1opn^sH_~_dFiLWZ2wa;9pzNo?HU%juzFcEx8k4AO8xj$s~ z;N`(cLiLmF&&*EPw$q?3an64jNoq9=eA9AIp)%a)Rg>M^UgNbb+}JEIq2#D@i)ZyRK32)aY>|s1hMXL?AWlH)e?6`)2^?2Vi+`=SZuhoF zIM2GRfy@HN0i5l|x`Q!$ELw*fsd1x+@^i-(ZUZckUx5W}zX|Ui^zBV)3u-Tr&$7za z&YV21&UWgOlw`Ryd8A677yI70sh%)NeMTqjqm7?i7^s1(2${KK@-Y`8*(Wn(@>ylb z>?pcG=G%t?x0$?{c6drD{~a{^g~qS;d=krGK6Ow;;OF#t8vsK82J-)Z^8WlIPI7>_ zd*knypL})vep}Iz^o~5Pl(_1_*|TSdV=VBkdz48jyG-R+1YNA?2#66s<%{OWv&Xtz zPNd(u&#U?l(>^#q-f22?+#%~qx@!BgD?|J3D}JC@hug5+R@JY2BEJ^9SvKFr_??91 zpMjT@z@NvfHelW7*G6LBwZ%L5#ogK{-$~ep`TV$D+b34Fyg#?F8H?Qb3-;yV9a?^+ zZcYhU{q8p$MeMsc?3>76yx!GomSY`JbbaineXD-JZkE2@D!H@fanLe*)!{Qj`ArZ) z|Ch@t+dBv#3i*70cTZIF$3KUPua2t-hil7cKPSgDE$H-)QQC_b3q85K>pfmfYW9_E zHt!iIV%i!1P@tUTR$;t8nv0J$YuDk_#ktzCME#l3{R)E1MnwLky<*>EcyU$3L(lhu ze9d1=UaiahM%%B18-AL#3#$*^gROo1U~Qz2-@dIajC^^umfxg&Ti!^xswgEcK6}&2 zTJ_R%>f9S*v1U~Ono+-)i#LqX;)%~#+0+WnAcpAoS@wbtelo^bWOF#9I3T{;0 z`65swc-TxhEqBAVmsJXTq%kKc(^L1^hhFm#h3DTe%S)XbmkL;=cD?L{>b7qz5(I=_qAZ&1DQ5k45-;^YmC)d&%H?mxNl@Q-A5a-bAqV_TXMaRg21uiP}m5n{2^J zu6seDH%e zp*UCB`ob2@xp*>!RY=`Rxq90&+ ztY<@h!fU;GnUioztZ@Uy26a2Gy-iI}dQ^46tnMnTyJ!yJL-$)PABVbeKE%6|aU9On z_S~H7Yj~gTMZou%cEJ7MeJ9d4Aq+>-{h|tb&E{&y}oI*x|{4zLWlB+PVV$*xK^x#9qPb6dEVq4PRFF zQA@sb>cN}TAeUp_umfAs4w%(<$tc;;43}=L$FrOxx2zv^U)U)3-<_D*;k$L>W}3>@ z*5>|)Iz&GfMSmvi?EaMU-XvK1A*5t$uiDr<@jzWc!Y&pVEx#T_d$f+efKK|A%LcVD zD&$Ajkz_^fMcCkT!4AL&CB;>0tFf+fUwU_Lp=soNA>{8Ve3jL{tqk>ATYuhY&xaTI z%mF!jp9*iK_R4&L%YWp;MAGR5cc$=hp()5u=CbG#P?*1^ah)9g5$3!^PX7!}I=F3DLHRKloJ5?-qx2_PC+?#k)M1Y30&5=c>nrU&YmOo-$?I z=jzkD`Ur4=(DU5pYLru%Fqz;lPuPaCA{OjK;(H{@aE-e*y%<|XY zCh7F01+(ob@>pcm1)$+yH!4Ja=Y*a%*pTi1zQWk17hRvQErgZj8NJin;Ot86+Ag@f zlEE)riVc>R;2*b(Og|(-5&aR4{`85}hqdLD9Yegf8i;t-Q~td}Z+(b^XFT7zFLm@) zg`l);oY_&gWj)tzEIFolkwj^HdPSCss_F7+CSF?}j{43C)O)|edQw_Q!&9hG7I$7} z-3~QNR#o=%X~k>rL{ECFfUdW~-8dYte=n)3?c)!;r>MN$K4W#01d{Y)DE>DyWPV7_ zgX)l0xzoStrH4{$HlO*d^Z7>m;1eYzp>vYr=i2|)V^&HOyz|#4*q6;*p~GCuQ9IEn zv^F-hwo=>X1sy1b+i7eghe^VDT5q1QXT;&|6og5h{v*BQj}Mtwub;=TpW&n7=etBW zM)>((rN>yE@~{yIoGluxpzlcJ48zb^Fo z)AjP#VUI^qLTiW2a+yq1_1akJGE>@e##k|KmuY+l!9=UA*sZyZx%hdASET?eZIUX< zVA>``(VR(j(Bw?x9?Ec>t#f0=l_4^2s3DqZ5it|vc;)pLg5wTA019h?e(xUpYqQ0E z`QLP}O%FN-vk4|-7xB35HqBp81D-l(JM#RioM+EY)P}BmgJG{nT=cNRJ5mCg_GYpI zxvh+Q+ccUMEK>EfKnn3&=yr`R^<(R$>Ib*(%}ycPI)`NT&YDq-4CNdzA6e-2rj~lP z@9y(0ciN=)>2ZH3uRsVGG1o9Fi&Zh2Eh`W~qSn}KUX_X%}^BpUt0 zE%}r4%7HBhItNJbj_q`kw3vu?&#_kk>x`Ss6SefgE7hE;hB=Z+&dhE6@`W-cU&y5} zVapPu6!qOkg%H2L{bx{Av?|~4+gs6i z$st&@ns2nCzINDDr3ZQVd4p1OPIg+->$`H)tF5AW9=FTEpYgdt*(KC}dGWLERK?H? zP#tTBh9feHu}ge9exApIB9-jz2ph5uq~S8I(xJ=eB|~_L>wN)9BII&KJgQ3$BVuVX z;eEPs!hA%HXgn`)vW#UDw(I)4^ACx&(3zP z4Mk@FO4sfEPF0#FGo923Eh%3rrsEoAl*1p8MA@X1UBb&{~zr7hpC1eZq|H3|6q zTF#?Ba#iMJE|dQ(J*w%btl`;S37lRGF2O0t&aX~58rRxc?$5cWpj%w(`|X2|Nrv1T zRcfoSE`2~Ck-uDnH8#;yUezaRcx1WIBQhWS3d-r*v2043AG>OoiP6$T$sC`9Z_hGQ zQPltS-DAtgWen}UVOVS{UBaUS7NDIZ8;IxHg?y=mZ@JX#obFKWrz>5aZ5}TOr_Mam zs+5R$?KxK2DOcWX6N*@L=TH*ta^*<8!^u9h6ML8QY^lznf~iyurh$fwu*vphSt@L5 zW?E=jt)RD*Llm%0Kd@ikTyI({>k=Uxu>|~ywqd56E><-;dF1>^V1YrzYmb5Ft_>*> zqA`#-!g9o&pi^XG)1|U@* zUft$q=sv2K;S)-O^MW&pcIc1WMR`Lr%N7zN&Nc0X*@=Y7m`ct}cGKO%32YK62wE zl_Xu_Bsen@HTE36wPCL>GgZHQPL$}8wzqZwa$^=YYOhHEub@23BxZL{7{9i?#Dh6E z(G!@-gKg^SG})7S_Uml&FscE2v2b|ejAV(QP2hFu&b>Pt`02^*ctw4W)|9wk%D-5@ z6_+EfWzodfO{|soV*i{4kWo6f6-&GJ@y|^A-ZJk5-N4aHjpSXK*z{{Tm;H(kW!bKs zX~gW5fRlTdP01y#dU@Yr=8t~f0X5X@?pD>N@k8?+R?RQzxR4Jw<(gXU=7nj5F1wY> z+y(Xj>k;S&%nP>_ZF(g<7pZ-y7>a3u=Glt2YJQzVC=oWFn-ODv^4PIso>A48LM%#- zZqmi=NxOE@)48gw_D$7zM~+p`LaRRCk!I-KmEZRBC;IY;I*PG3(aIDZPIzEk;OMS0 zx}cXv-Tu*fVQOheS^5Yk>?*H|L(dEmjb&>@EH8#H^mv2_M-pOi5ODi>JB09-`7;C6 zSH~=AV)WNjO)UpXu4JbM?dvQ5IJ*w(j$Mg#oV8B$z$X2P(_cUK${YW2tFcnZ zB_-1G=kTRIjv7N3a!WEx(YW~69}3q7ue+^3k&jLW+M7+a(^xQl$!UQ@lHU9Nz>g5T zC*i+xeRzg(}LPb2#BYiX0gP@7A=%EBL}LC(8>{Oiv|EpHq0NbMSx+d}gs&AsY% zixCmHC0vo^8Pt3pg%ag}!~X zOqO)sMH(lIU%niNx1NMnQwe>021=;g%iq7MobPflHEcG5O5_+a62}X_PdKAHL2Kht z>CQB9raE!2$xOPk_*rRv1L}#ewowQz7dPxt$J78Ti`eNLSw0ZzO}=zjmG*X(jKb-! zF_nG$0-*K8^;)M&)$NQND=bGY{ur0rYWgUaWRzzmY$3-{_;QneIR%fjTi{iFrBlK5W>A|`OPEI0n*JQp7OvSkFKfafo$Rw=aEWBOr29gX0ygwq@*Uw5^+oYn$fe=E(~_ zy-&Z&XcDf@%bl#HGD>pu)7#d_8a%-D_?EMap;pHh%q>Ajunjw9DQU3{-)e1pMAFNu zi}$7VxMzkHv_gtI1hwWGYWr7>Wo=JggPnK1gxsVU7W8VULW;;JkwD=dg}`dr=8^L7 zlJKG|ipG*_pFbMRwrGs?lz1R0QL{8v9WwmT_+;&nTGgkgV8izLCU7YvBk;3K$Ssc! z?1YLL9!OhcYkgsyAQAIi9X6Z#s4`_a7I3GG2Dx>C8x&O5t}YII|>8 z6IP*9bFXwz2LBdy&hWYS#);a#_Mre3@`;+BmT3SAEUTM3A9#NZ<19Jn$c!cX z*CpPNH9B-#A>f2F#F7CtGc6-ou!h|4f`vtmc=oYWc&RH|4=)dS85O z2Xy(W4N3+T9exn@nQ50~jTXpx-eF zZN@8Ed?D%*i5p1E#>ek{|GaGpAx3mrT1w0;|AiFU z@v61hIX<$wzkUKQ7k-qZLwSPmYQE*81SoHtevl8KIEkvF-qjQ7AVmUHnSidj4KOU~ zp2g><95^ByRPACcjC28_ngg_Ghg7&a*0yWSx(#oYCDXU#Ru}X-@3d2TxlvJ1vwEV~ zbi0mUN5F)<- zLH*i9_VAr&yA8V|3CJBf2J8ugpZ_%F^$#NQ*xhJ(`W3=tk!BB+BGEJd51bFvehhS} zaAA*0)~Q6xP$!mlmi<28ds;8Dh7q;}FhXVr^{G;b3q9+qqd3-B_cdU2=huXa=UKA= zj=3LhE1TDb&Y$dy!~2MSS0dznVv?Nr{@@yI`}zD~phJ{lK;RZ)(Y#JlFBZwP0F;Vh zck-?CY)r_|#$$cgge$o-(^B1<<44}^jBeTL;B$sGf?t>}fuUs9&Udg^pz=6iX#bD=1|! za?Q-Uqp}4ZS-ZIfje!b~4OKytN!q@>8NqyLjH$u71)g)C-s{2>!N#5ehyBrB0%` zcNMdf7zHnPaP#$^70c`RIiin+iyj4IsuiAVOCu2)s8d2}bT4@r)cQu)m^7OL`;k9( zM!f~vS~5xNi*K#HC|j}DDl2w@%F{U4DJ~dGw62?<$zCva*z z4NFeug)Q)AJhov)7oh!}-+3T>ak?KWKA7OA;`iH;SFKqWyTFEP7QT;;hy~KDxq0`g zw_9u-5qadIYLvtH`1-z4h?xZG$Zn%GjMIEpg+(e4FXEjw^tMKT5<;P z1iR8-&p`EX@xlYDzB<{fg$P4eP8KXv9p)CSB>-DT?`gEY1=IPP0L>#r z(+?YsmIrey>0q1}^(FxhDniL^5-%&L1M*A8!6&S04nZ)+xr*b$GzFZD13npMj$y?w9)f%!m$ z5GG2P&Ldo_R6@wQPBteg&CYsH%H>~rKEJvxYhhW!cP23Nb2|-}2;!0r*g5I{Yq940 z_eES8+U}(f0S{5P6VP^H5C_oB13BkwJ5cfc2XzyWq=HkR4{X`8Wem2;uX>UOsJ!HA zKk8G>>U0CpEYwq7+cHdg;NQp@#~V6n@i@Dp^GBd4#KZ8&E|cA-9zAf4Hl%elb4%OJ zO<_L{g3*s_M6^O7xc|x+i^|dK)C?GY8$O%N*LhR2b`WPW!|;#LFjgkc?F;KEUB5|@|*ke+&}lEz5V^G%`-qiJm4JA zLOrDb&JqG!brIQW1V|NwHUqMTsmD62+h1fEtz;N5bYG`yv8JqYVSI{YhAL1v(6t_t zRNOp(AAWH3q+yX)o4YmINt|cLvoqtJc?rACl5lpC%KgqYfMm=l?MYL!h|j`n6TAy6 zcLOK%+ijjw@#aZVnOIT=?mw#Q%rwLH>R>)9y<=Sk4)>#}_j|h*pvsJbEFf)5H;yiz z&0jQhOeGN4b>{dkjeF&e1^W{|aK`ha@n96rn*#oq|5MBCofNbMcgTSJ&I8_I{kaRU zBg2neqZ&Z~2weW$kPS@Mv%lg>6eN~qGQw$;@R^nH`*ZW2QKIU! zU6Z*BqkKC801Sk)}eeIf&XFa^j+vJRz=|G1YY+K^jK*D7;~nHRG2CQCS{={KM~9xg0i%WMB{KD6OLoef(oSKph1&=!*xT0WXi+(wi7M97p>HPj++J-!ONY)Xin8 zyCJRy%T0sfgiIh_y4;jNn7*jDtffolse$cgIwQ-oO!dyN}29u4G*wb!BiSE;;4AjYRudH z%b&qiGslbPG(FpJ==vP|YvWM3EDlAx43UbclUMTE01JaWx@-Y#s|M99D8>$I6@$Ma z{mgB00T7HP4E(^A7<+zuhmey4FtqaX8~MOD+dPJ0k4`m2DF!Z0mUHDfi^mIEtLrGn zWFL5sN)UbnK{m(+4RxDQ_%)G$Pk~SwMT-Z9KtUo8eB}OQi^AQ$T{dR;Y%P??wElo* zh0FD;RESYp&$GC-(1=HyRH!WoCIRh(CbqEGVIyrf#g_1o28KHIcFkWOAdw7`>@Bq1 z1Q_~MLs~eDeDx~^_df)zqvIkKu}XlWj3Cx0s1wxAFQDk^ETjH8KhJh}PJ_bcalgUm z56%(nJR^VghQm92ee$qZS-*u7RLMfD1QoqrRhm~3UPy9*tqeYsyM0&s?53oY-S9MuS( z>aS9x+N(OyPJuqIK^X6-QgchU6cY26g)<5C6ewYP0C}m~c)b$Y&Ii+J;cD1vN4S`u z9>V~s$3*vYEkF8`SNYbdBv87*z_1E{D#?8zqkIY=%j`UrWSFW)Xp;A@d5EevB$>Yi z`hDSt}=sn`hc75>2olY@HIUU6Z}Ox9Q`E3D0wkc4FNMTF-c^ zVWcDd!mC>Zrt5YXfcW%P}ZtT{Xtws z&?5j$fYCe`vDX50xl^Dfp(KKqNI}K}QkHVWNIL~Z-UG0_~;O#1Pa_LHpbTa3~Ru1CF(t~PHgg0qN4jS9IgaG)uG%{y{$-MMoIc}G2< zL$;N9vkbsF&>FvT2pk$DmLN)6b#;uaEdFF{2T--3&?zJ}n-ZaC7&>e&U5c441@Iu*RrPAo7q0q~Jf=v18r=b;O*c@_vs z=lHVqeI-LZ8HLM-$s^29EgH=I;)nsrYP45is3f3G=>foCb>_?&y&%{EPe@u|H#U=% ztgFfFseFS#!yAKaqx|U4ICXwaA}2S zJLYYV_1UvysRJVx>TcOFvY&Ji%L!G`HfCwYv)^FtfTig>Yik0i=|@D_!Q77=8Wg-EOk?6ULd99OVbg;ni5e(x<*R~Y|%GYp1E6AbL;`8p)q z+s>olu?>%^mvRPD8$zsEOKV7s)}Qw`8(BH{wVQp&fo2o`(@X6^P&FEIy2cB z=X=en_qzRNy58O^hkdHKLsQfAa!$tC@pzLj938*?;?kLizTdkG#V_10uXQ{8>)>3t zk&?mrR>3ThBGBdV<=maaTZNcU7?kQo{a<~2(5}YI`&H~PR=Dza7?n-72>-Y_@z9lv zcVB6q1Ecqph(?qBTcD*(*(6&hgsxhCd(U- z2E2tnACju#1#j*+u$Pdeq$E^4N02L`2NaP*6dXk9L5{QF6CIe87wf*}*F(Bjkp{#21r5nw8I*4bPkVPUKL&iElfNeN)B_)kmZ!L(Paooln{?0!U`x{;A2(wv~~-}&5cDngsPLeP8cSaBG0k^mWDpEsRxb`sd~6pCZS`7t&9Bn19jE zdqu}Fp8Rt3=&Rk)uC83+HSnzR)$IeGW#}s|MQ@k zI$@yWO3mlmqE;SW)?ubc8YWdNDQMNgibfn)Z@@@0h}c6bMjJW-Dk_6c&L}MOE8#J~ z;6>9hegG9x03vNWcqp`(zt)?Bb=(5M>uGtf({7#xVBR5DK1VEQl_Zg80DfigP~BrSNJqeQ+0kk7Pup)P`|ycDWp zTi62PRx*7GvXVRu*8~3~eg~oj(pHnp_`aU(uo{R!|80`8(URvmf~8#RJajJ>;y>F! zQ>~j#}_CNT#=Uer;n&KdgDnP;m<7>|Wkp7DAXIYTzA zZhQL@icVU3fmon`70rWOI>>*VpT8fV4g*45!?P`;B~@Q%g|))%GTQ2gp6jBa0jP${ zpN#QMfU0KEgaCBB&szoMYQO7EmbSx${xGf)sWK3u;ozq>55Bme0lFb7Mo5YD^73i} zy$MAFnmM{Iqx1+tO`yaU-yUKz`^S>?$p`^uT861ov7I%a<2fMu^sq#w>BwO*)0uNB8Bd*sU;_>D_mQ9K7s;< zF^Itk?GD4qAkChwVBkCEL51VpyLWFRbOF|n;p|5!=K;zzL!LR*tn)9;8G2^){65@Q z`j77gc=l{%js-yhxc0ZOiGj$AUE)Y1^1OXi3woIH%=cZAuuWKZ=-NnZI{2Vh$1~`> zE=XuA2ARtp$Q7c>v*U@$=~-F3VMmNtP&0566c2Z;Ep%9a{keHoBB0}u*nu(&=Y2FA zhQ3aYjS517 zE>d^R^~N$ZwY5h<4R1y936K+RWZHy;zbL$9@qVFWXydMgOOj-2{d5+szR_$AK@jRqxY(+GQjtJe=&2QYa+~)0TU=pymYy*u%tE1NIp4~4yUtC)TNa=|AN`g-(jP>Olx+v%;imWRy5n2f^^u(ImMIJ&(G={m@&5 zk7`gFLB5GWY?tLiBHk?`c#{@5=2UroV~1J3E_R-`*RJ&1i&2jFdyE#yu3GTnZSDU3 z&6_tHpcJ55gb-C;VbwmkInn39C?IZ-B<1VkVeZGIIB3@gmipzQok>u1kWlPCSbrUf z8%V|vUwj3nG|0WLD$b@SLQ6|4lLU4pTnviTa;Lnyy1LhH?P%hLF7Bg`fPYV{E@=Du zZbD5*mnv3oJ_Cuz1y`bH zaDa5|Af&t_SVw492?!IbCX^q!YR&H?{YW&NgPf0X9uFmRxfB1l^=fY+#AjV#{$UKN zOBz(%6{!uFcKI*mSUrB92AU?7MjLEx9Edl5N-*ksLu@$>E zHK;e|b>JP%HAPRJ?10%oH$PytEnrN*xiAnqU$0*CGtjO;hw_HqV2!C;TU%GcI9mz0 zt+kV(rU_~cXtJ}-E|P;V4QT-)^Fg2PBDA2K!oq>C6-)NIqak&Gy=VfoKK>i@x2>z- zR)_z&oBK=eZ=q%_Ow{&O-DR|Ca;bK@`5B!<2{1(fD2LX)04Nxby!3Xe3tdxF(`RyP z`5iEMGGL+pqQy5+-d1Jc8`e6_mFN=6v*_G93>$#SSj=a0@`h%wpp(@q5%kG%e;K{t zsT6P$iiA8w5|N{hXd4nG-Q3)KyV;@1^5(UlEA9-yBtTSQO@TayNv_A)mJ($89FM&g zxjNQdy8;Cp;gZ-`AI^u7tQtuLO{UKQk70eA;e;L^F05VXoq$|HH+rsA#+l(pMl{@^ zwi9q%BkGqh{-ptAU6kCFLz$b;*PC~;)2-Q8=CYiiDe?8PQR<^B>5w)ya`ZZ}5z=v6&Bq^@p>WNpB-5#CK1nwi* zc~)T`UU=FszdXJamM5AQgsbG4hY5PObHfj9BrS$C6YDN?4f2-Gc`#BFJUSbz#V*P+ znPZtA4pUO50N%r}%P8W-V@=i2h;(8rEFMaXDH+kS60~|L4)HWWOe3 zyy!i8GlX>gfP*r&msC;hR^a+b#2atub2bLvJ(Rz{2XVo|R2*8^FS1XjkB3^#k` zFKVW!Ps2)>*lZa*dozbF7{{6P)E#a2>ld4l$E9hMCab!#u{GyP^ycxDN4bF!kAe~m zlk%M(R`FW*#rI8syuSG+2kBhmjm7cy7LY`&tq_po68>X$)KppcSUjjj=a;l1_ z{XH7Va~IBxgaB$uDu;oay~?6@w`bA>HG52W&RoW4;OWK3TmXLfM<(Pjt}?(4Dc|>_6v3!PLY9BTY%&}w&@xqYTK}BhTCq3) z5;~;aD9_c0#@&*nrBdD>V47u~3R#$hbW?{KDssor9Q7=8eIrB`kxY&_F(aA-0bBX$ z>D?JL9ESK5j0mQfs#W69fRVO$%V^E+WIc22d;|Ay=dqt6Z{50eD&^1tBwM4pjB5lW z5Dy;ZisdxB`m2A^$F^(>%6&O6pO&O*HH*$?{EUh8S7h9XOs$65U9LMLnqCvpQZQsW zFLbb4cyg4Cb2AJw?X+WMPtlpC!Vez1w^;A(@l3#(G`3ckPW`Z*XBE=6W$)OMg$cQd zB=+)ZAcYRO>e$eq@9>_g*>COPm|RTUL-i)(K#K1F^k@OTGXwEiNJli!u*YNy zAab~<-J4gwKp0SfF5utGLVJX2lu5vgFXVFf^-buKJtWdVLZO0AUC&ZxG@He#kBwf12%5q>pQuX@68@z z@L-zw=0Sg8?K9|(Tx z2=bFFt_*2c`+-b_CY1NP)EOY{4)i)JYwOO7?){@vy!$(xfDW&7{U;ZnDVcwx&GB1ptf>1tYMe8oKCklwbtG#JHwljLF8&K7mSF9w|ks&!e=>9 zGOPreY9TvzRo7(;ZOIvRQ-s&F&-=>#H|ST*o9P9azPiVubq`E)_wFSMZnGlJurEyV zP35%l1B)Lg=C!KmJICJb+Ssn_=<@jaj(K{`ZZqos?kX|;&;LvWM;q*C^@2YYEqpJh zYTvQE$dmh~=MlZ(&R?i}`c-_mh33VOb;1dl=o|Cs%(X^#D3a)auB&m7Hd6J+ac^|U z|B_}eXn{?%)djyecw>y^Klz*Xza;8~ovyTnE2}Ymqg6j(-yOEIvojJ@GDkp9Zw7B_ z1&$)c5WagL4meV2-RY2H`))<35x|?;NUBmdThGn-P+soFAhHUTt?c8QT-8pnt@mVq z2oBDGTBB+7`LI`mVw#p9-(?~_BQQE6LvtgYzQ^wKxQ#)tPL7Lx{GAWn?5Qe^_G!Ez z%kx8E4r%aBe&y`U*{H>jz1*uD)m`j?hn=j{xGXuT&c^oLnN#uVWrt{YF1U7eT?;>% zG-{YwCI99U1Inc(`%0ejt7JsR5CKE_C2%3%fq^7er9Y<*2~({=Bajt>h}J{o)%aJ^ zr`Xlm5QxynUC_jySyWC{mEH{**;a_X0MOpi080OE__`7;74%!QD}eh(K=1S0^j6yz zile2KRSdEi67_@qB47w}^y6Lifse$vxHu$YFsN*FPXU@WM0We^(0@hfT|Tmt4md0w zS{12CF^vSg(5lDFNB3p}Ct!k-Rt!uHv9|t+n>+R$kI>C|c9LCgVcMRjo_UoiwhU=1 zFBXw4w4$NCR%YbxCgAISGFzW!Zc=UpU+VMw*=3h^@wl;m!?^Uim`+;?xmb3iN#`SF zzs#M)S9Xxs9+sGB!42e74+uzBAjs^HZ&5RYP6Q zvf#5FaqK-Wn)H`kOj~h>6Y*q{v(uwCK?H8{jfGinrw?1gc?s$k1OL>37eI6^l?07n;2c;Ks#ErB%J zgxEODXKEt_a$q)1Fbr}N1Lm;X4BUZbBOw*N4vk(x$~1wJXTMfhu&qQZ8j#%Qxc&Au zgOhAP_OzqyD!=eJYx^fTdp)07G^r%%Xh}wFb84?%>y>^Q(e{~8Hny^|LJkd>$!Lh~ zS6ScuWSW#ypOoEi24u17W(`feEKIvpz>{VWS_%ByF??NM_u7BbIL<9#JW~yFnHPH5o}t3mu@fB{ZZh{yjL;*ra`OFnQ(pts ziGW_wgl=*H!c8scyG}avFKE~Qhp%ENx`Dd)x=jidW?oDH`RE23mFg$4SHdu9#mcWALqVkZ!8ohp@96$Z{htAF1ApCvq=XXSQyw4}w8Aft zWrwC(5T!=9eLV^BDfeS1QP8w{Hjr?{bM)8gVN>(&J8k*4??kqpJ6ci27a}G%|Jv44WgBV^xfAPcyw2->L(XbTLOGXM=5Fy z==)!0YWYNeZ_3{=jP@J(*j+izGQwu8sEoktEh%t_WT2rKv>WI)s5VeAm%*%QMR%*A zxjvZDWTL4U3*xvHs#~Datr!BIKfXqPMK=JV=^Q|Pr+j^V0fT`M#C|#jbAJ6bRImUu zb8_~=$iPW55;9=KkO_joI|B*2Z4UKs7(wTPMQs6qkeu=S`2ln@UkVy6fVB21)CE;? zCG7#{ECd^P7tO0W&saK?W0-?eh=UFW-#q}+q~_$aHa1zMrJAr-3o1hFu?0%@iV*_?x( zDkCF2dYq4s&nc@IsV&_8q6AD03%dMYsN{a>uJZEofoOCP2o0JKf!KcppSwZXsOEJA zx?KPXZK&RlhRM_+AvzXHU|F|~rDgH_rmwNc0`%Q@MQhNV3CQ$BsxczO0U&{n!b}^W zOwRXB{~?_t+ZNeRCMf9qP7Bbz6;F@d4S4z0o=f0bw;;P8hcW>9vM}G|1{xw`hyv12 zM`x#R-mA6Ppl=cS(t%ArXoearI~dF+1E*38<`9FCML603fsqEr z<>hIB=!d|^9!hvXTKG&O&IIrRnp;60ELeIg4N%N;;Uee;@&#*o8Qlz);2b(e!-KZt zL*9l;LJ_DN`Y?CW43k^MaC^uENTsSaLzDkQ^@le|_^%52d(ppW3Mr6JnJ_tv_%+&G zM8*+bgpWz0=!7jy#Ky(>&JM|7=i66Cg$5Sho7KURd4Yl z_MLfopf?6xQlu8g2__u%-Q(laM=eq!u)-^=T%1QZ8bBWI7M$TSb9kF z+iOyMtO{r0=kf?NoBqMs>I7skok;EXzfiX}!3S%$~}ugJBELPMWdr zCW`Jymk~^{)8``o1}``-Vj5AJwC>JE`DyvGzpfARFZWZlq=_<@N2b(i1QiV3zK(UT z8?}(yRk32r2=bWzy(ulXMCo6OzU+1AYdOBbXxFvMn%nA{>>aQIYb)!ts$N!a)kf?m z+^QcipEdW*%%+4gMcei@o3?lVO8f=u-nx7VeWlL-vai-v@#n3>uprH_6n*+@)<~w= z7(3Izm~WM8CCNOxugR3wXoRoxaPvD<#f6r_?K^t8+dlpF)whNE31}y#VM zWo$LEDt09Hh+;C`Nc?z{@UP!*R`N(TezvOyqlR51l~n}}@b$oGFY0Yh+c}fXJ2UNU z`YK+y(idl&`WKVqLdG*irPO_mdzP%-S}rKIKVge{{FtitF)C9|@eOItsiIq#`0RZe z3q?`F!;aJ;~k(NqMOFknSgv(}c z6si_xdb79YH;xGE;-w)pHItc#Y?WEzEBz6+p^7%I!oh({O0!EfaZw$U1Ok>ohNAyx(ISy#tt zVTZj@QKr@BR%O*p2N21Z#+u&re_o19)TJ89ZQ^xb>=%>l;!6CTlY$BA&5csa7>})8 z+)-m2H#=V|HGBjY*SKsu9@t`SYH3Re59KU)+MXd(Bp2Ky^GOM_C4p6=sceT^o_C$S z-|l76Q)b%8j--+LBC0m@%E^;tS0nov zT%~XC9fiQokNt*OVWGjE?D9Vf3N*qZoXka2%{lwk8$U0xGD>lkgEU*KcE9!=19M#7 z%*0cX!qWQ%A$y`z_$lZtaVbo+Cs_PAqNh z3o6{p7?bH79$uq=DLbj`I?HPP*3l>1@{$`IiPD*xs^Y!)L$#(x3j?`;H;lT~D3wzq z%M;Og0SkLrGLb5_I-_&)F0KDhd)FG&UX*WM)TjwC6Iur2t)!T5`oAy zd%{lpWB={Hon3!^XTF(lzBBK6&hx(KInS$9Zld(m@CSLicDQ1)hZ|Y#YP_d3VJmN% z69-2wj@4KDD}~z89jVzJugqtp@KrsI^#*=_kuNErw|na(nLH7d&q#}k*X#(Zlu!^B zljzO*MT6)~=opKIz8~@g6&}w9al7{~Ssgz{hCFJDTLBg-_9OG8b<42=OF8|>bsyxR z*kIzT(p+1ZpG7xF6;C(Ju}4i$8GuX4ccBtO$D>ueE=tj7!nDMcyM3X1HJ3gVB&1rs z@SU%5%vn%G-JefodoF>UyM4%VbanBoJpMj{VM?TH4|0E1qVM9P%oV7zLsDvg$v!L_OWQ?WZ|g5+Bv^%!(wf%+)|u9`9uueJ^JkjscXl#pU#L=|`$Uq% z5VW>>A7g%2Z&qilRC#cglc@6p+{jeDEX1~t8db!Y#^(I2UY-!|t@X!9FJ(s2k<2-V zUzvC}dV&>woencD=DiceqB}gTs}{?(ape`9fW{UcE6DGR7rX}}#n)K-BG~D{5Xp9} zI+pELK%m(_ul8rLeS{4PrFNn zb^Nh=POmWcH=ZTDWIiSzd$-A!r;3$t9QFElc~q$tE|oMyXOfC zZ670|2?fSy&_ww+#L3+GU)R%f`iA zR|Q^pfwuA9^7Jid#8TS zLeOMt=iEe0rLN0(*AC$kw#MK&HzeHZ@ul_M4SK4zko1y$!&m~pC@RoWp}m33Yn#$r8M@pK;!1%_jDE zB{8}O21;z$6C5k}4wETbm(x(2q1dtA|D|zpl!i;!sr+;|4_BTkkH#*>l+8EhAay2h zsL2`Rw$Q*6(+Y*$zWTN=w=p9MV40$Xy&WzUhlB9AM?ox%vxA(VMk>%pfLE{Ow}hjS z>7|mYg_u!Vc91ISFiipA_8FN1Hs7`7OQLWVGpUHV*KQj_fRD1yk8I!5A?PyM?&0Km zRRgx$Pbl^t0buYy@#H5taea~4ycA5cL*q|1^%=(qNDR#ZdJe3BFOwhm_ySVZ%F{su z$qlEl9Oe;l0RjV%y)E>YUwTWP`;`GAb9qbqh(|FY&0^gSk=8ld=}=vG7D`dKCv^s7 z$IeMo>(GeD7qvCWxa%fBqM!{iYw1x`FG1t}I9gL-`LRJiTFZ`qOuSy$dy-Wx9J0wB ze2C_S=5MKao&ohJ0-bsZX27E?JbBpjH%>*H-H#9~n^iqT_OQiBL|I1EzKMy_&*$9S z$itttWj~ZP_)qKNxG00y#855jEzf!ixJS?@+kxMQxTiMq$-{qY%YO7s zLR4JJ{i~pfks6<1Rk*D8*7OHB97tZHtudJ2Y3uo*#Uq^g;T;fyp)a}Q~T$<|7op#kF`fJ2*legov#_-5^3Iq-kPZ1ThrH@7)EDUbNG zEgL#<5=6|WggK)Y6jCm4`Jv!9m&944+V|hz>Z=dy*v^A|%wDcKh3Ddd zPTVUowmH0hGM?Moia?`_?eVO;!epv}46wRR81BJ3zv^&;WSNos@_Q}@(oFHV>@vr$`CH?K6%zK}U diff --git a/frontend/src/scenes/authentication/PasswordReset.tsx b/frontend/src/scenes/authentication/PasswordReset.tsx index d1ba200bc25cc..d04b22db8bddb 100644 --- a/frontend/src/scenes/authentication/PasswordReset.tsx +++ b/frontend/src/scenes/authentication/PasswordReset.tsx @@ -151,11 +151,7 @@ function ResetThrottled(): JSX.Element { return (

There have been too many reset requests for the email {requestPasswordReset?.email || 'you typed'}. - Please try again later or{' '} - - get in touch - {' '} - if you think this has been a mistake. + Please try again later or contact support if you think this has been a mistake.
Date: Wed, 20 Mar 2024 12:51:21 +0100 Subject: [PATCH 08/17] fix: Throw on password reset errors (#21007) --- frontend/src/scenes/authentication/passwordResetLogic.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/frontend/src/scenes/authentication/passwordResetLogic.ts b/frontend/src/scenes/authentication/passwordResetLogic.ts index a0240f016fa36..854a24a225d48 100644 --- a/frontend/src/scenes/authentication/passwordResetLogic.ts +++ b/frontend/src/scenes/authentication/passwordResetLogic.ts @@ -1,3 +1,4 @@ +import { captureException } from '@sentry/react' import { kea, path, reducers } from 'kea' import { forms } from 'kea-forms' import { loaders } from 'kea-loaders' @@ -69,7 +70,9 @@ export const passwordResetLogic = kea([ try { await api.create('api/reset/', { email }) } catch (e: any) { - actions.setRequestPasswordResetManualErrors(e) + actions.setRequestPasswordResetManualErrors({ email: e.detail ?? 'An error occurred' }) + captureException('Failed to reset password', { extra: { error: e } }) + throw e } }, }, @@ -104,6 +107,7 @@ export const passwordResetLogic = kea([ window.location.href = '/' // We need the refresh } catch (e: any) { actions.setPasswordResetManualErrors({ password: e.detail }) + throw e } }, }, From 0c5b8cb18e68a1271d422adcdab0d62c4b403a51 Mon Sep 17 00:00:00 2001 From: Julian Bez Date: Wed, 20 Mar 2024 12:11:29 +0000 Subject: [PATCH 09/17] fix(insights): Fix set active view when changing insight (#21034) --- .../insights/InsightNav/insightNavLogic.tsx | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx b/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx index d2f28906bfe95..9dff8c87eeb8c 100644 --- a/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx +++ b/frontend/src/scenes/insights/InsightNav/insightNavLogic.tsx @@ -1,4 +1,5 @@ import { actions, afterMount, connect, kea, key, listeners, path, props, reducers, selectors } from 'kea' +import { urlToAction } from 'kea-router' import { FEATURE_FLAGS } from 'lib/constants' import { LemonTag } from 'lib/lemon-ui/LemonTag/LemonTag' import { featureFlagLogic } from 'lib/logic/featureFlagLogic' @@ -41,7 +42,7 @@ import { isStickinessQuery, isTrendsQuery, } from '~/queries/utils' -import { BaseMathType, InsightLogicProps, InsightType } from '~/types' +import { BaseMathType, FilterType, InsightLogicProps, InsightType } from '~/types' import { MathAvailability } from '../filters/ActionFilter/ActionFilterRow/ActionFilterRow' import type { insightNavLogicType } from './insightNavLogicType' @@ -278,6 +279,23 @@ export const insightNavLogic = kea([ } }, })), + urlToAction(({ actions }) => ({ + '/insights/:shortId(/:mode)(/:subscriptionId)': ( + _, // url params + { dashboard, ...searchParams }, // search params + { filters: _filters } // hash params + ) => { + // capture any filters from the URL, either #filters={} or ?insight=X&bla=foo&bar=baz + const filters: Partial | null = + Object.keys(_filters || {}).length > 0 ? _filters : searchParams.insight ? searchParams : null + + if (!filters?.insight) { + return + } + + actions.setActiveView(filters?.insight) + }, + })), afterMount(({ values, actions }) => { if (values.query && isInsightVizNode(values.query)) { actions.updateQueryPropertyCache(cachePropertiesFromQuery(values.query.source, values.queryPropertyCache)) From 235f7c643bb4b33c40d5420ad6794a0cb0c78db0 Mon Sep 17 00:00:00 2001 From: Paul D'Ambra Date: Wed, 20 Mar 2024 12:52:34 +0000 Subject: [PATCH 10/17] fix: padding reset for mobile replay (#21041) * fix: padding reset for mobile replay * fix * updat snapshot --- .../__snapshots__/transform.test.ts.snap | 44 +++++++++++++++++++ .../mobile-replay/transformer/transformers.ts | 1 + 2 files changed, 45 insertions(+) diff --git a/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap b/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap index 889de23db5174..a421f7ff220bf 100644 --- a/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap +++ b/ee/frontend/mobile-replay/__snapshots__/transform.test.ts.snap @@ -55,6 +55,7 @@ exports[`replay/transform transform can convert images 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -214,6 +215,7 @@ exports[`replay/transform transform can convert navigation bar 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -395,6 +397,7 @@ exports[`replay/transform transform can convert rect with text 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -551,6 +554,7 @@ exports[`replay/transform transform can convert status bar 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -718,6 +722,7 @@ exports[`replay/transform transform can ignore unknown wireframe types 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -860,6 +865,7 @@ exports[`replay/transform transform can process unknown types without error 1`] border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -1006,6 +1012,7 @@ exports[`replay/transform transform can set background image to base64 png 1`] = border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -1199,6 +1206,7 @@ exports[`replay/transform transform child wireframes are processed 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -1747,6 +1755,7 @@ exports[`replay/transform transform inputs buttons with nested elements 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -1913,6 +1922,7 @@ exports[`replay/transform transform inputs input - $inputType - hello 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2032,6 +2042,7 @@ exports[`replay/transform transform inputs input - button - click me 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2168,6 +2179,7 @@ exports[`replay/transform transform inputs input - checkbox - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2315,6 +2327,7 @@ exports[`replay/transform transform inputs input - checkbox - $value 2`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2461,6 +2474,7 @@ exports[`replay/transform transform inputs input - checkbox - $value 3`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2609,6 +2623,7 @@ exports[`replay/transform transform inputs input - checkbox - $value 4`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2740,6 +2755,7 @@ exports[`replay/transform transform inputs input - email - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -2871,6 +2887,7 @@ exports[`replay/transform transform inputs input - number - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3002,6 +3019,7 @@ exports[`replay/transform transform inputs input - password - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3133,6 +3151,7 @@ exports[`replay/transform transform inputs input - progress - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3289,6 +3308,7 @@ exports[`replay/transform transform inputs input - progress - $value 2`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3421,6 +3441,7 @@ exports[`replay/transform transform inputs input - progress - 0.75 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3553,6 +3574,7 @@ exports[`replay/transform transform inputs input - progress - 0.75 2`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3685,6 +3707,7 @@ exports[`replay/transform transform inputs input - search - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3816,6 +3839,7 @@ exports[`replay/transform transform inputs input - select - hello 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -3979,6 +4003,7 @@ exports[`replay/transform transform inputs input - tel - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4111,6 +4136,7 @@ exports[`replay/transform transform inputs input - text - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4242,6 +4268,7 @@ exports[`replay/transform transform inputs input - text - hello 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4373,6 +4400,7 @@ exports[`replay/transform transform inputs input - textArea - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4504,6 +4532,7 @@ exports[`replay/transform transform inputs input - textArea - hello 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4635,6 +4664,7 @@ exports[`replay/transform transform inputs input - toggle - $value 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4814,6 +4844,7 @@ exports[`replay/transform transform inputs input - toggle - $value 2`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -4993,6 +5024,7 @@ exports[`replay/transform transform inputs input - toggle - $value 3`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -5172,6 +5204,7 @@ exports[`replay/transform transform inputs input - toggle - $value 4`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -5335,6 +5368,7 @@ exports[`replay/transform transform inputs input - url - https://example.io 1`] border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -5466,6 +5500,7 @@ exports[`replay/transform transform inputs input gets 0 padding by default but c border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -6918,6 +6953,7 @@ exports[`replay/transform transform inputs placeholder - $inputType - $value 1`] border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -7053,6 +7089,7 @@ exports[`replay/transform transform inputs progress rating 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -7686,6 +7723,7 @@ exports[`replay/transform transform inputs radio group - $inputType - $value 1`] border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -7805,6 +7843,7 @@ exports[`replay/transform transform inputs radio_group - $inputType - $value 1`] border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -7934,6 +7973,7 @@ exports[`replay/transform transform inputs radio_group 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -8063,6 +8103,7 @@ exports[`replay/transform transform inputs web_view - $inputType - $value 1`] = border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -8198,6 +8239,7 @@ exports[`replay/transform transform inputs web_view with URL 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -8333,6 +8375,7 @@ exports[`replay/transform transform inputs wrapping with labels 1`] = ` border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; @@ -8480,6 +8523,7 @@ exports[`replay/transform transform omitting x and y is equivalent to setting th border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; diff --git a/ee/frontend/mobile-replay/transformer/transformers.ts b/ee/frontend/mobile-replay/transformer/transformers.ts index 96aea3e36c06e..1527a24d7dbeb 100644 --- a/ee/frontend/mobile-replay/transformer/transformers.ts +++ b/ee/frontend/mobile-replay/transformer/transformers.ts @@ -1383,6 +1383,7 @@ function makeCSSReset(context: ConversionContext): serializedNodeWithId { border: 0; outline: 0; background: transparent; + padding-block: 0 !important; } .input:focus { outline: none; From 4073dc1cf90d162223c11d5ec7f1fccd0a293d73 Mon Sep 17 00:00:00 2001 From: Eric Duong Date: Wed, 20 Mar 2024 10:12:48 -0400 Subject: [PATCH 11/17] chore(data-warehouse): add field type on breakdown logic (#21028) --- posthog/hogql_queries/insights/trends/trends_query_runner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/posthog/hogql_queries/insights/trends/trends_query_runner.py b/posthog/hogql_queries/insights/trends/trends_query_runner.py index b9acb5c37d000..29d29b55e8b0f 100644 --- a/posthog/hogql_queries/insights/trends/trends_query_runner.py +++ b/posthog/hogql_queries/insights/trends/trends_query_runner.py @@ -709,7 +709,7 @@ def _is_breakdown_field_boolean(self): if not table_model: raise ValueError(f"Table {series.table_name} not found") - field_type = dict(table_model.columns)[self.query.breakdownFilter.breakdown] + field_type = dict(table_model.columns)[self.query.breakdownFilter.breakdown]["clickhouse"] if field_type.startswith("Nullable("): field_type = field_type.replace("Nullable(", "")[:-1] From bfe46620b452f22815ff006de8359251cf6a3ee5 Mon Sep 17 00:00:00 2001 From: PostHog Bot <69588470+posthog-bot@users.noreply.github.com> Date: Wed, 20 Mar 2024 11:00:36 -0400 Subject: [PATCH 12/17] chore(deps): Update posthog-js to 1.116.3 (#21047) --- package.json | 2 +- pnpm-lock.yaml | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index 0a268fc208fd5..770be74997198 100644 --- a/package.json +++ b/package.json @@ -144,7 +144,7 @@ "pmtiles": "^2.11.0", "postcss": "^8.4.31", "postcss-preset-env": "^9.3.0", - "posthog-js": "1.116.1", + "posthog-js": "1.116.3", "posthog-js-lite": "2.5.0", "prettier": "^2.8.8", "prop-types": "^15.7.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ef81ba7d4c4d9..5f157ec8b039e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -251,8 +251,8 @@ dependencies: specifier: ^9.3.0 version: 9.3.0(postcss@8.4.31) posthog-js: - specifier: 1.116.1 - version: 1.116.1 + specifier: 1.116.3 + version: 1.116.3 posthog-js-lite: specifier: 2.5.0 version: 2.5.0 @@ -6793,7 +6793,7 @@ packages: '@storybook/csf': 0.1.3 '@storybook/global': 5.0.0 '@storybook/types': 7.6.17 - '@types/qs': 6.9.12 + '@types/qs': 6.9.13 dequal: 2.0.3 lodash: 4.17.21 memoizerific: 1.11.3 @@ -8195,6 +8195,11 @@ packages: /@types/qs@6.9.12: resolution: {integrity: sha512-bZcOkJ6uWrL0Qb2NAWKa7TBU+mJHPzhx9jjLL1KHF+XpzEcR7EXHvjbHlGtR/IsP1vyPrehuS6XqkmaePy//mg==} + dev: false + + /@types/qs@6.9.13: + resolution: {integrity: sha512-iLR+1vTTJ3p0QaOUq6ACbY1mzKTODFDT/XedZI8BksOotFmL4ForwDfRQ/DZeuTHR7/2i4lI1D203gdfxuqTlA==} + dev: true /@types/query-selector-shadow-dom@1.0.0: resolution: {integrity: sha512-cTGo8ZxW0WXFDV7gvL/XCq4213t6S/yWaSGqscnXUTNDWqwnsYKegB/VAzQDwzmACoLzIbGbYXdjJOgfPLu7Ig==} @@ -13625,7 +13630,7 @@ packages: hogan.js: 3.0.2 htm: 3.1.1 instantsearch-ui-components: 0.3.0 - preact: 10.19.6 + preact: 10.20.0 qs: 6.9.7 search-insights: 2.13.0 dev: false @@ -17441,19 +17446,19 @@ packages: resolution: {integrity: sha512-Urvlp0Vu9h3td0BVFWt0QXFJDoOZcaAD83XM9d91NKMKTVPZtfU0ysoxstIf5mw/ce9ZfuMgpWPaagrZI4rmSg==} dev: false - /posthog-js@1.116.1: - resolution: {integrity: sha512-tYKw6K23S3koa2sfX0sylno7jQQ6ET7u1Lw4KqowhciNhS0R5OWTo3HWEJPt64e9IzeWQGcgb9utJIWwrp5D0Q==} + /posthog-js@1.116.3: + resolution: {integrity: sha512-KakGsQ8rS/K/U5Q/tiBrRrFRCgGrR0oI9VSYw9hwNCY00EClwAU3EuykUuQTFdQ1EuYMrZDIMWDD4NW6zgf7wQ==} dependencies: fflate: 0.4.8 - preact: 10.19.6 + preact: 10.20.0 dev: false /potpack@2.0.0: resolution: {integrity: sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==} dev: false - /preact@10.19.6: - resolution: {integrity: sha512-gympg+T2Z1fG1unB8NH29yHJwnEaCH37Z32diPDku316OTnRPeMbiRV9kTrfZpocXjdfnWuFUl/Mj4BHaf6gnw==} + /preact@10.20.0: + resolution: {integrity: sha512-wU7iZw2BjsaKDal3pDRDy/HpPB6cuFOnVUCcw9aIPKG98+ZrXx3F+szkos8BVME5bquyKDKvRlOJFG8kMkcAbg==} dev: false /prelude-ls@1.2.1: From 4ca3c29b5d1dea94d79773cbe2cc76a168ee692d Mon Sep 17 00:00:00 2001 From: Ben White Date: Wed, 20 Mar 2024 16:12:51 +0100 Subject: [PATCH 13/17] feat: Add comma separator support for LemonInputSelect (#21031) * Add comma sepator * feat: Alt-click to edit inputselect items (#21032) --- ...ect--multiple-select-with-custom--dark.png | Bin 1646 -> 1357 bytes ...ct--multiple-select-with-custom--light.png | Bin 1696 -> 1427 bytes .../LemonInputSelect.stories.tsx | 17 +- .../LemonInputSelect/LemonInputSelect.tsx | 145 ++++++++++++------ 4 files changed, 118 insertions(+), 44 deletions(-) diff --git a/frontend/__snapshots__/lemon-ui-lemon-input-select--multiple-select-with-custom--dark.png b/frontend/__snapshots__/lemon-ui-lemon-input-select--multiple-select-with-custom--dark.png index aa1ba92d1702f51e6269e217cc34fde1d097d853..de66482b3ee6e84a942c809cd11d49ae5d6cb709 100644 GIT binary patch delta 1326 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 N002ovPDHLkV1ixPIZ6Nk diff --git a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.stories.tsx b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.stories.tsx index 796d1794b4c89..f7c9212186d1f 100644 --- a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.stories.tsx +++ b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.stories.tsx @@ -54,9 +54,24 @@ MultipleSelect.args = { export const MultipleSelectWithCustom: Story = Template.bind({}) MultipleSelectWithCustom.args = { - placeholder: 'Enter any email...', + placeholder: 'Pick a url...', mode: 'multiple', allowCustomValues: true, + options: [ + { + key: 'http://posthog.com/docs', + label: 'http://posthog.com/docs', + }, + { + key: 'http://posthog.com/pricing', + label: 'http://posthog.com/pricing', + }, + + { + key: 'http://posthog.com/products', + label: 'http://posthog.com/products', + }, + ], } export const Disabled: Story = Template.bind({}) diff --git a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx index 967f18e323753..9e5240a275a68 100644 --- a/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx +++ b/frontend/src/lib/lemon-ui/LemonInputSelect/LemonInputSelect.tsx @@ -1,3 +1,5 @@ +import { Tooltip } from '@posthog/lemon-ui' +import { useKeyHeld } from 'lib/hooks/useKeyHeld' import { LemonSkeleton } from 'lib/lemon-ui/LemonSkeleton' import { LemonSnack } from 'lib/lemon-ui/LemonSnack/LemonSnack' import { range } from 'lib/utils' @@ -49,14 +51,21 @@ export function LemonInputSelect({ const inputRef = useRef(null) const [selectedIndex, setSelectedIndex] = useState(0) const values = value ?? [] + const altKeyHeld = useKeyHeld('Alt') + + const separateOnComma = allowCustomValues && mode === 'multiple' const visibleOptions = useMemo(() => { const res: LemonInputSelectOption[] = [] const customValues = [...values] + // We show the input value if custom values are allowed and it's not in the list + if (allowCustomValues && inputValue && !values.includes(inputValue)) { + customValues.unshift(inputValue) + } + options.forEach((option) => { // Remove from the custom values list if it's in the options - if (customValues.includes(option.key)) { customValues.splice(customValues.indexOf(option.key), 1) } @@ -75,14 +84,8 @@ export function LemonInputSelect({ res.unshift({ key: value, label: value }) }) } - - // Finally we show the input value if custom values are allowed and it's not in the list - if (allowCustomValues && inputValue && !values.includes(inputValue)) { - res.unshift({ key: inputValue, label: inputValue }) - } - return res - }, [options, inputValue, value]) + }, [options, inputValue, values]) // Reset the selected index when the visible options change useEffect(() => { @@ -90,33 +93,69 @@ export function LemonInputSelect({ }, [visibleOptions.length]) const setInputValue = (newValue: string): void => { + // Special case for multiple mode with custom values + if (separateOnComma && newValue.includes(',')) { + const newValues = [...values] + + newValue.split(',').forEach((value) => { + const trimmedValue = value.trim() + if (trimmedValue && !values.includes(trimmedValue)) { + newValues.push(trimmedValue) + } + }) + + onChange?.(newValues) + newValue = '' + } + _setInputValue(newValue) onInputChange?.(inputValue) } - const _onActionItem = (item: string): void => { + const _removeItem = (item: string): void => { let newValues = [...values] - if (values.includes(item)) { - // Remove the item - if (mode === 'single') { - newValues = [] - } else { - newValues.splice(values.indexOf(item), 1) - } + // Remove the item + if (mode === 'single') { + newValues = [] } else { - // Add the item - if (mode === 'single') { - newValues = [item] - } else { + newValues.splice(values.indexOf(item), 1) + } + + onChange?.(newValues) + } + + const _addItem = (item: string): void => { + let newValues = [...values] + // Add the item + if (mode === 'single') { + newValues = [item] + } else { + if (!newValues.includes(item)) { newValues.push(item) } - - setInputValue('') } + setInputValue('') onChange?.(newValues) } + const _onActionItem = (item: string): void => { + if (altKeyHeld && allowCustomValues) { + // In this case we want to remove it if added and set input to it + if (values.includes(item)) { + _removeItem(item) + } + setInputValue(item) + return + } + + if (values.includes(item)) { + _removeItem(item) + } else { + _addItem(item) + } + } + const _onBlur = (): void => { // We need to add a delay as a click could be in the popover or the input wrapper which refocuses setTimeout(() => { @@ -143,8 +182,8 @@ export function LemonInputSelect({ const _onKeyDown = (e: React.KeyboardEvent): void => { if (e.key === 'Enter') { e.preventDefault() - const itemToAdd = visibleOptions[selectedIndex]?.key + if (itemToAdd) { _onActionItem(visibleOptions[selectedIndex]?.key) } @@ -164,33 +203,51 @@ export function LemonInputSelect({ } } - // TRICKY: We don't want the popover to affect the snack buttons - const prefix = ( - - <> - {values.map((value) => { - const option = options.find((option) => option.key === value) ?? { - label: value, - labelComponent: null, - } - return ( - <> - _onActionItem(value)}> + const prefix = useMemo( + () => ( + // TRICKY: We don't want the popover to affect the snack buttons + + <> + {values.map((value) => { + const option = options.find((option) => option.key === value) ?? { + label: value, + labelComponent: null, + } + const snack = ( + _onActionItem(value)} + onClick={allowCustomValues ? () => _onActionItem(value) : undefined} + > {option?.labelComponent ?? option?.label} - - ) - })} - - + ) + return allowCustomValues ? ( + + + click to edit + + } + > + {snack} + + ) : ( + snack + ) + })} + + + ), + [values, options, altKeyHeld, allowCustomValues] ) return ( { popoverFocusRef.current = false setShowPopover(false) @@ -219,7 +276,9 @@ export function LemonInputSelect({ {isHighlighted ? ( {' '} - {!values.includes(option.key) + {altKeyHeld && allowCustomValues + ? 'edit' + : !values.includes(option.key) ? mode === 'single' ? 'select' : 'add' From 0bcf49f997c0ecbda1ac96861ce6a57eaf49b2c3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Oberm=C3=BCller?= Date: Wed, 20 Mar 2024 17:24:01 +0100 Subject: [PATCH 14/17] fix(hogql): properly handle dst transition for funnel conversion window (#21042) --- .../hogql_queries/insights/funnels/base.py | 10 +- .../funnel_correlation_query_runner.py | 2 +- .../insights/funnels/funnel_unordered.py | 6 +- .../test/__snapshots__/test_funnel.ambr | 84 ++--- ...test_funnel_breakdowns_by_current_url.ambr | 8 +- .../test_funnel_correlation.ambr | 326 +++++++++--------- .../test_funnel_correlations_persons.ambr | 26 +- .../__snapshots__/test_funnel_persons.ambr | 18 +- .../__snapshots__/test_funnel_strict.ambr | 30 +- .../test_funnel_strict_persons.ambr | 18 +- .../test_funnel_time_to_convert.ambr | 130 +++---- .../__snapshots__/test_funnel_trends.ambr | 18 +- .../test_funnel_trends_persons.ambr | 18 +- .../__snapshots__/test_funnel_unordered.ambr | 42 +-- .../test_funnel_unordered_persons.ambr | 18 +- .../insights/funnels/test/test_funnel.py | 65 +++- .../funnels/test/test_funnel_unordered.py | 54 +++ 17 files changed, 492 insertions(+), 381 deletions(-) diff --git a/posthog/hogql_queries/insights/funnels/base.py b/posthog/hogql_queries/insights/funnels/base.py index 5d84328d2063d..ef8782fade54a 100644 --- a/posthog/hogql_queries/insights/funnels/base.py +++ b/posthog/hogql_queries/insights/funnels/base.py @@ -830,7 +830,7 @@ def _get_step_times(self, max_steps: int) -> List[ast.Expr]: for i in range(1, max_steps): exprs.append( parse_expr( - f"if(isNotNull(latest_{i}) AND latest_{i} <= latest_{i-1} + INTERVAL {windowInterval} {windowIntervalUnit}, dateDiff('second', latest_{i - 1}, latest_{i}), NULL) step_{i}_conversion_time" + f"if(isNotNull(latest_{i}) AND latest_{i} <= toTimeZone(latest_{i-1}, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, dateDiff('second', latest_{i - 1}, latest_{i}), NULL) step_{i}_conversion_time" ), ) @@ -959,7 +959,7 @@ def _get_exclusion_condition(self) -> List[ast.Expr]: to_time = f"latest_{exclusion.funnelToStep}" exclusion_time = f"exclusion_{exclusion_id}_latest_{exclusion.funnelFromStep}" condition = parse_expr( - f"if( {exclusion_time} > {from_time} AND {exclusion_time} < if(isNull({to_time}), {from_time} + INTERVAL {windowInterval} {windowIntervalUnit}, {to_time}), 1, 0)" + f"if( {exclusion_time} > {from_time} AND {exclusion_time} < if(isNull({to_time}), toTimeZone({from_time}, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, {to_time}), 1, 0)" ) conditions.append(condition) @@ -988,7 +988,11 @@ def _get_sorting_condition(self, curr_index: int, max_steps: int) -> ast.Expr: duplicate_event = is_equal(series[i], series[i - 1]) or is_superset(series[i], series[i - 1]) conditions.append(parse_expr(f"latest_{i - 1} {'<' if duplicate_event else '<='} latest_{i}")) - conditions.append(parse_expr(f"latest_{i} <= latest_0 + INTERVAL {windowInterval} {windowIntervalUnit}")) + conditions.append( + parse_expr( + f"latest_{i} <= toTimeZone(latest_0, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}" + ) + ) return ast.Call( name="if", diff --git a/posthog/hogql_queries/insights/funnels/funnel_correlation_query_runner.py b/posthog/hogql_queries/insights/funnels/funnel_correlation_query_runner.py index 945a1b7da5cce..2fef78a372324 100644 --- a/posthog/hogql_queries/insights/funnels/funnel_correlation_query_runner.py +++ b/posthog/hogql_queries/insights/funnels/funnel_correlation_query_runner.py @@ -761,7 +761,7 @@ def _get_events_join_query(self) -> str: AND toTimeZone(toDateTime(event.timestamp), 'UTC') > funnel_actors.first_timestamp AND toTimeZone(toDateTime(event.timestamp), 'UTC') < coalesce( funnel_actors.final_timestamp, - funnel_actors.first_timestamp + INTERVAL {windowInterval} {windowIntervalUnit}, + toTimeZone(funnel_actors.first_timestamp, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, date_to) -- Ensure that the event is not outside the bounds of the funnel conversion window diff --git a/posthog/hogql_queries/insights/funnels/funnel_unordered.py b/posthog/hogql_queries/insights/funnels/funnel_unordered.py index 09fc322621eac..af3ed18d4f82e 100644 --- a/posthog/hogql_queries/insights/funnels/funnel_unordered.py +++ b/posthog/hogql_queries/insights/funnels/funnel_unordered.py @@ -168,7 +168,7 @@ def _get_step_times(self, max_steps: int) -> List[ast.Expr]: for i in range(1, max_steps): exprs.append( parse_expr( - f"if(isNotNull(conversion_times[{i+1}]) AND conversion_times[{i+1}] <= conversion_times[{i}] + INTERVAL {windowInterval} {windowIntervalUnit}, dateDiff('second', conversion_times[{i}], conversion_times[{i+1}]), NULL) step_{i}_conversion_time" + f"if(isNotNull(conversion_times[{i+1}]) AND conversion_times[{i+1}] <= toTimeZone(conversion_times[{i}], 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, dateDiff('second', conversion_times[{i}], conversion_times[{i+1}]), NULL) step_{i}_conversion_time" ) ) # array indices in ClickHouse are 1-based :shrug: @@ -190,7 +190,7 @@ def get_sorting_condition(self, max_steps: int) -> List[ast.Expr]: basic_conditions: List[str] = [] for i in range(1, max_steps): basic_conditions.append( - f"if(latest_0 < latest_{i} AND latest_{i} <= latest_0 + INTERVAL {windowInterval} {windowIntervalUnit}, 1, 0)" + f"if(latest_0 < latest_{i} AND latest_{i} <= toTimeZone(latest_0, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, 1, 0)" ) if basic_conditions: @@ -214,7 +214,7 @@ def _get_exclusion_condition(self) -> List[ast.Expr]: to_time = f"event_times[{exclusion.funnelToStep + 1}]" exclusion_time = f"exclusion_{exclusion_id}_latest_{exclusion.funnelFromStep}" condition = parse_expr( - f"if( {exclusion_time} > {from_time} AND {exclusion_time} < if(isNull({to_time}), {from_time} + INTERVAL {windowInterval} {windowIntervalUnit}, {to_time}), 1, 0)" + f"if( {exclusion_time} > {from_time} AND {exclusion_time} < if(isNull({to_time}), toTimeZone({from_time}, 'UTC') + INTERVAL {windowInterval} {windowIntervalUnit}, {to_time}), 1, 0)" ) conditions.append(condition) diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr index 29a1483cafc1c..8379672352c1c 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel.ambr @@ -30,9 +30,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalSecond(15))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalSecond(15))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalSecond(15))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -128,9 +128,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalSecond(15))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalSecond(15))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalSecond(15))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalSecond(15))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalSecond(15))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -238,9 +238,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -341,8 +341,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -413,9 +413,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -523,9 +523,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -637,9 +637,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -751,9 +751,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -860,8 +860,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -922,8 +922,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1009,8 +1009,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -1115,8 +1115,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -1228,8 +1228,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -1343,9 +1343,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -1496,9 +1496,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -1650,9 +1650,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr index 656a22a2aff51..a50033fda2f7f 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_breakdowns_by_current_url.ambr @@ -50,8 +50,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -156,8 +156,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr index 5019f0f57f7b7..b5e3e020c1ec0 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlation.ambr @@ -41,8 +41,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -74,7 +74,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(event__pdi.person_id, funnel_actors.actor_id) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) GROUP BY name LIMIT 100 UNION ALL @@ -110,8 +110,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -186,8 +186,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -268,8 +268,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -362,8 +362,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -505,8 +505,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -648,8 +648,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -791,8 +791,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -918,8 +918,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -944,7 +944,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_1`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), in(event.event, ['positively_related', 'negatively_related']))) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), in(event.event, ['positively_related', 'negatively_related']))) GROUP BY name HAVING and(ifNull(greater(plus(success_count, failure_count), 2), 0), ifNull(notIn((prop).1, []), 0)) LIMIT 100 @@ -981,8 +981,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1052,8 +1052,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1078,7 +1078,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_1`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), in(event.event, ['positively_related', 'negatively_related']))) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), in(event.event, ['positively_related', 'negatively_related']))) GROUP BY name HAVING and(ifNull(greater(plus(success_count, failure_count), 2), 0), ifNull(notIn((prop).1, []), 0)) LIMIT 100 @@ -1115,8 +1115,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1181,8 +1181,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1207,7 +1207,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) GROUP BY name LIMIT 100 UNION ALL @@ -1243,8 +1243,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1330,8 +1330,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -1375,7 +1375,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -1441,8 +1441,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -1486,7 +1486,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -1552,8 +1552,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -1597,7 +1597,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -1663,8 +1663,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -1708,7 +1708,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -1753,8 +1753,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1787,7 +1787,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) GROUP BY name LIMIT 100 UNION ALL @@ -1823,8 +1823,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1918,8 +1918,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -1963,7 +1963,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2029,8 +2029,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2074,7 +2074,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2119,8 +2119,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2145,7 +2145,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) GROUP BY name LIMIT 100 UNION ALL @@ -2181,8 +2181,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2268,8 +2268,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2313,7 +2313,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2379,8 +2379,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2424,7 +2424,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'positively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2490,8 +2490,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2535,7 +2535,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2601,8 +2601,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2646,7 +2646,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2691,8 +2691,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2725,7 +2725,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), notIn(event.event, [])) GROUP BY name LIMIT 100 UNION ALL @@ -2761,8 +2761,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -2856,8 +2856,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -2901,7 +2901,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -2967,8 +2967,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -3012,7 +3012,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(funnel_actors.actor_id, event.`$group_0`) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2020-01-14 23:59:59', 6, 'UTC')))), notIn(event.event, ['paid', 'user signed up']), equals(event.event, 'negatively_related'), ifNull(notEquals(funnel_actors.steps, 2), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(groups.key, source.actor_id) ORDER BY source.actor_id ASC @@ -3060,8 +3060,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3134,8 +3134,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3220,8 +3220,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -3338,8 +3338,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -3456,8 +3456,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -3574,8 +3574,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -3675,8 +3675,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3749,8 +3749,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3818,8 +3818,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3892,8 +3892,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -3978,8 +3978,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4096,8 +4096,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4214,8 +4214,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4332,8 +4332,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4433,8 +4433,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -4507,8 +4507,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -4576,8 +4576,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -4650,8 +4650,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -4736,8 +4736,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4854,8 +4854,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -4972,8 +4972,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5090,8 +5090,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5191,8 +5191,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -5265,8 +5265,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -5334,8 +5334,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -5408,8 +5408,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -5494,8 +5494,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5612,8 +5612,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5730,8 +5730,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5848,8 +5848,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -5949,8 +5949,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -6023,8 +6023,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -6092,8 +6092,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -6166,8 +6166,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -6252,8 +6252,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -6370,8 +6370,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -6488,8 +6488,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -6606,8 +6606,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -6707,8 +6707,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -6781,8 +6781,8 @@ latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlations_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlations_persons.ambr index fe7404ea9b7a1..62715e7d96b63 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlations_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_correlations_persons.ambr @@ -63,8 +63,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -115,7 +115,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(event__pdi.person_id, funnel_actors.actor_id) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC')))), notIn(event.event, ['$pageview', 'insight analyzed']), equals(event.event, 'insight loaded'), ifNull(equals(funnel_actors.steps, 2), 0)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC')))), notIn(event.event, ['$pageview', 'insight analyzed']), equals(event.event, 'insight loaded'), ifNull(equals(funnel_actors.steps, 2), 0)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(persons.id, source.actor_id) ORDER BY persons.id ASC @@ -213,9 +213,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -321,7 +321,7 @@ and isNull(max_steps))) WHERE ifNull(in(steps, [1, 2, 3]), 0) ORDER BY aggregation_target ASC) AS funnel_actors ON equals(event__pdi.person_id, funnel_actors.actor_id) - WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(funnel_actors.first_timestamp, toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC')))), notIn(event.event, ['$pageview', 'insight analyzed', 'insight updated']), equals(event.event, 'insight loaded'), ifNull(notEquals(funnel_actors.steps, 3), 1)) + WHERE and(equals(event.team_id, 2), greaterOrEquals(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-01 00:00:00', 6, 'UTC'))), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC'))), equals(event.team_id, 2), greater(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), funnel_actors.first_timestamp), less(toTimeZone(toDateTime(toTimeZone(event.timestamp, 'UTC'), 'UTC'), 'UTC'), coalesce(funnel_actors.final_timestamp, plus(toTimeZone(funnel_actors.first_timestamp, 'UTC'), toIntervalDay(14)), assumeNotNull(parseDateTime64BestEffortOrNull('2021-01-08 23:59:59', 6, 'UTC')))), notIn(event.event, ['$pageview', 'insight analyzed', 'insight updated']), equals(event.event, 'insight loaded'), ifNull(notEquals(funnel_actors.steps, 3), 1)) GROUP BY actor_id ORDER BY actor_id ASC) AS source ON equals(persons.id, source.actor_id) ORDER BY persons.id ASC @@ -401,8 +401,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -544,8 +544,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event @@ -687,8 +687,8 @@ uuid_1 AS uuid_1, `$session_id_1` AS `$session_id_1`, `$window_id_1` AS `$window_id_1`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, if(isNull(latest_0), tuple(NULL, NULL, NULL, NULL), if(isNull(latest_1), step_0_matching_event, step_1_matching_event)) AS final_matching_event diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr index a9a6fc5357f72..723165d9d8320 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_persons.ambr @@ -52,9 +52,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -234,9 +234,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -416,9 +416,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr index 5b12c1d8d00e0..927a924d8a884 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict.ambr @@ -50,8 +50,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -155,8 +155,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -267,8 +267,8 @@ step_1 AS step_1, latest_1 AS latest_1, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -381,9 +381,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -534,9 +534,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -688,9 +688,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr index 0b2f21460022e..1496e07c65e02 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_strict_persons.ambr @@ -52,9 +52,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -194,9 +194,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -336,9 +336,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr index aae3049d382ec..2463fd084116f 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_time_to_convert.ambr @@ -35,9 +35,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -102,7 +102,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -135,7 +135,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -168,7 +168,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -201,7 +201,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -249,9 +249,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -316,7 +316,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -349,7 +349,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -383,7 +383,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(7))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_2 @@ -450,9 +450,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -498,7 +498,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -526,7 +526,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -554,7 +554,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -582,7 +582,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -625,9 +625,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -673,7 +673,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -701,7 +701,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -730,7 +730,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN 1 PRECEDING AND 1 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -793,10 +793,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -836,10 +836,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -879,10 +879,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -928,7 +928,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -944,7 +944,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -960,7 +960,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -988,7 +988,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1004,7 +1004,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1020,7 +1020,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1048,7 +1048,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1064,7 +1064,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1080,7 +1080,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1108,7 +1108,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1124,7 +1124,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1140,7 +1140,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1184,10 +1184,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1227,10 +1227,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1270,10 +1270,10 @@ step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -1319,7 +1319,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1335,7 +1335,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1351,7 +1351,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1379,7 +1379,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1395,7 +1395,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1411,7 +1411,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1440,7 +1440,7 @@ FROM (SELECT aggregation_target AS aggregation_target, steps AS steps, max(steps) OVER (PARTITION BY aggregation_target) AS max_steps, step_1_conversion_time AS step_1_conversion_time, step_2_conversion_time AS step_2_conversion_time FROM - (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1456,7 +1456,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target @@ -1472,7 +1472,7 @@ HAVING ifNull(equals(argMax(person_distinct_id2.is_deleted, person_distinct_id2.version), 0), 0)) AS e__pdi ON equals(e.distinct_id, e__pdi.distinct_id) WHERE and(equals(e.team_id, 2), and(and(greaterOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-07 00:00:00.000000', 6, 'UTC')), lessOrEquals(toTimeZone(e.timestamp, 'UTC'), toDateTime64('2021-06-13 23:59:59.999999', 6, 'UTC'))), in(e.event, tuple('step one', 'step three', 'step two'))), or(ifNull(equals(step_0, 1), 0), ifNull(equals(step_1, 1), 0), ifNull(equals(step_2, 1), 0))))) WHERE ifNull(equals(step_0, 1), 0) - UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + UNION ALL SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, arraySort([latest_0, latest_1, latest_2]) AS event_times, arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, step_0 AS step_0, latest_0 AS latest_0, step_1 AS step_1, min(latest_1) OVER (PARTITION BY aggregation_target ORDER BY timestamp DESC ROWS BETWEEN UNBOUNDED PRECEDING AND 0 PRECEDING) AS latest_1, step_2 AS step_2, min(latest_2) OVER (PARTITION BY aggregation_target diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr index 5fa91548e3037..c86902d5df182 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends.ambr @@ -22,9 +22,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -109,9 +109,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -196,9 +196,9 @@ latest_1 AS latest_1, step_2 AS step_2, latest_2 AS latest_2, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_persons.ambr index 9dcb793d30329..32518bdbf9bbc 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_trends_persons.ambr @@ -39,9 +39,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -207,9 +207,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, @@ -375,9 +375,9 @@ uuid_2 AS uuid_2, `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, tuple(latest_0, uuid_0, `$session_id_0`, `$window_id_0`) AS step_0_matching_event, tuple(latest_1, uuid_1, `$session_id_1`, `$window_id_1`) AS step_1_matching_event, tuple(latest_2, uuid_2, `$session_id_2`, `$window_id_2`) AS step_2_matching_event, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr index 214583b03f081..52269964088e1 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered.ambr @@ -49,9 +49,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -101,9 +101,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -206,9 +206,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -265,9 +265,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -377,9 +377,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -429,9 +429,9 @@ latest_1 AS latest_1, prop AS prop, arraySort([latest_0, latest_1]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -544,9 +544,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -697,9 +697,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, @@ -851,9 +851,9 @@ step_2 AS step_2, latest_2 AS latest_2, prop AS prop, - if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 2, 1)) AS steps, - if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, - if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(latest_1, toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, + if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0), ifNull(lessOrEquals(latest_1, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 3, if(and(ifNull(lessOrEquals(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 2, 1)) AS steps, + if(and(isNotNull(latest_1), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time, + if(and(isNotNull(latest_2), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_1, 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', latest_1, latest_2), NULL) AS step_2_conversion_time, prop AS prop FROM (SELECT aggregation_target AS aggregation_target, diff --git a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr index 6f308a54388e3..5ced3fb2b9766 100644 --- a/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr +++ b/posthog/hogql_queries/insights/funnels/test/__snapshots__/test_funnel_unordered_persons.ambr @@ -45,10 +45,10 @@ `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -122,10 +122,10 @@ `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, @@ -199,10 +199,10 @@ `$session_id_2` AS `$session_id_2`, `$window_id_2` AS `$window_id_2`, arraySort([latest_0, latest_1, latest_2]) AS event_times, - arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(latest_0, toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, + arraySum([if(and(ifNull(less(latest_0, latest_1), 0), ifNull(lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), if(and(ifNull(less(latest_0, latest_2), 0), ifNull(lessOrEquals(latest_2, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14))), 0)), 1, 0), 1]) AS steps, arraySort([latest_0, latest_1, latest_2]) AS conversion_times, - if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(conversion_times[1], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, - if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(conversion_times[2], toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time + if(and(isNotNull(conversion_times[2]), ifNull(lessOrEquals(conversion_times[2], plus(toTimeZone(conversion_times[1], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[1], conversion_times[2]), NULL) AS step_1_conversion_time, + if(and(isNotNull(conversion_times[3]), ifNull(lessOrEquals(conversion_times[3], plus(toTimeZone(conversion_times[2], 'UTC'), toIntervalDay(14))), 0)), dateDiff('second', conversion_times[2], conversion_times[3]), NULL) AS step_2_conversion_time FROM (SELECT aggregation_target AS aggregation_target, timestamp AS timestamp, diff --git a/posthog/hogql_queries/insights/funnels/test/test_funnel.py b/posthog/hogql_queries/insights/funnels/test/test_funnel.py index d132927863542..98f4d060fb905 100644 --- a/posthog/hogql_queries/insights/funnels/test/test_funnel.py +++ b/posthog/hogql_queries/insights/funnels/test/test_funnel.py @@ -3523,6 +3523,59 @@ def test_funnel_aggregation_with_groups_with_cohort_filtering(self): self.assertEqual(results[1]["name"], "paid") self.assertEqual(results[1]["count"], 1) + def test_funnel_window_ignores_dst_transition(self): + _create_person( + distinct_ids=[f"user_1"], + team=self.team, + ) + + events_by_person = { + "user_1": [ + { + "event": "$pageview", + "timestamp": datetime(2024, 3, 1, 15, 10), # 1st March 15:10 + }, + { + "event": "user signed up", + "timestamp": datetime( + 2024, 3, 15, 14, 27 + ), # 15th March 14:27 (within 14 day conversion window that ends at 15:10) + }, + ], + } + journeys_for(events_by_person, self.team) + + filters = { + "events": [ + {"id": "$pageview", "type": "events", "order": 0}, + {"id": "user signed up", "type": "events", "order": 1}, + ], + "insight": INSIGHT_FUNNELS, + "date_from": "2024-02-17", + "date_to": "2024-03-18", + } + + query = cast(FunnelsQuery, filter_to_query(filters)) + results = FunnelsQueryRunner(query=query, team=self.team).calculate().results + + self.assertEqual(results[1]["name"], "user signed up") + self.assertEqual(results[1]["count"], 1) + self.assertEqual(results[1]["average_conversion_time"], 1_207_020) + self.assertEqual(results[1]["median_conversion_time"], 1_207_020) + + # there is a PST -> PDT transition on 10th of March + self.team.timezone = "US/Pacific" + self.team.save() + + query = cast(FunnelsQuery, filter_to_query(filters)) + results = FunnelsQueryRunner(query=query, team=self.team).calculate().results + + # we still should have the user here, as the conversion window should not be affected by DST + self.assertEqual(results[1]["name"], "user signed up") + self.assertEqual(results[1]["count"], 1) + self.assertEqual(results[1]["average_conversion_time"], 1_207_020) + self.assertEqual(results[1]["median_conversion_time"], 1_207_020) + return TestGetFunnel @@ -3550,8 +3603,8 @@ def test_smoke(self): latest_0, step_1, latest_1, - if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), 2, 1) AS steps, - if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), 2, 1) AS steps, + if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target, @@ -3610,8 +3663,8 @@ def test_smoke(self): latest_0, step_1, latest_1, - if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), 2, 1) AS steps, - if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), 2, 1) AS steps, + if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target, @@ -3681,8 +3734,8 @@ def test_smoke(self): latest_0, step_1, latest_1, - if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), 2, 1) AS steps, - if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(latest_0, toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time + if(and(less(latest_0, latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), 2, 1) AS steps, + if(and(isNotNull(latest_1), lessOrEquals(latest_1, plus(toTimeZone(latest_0, 'UTC'), toIntervalDay(14)))), dateDiff('second', latest_0, latest_1), NULL) AS step_1_conversion_time FROM (SELECT aggregation_target, diff --git a/posthog/hogql_queries/insights/funnels/test/test_funnel_unordered.py b/posthog/hogql_queries/insights/funnels/test/test_funnel_unordered.py index 36e5d87f39e49..49e495e69222d 100644 --- a/posthog/hogql_queries/insights/funnels/test/test_funnel_unordered.py +++ b/posthog/hogql_queries/insights/funnels/test/test_funnel_unordered.py @@ -1620,3 +1620,57 @@ def test_funnel_unordered_entity_filters(self): self.assertEqual(results[0]["count"], 1) self.assertEqual(results[1]["count"], 1) + + def test_funnel_window_ignores_dst_transition(self): + _create_person( + distinct_ids=[f"user_1"], + team=self.team, + ) + + events_by_person = { + "user_1": [ + { + "event": "$pageview", + "timestamp": datetime(2024, 3, 1, 15, 10), # 1st March 15:10 + }, + { + "event": "user signed up", + "timestamp": datetime( + 2024, 3, 15, 14, 27 + ), # 15th March 14:27 (within 14 day conversion window that ends at 15:10) + }, + ], + } + journeys_for(events_by_person, self.team) + + filters = { + "events": [ + {"id": "$pageview", "type": "events", "order": 0}, + {"id": "user signed up", "type": "events", "order": 1}, + ], + "insight": INSIGHT_FUNNELS, + "funnel_order_type": "unordered", + "date_from": "2024-02-17", + "date_to": "2024-03-18", + } + + query = cast(FunnelsQuery, filter_to_query(filters)) + results = FunnelsQueryRunner(query=query, team=self.team).calculate().results + + self.assertEqual(results[1]["name"], "Completed 2 steps") + self.assertEqual(results[1]["count"], 1) + self.assertEqual(results[1]["average_conversion_time"], 1_207_020) + self.assertEqual(results[1]["median_conversion_time"], 1_207_020) + + # there is a PST -> PDT transition on 10th of March + self.team.timezone = "US/Pacific" + self.team.save() + + query = cast(FunnelsQuery, filter_to_query(filters)) + results = FunnelsQueryRunner(query=query, team=self.team).calculate().results + + # we still should have the user here, as the conversion window should not be affected by DST + self.assertEqual(results[1]["name"], "Completed 2 steps") + self.assertEqual(results[1]["count"], 1) + self.assertEqual(results[1]["average_conversion_time"], 1_207_020) + self.assertEqual(results[1]["median_conversion_time"], 1_207_020) From 590ef1cbd1ec49836c5d61efc707cbdb1a0b21b2 Mon Sep 17 00:00:00 2001 From: Bianca Yang <21014901+xrdt@users.noreply.github.com> Date: Wed, 20 Mar 2024 09:44:04 -0700 Subject: [PATCH 15/17] fix: Swap out password reset token generator (#21026) swap out token generator --- posthog/admin/admins/user_admin.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/posthog/admin/admins/user_admin.py b/posthog/admin/admins/user_admin.py index c1129ef334fa4..9872fad946f30 100644 --- a/posthog/admin/admins/user_admin.py +++ b/posthog/admin/admins/user_admin.py @@ -1,12 +1,11 @@ -from django.utils.html import format_html - from django.contrib.auth.admin import UserAdmin as DjangoUserAdmin from django.contrib.auth.forms import UserChangeForm as DjangoUserChangeForm -from django.contrib.auth.tokens import default_token_generator +from django.utils.html import format_html from django.utils.translation import gettext_lazy as _ from posthog.admin.inlines.organization_member_inline import OrganizationMemberInline from posthog.admin.inlines.totp_device_inline import TOTPDeviceInline +from posthog.api.authentication import password_reset_token_generator from posthog.models import User @@ -18,7 +17,7 @@ def __init__(self, *args, **kwargs): # we have a link to the password reset page which the _user_ can use themselves. # This way if some user needs to reset their password and there's a problem with receiving the reset link email, # an admin can provide that reset link manually – much better than sending a new password in plain text. - password_reset_token = default_token_generator.make_token(self.instance) + password_reset_token = password_reset_token_generator.make_token(self.instance) self.fields["password"].help_text = ( "Raw passwords are not stored, so there is no way to see this user’s password, but you can send them " f'this password reset link ' From a22c8a6e505430abbad7e57a72b6ac84d4f47c7b Mon Sep 17 00:00:00 2001 From: Frank Hamand Date: Wed, 20 Mar 2024 17:11:39 +0000 Subject: [PATCH 16/17] fix: Revert healthcheck change (#21054) Revert healthcheck change We suspect this is causing pods to come up before they are ready to serve traffic --- unit.json | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/unit.json b/unit.json index 72e3d2f03edb6..472189f0c2880 100644 --- a/unit.json +++ b/unit.json @@ -17,14 +17,6 @@ }, "routes": { "posthog": [ - { - "match": { - "uri": ["/_health", "/_readyz", "/_livez"] - }, - "action": { - "pass": "applications/posthog-health" - } - }, { "action": { "pass": "applications/posthog" @@ -53,17 +45,6 @@ ] }, "applications": { - "posthog-health": { - "type": "python 3.10", - "processes": 1, - "working_directory": "/code", - "path": ".", - "module": "posthog.wsgi", - "user": "nobody", - "limits": { - "requests": 5000 - } - }, "posthog": { "type": "python 3.10", "processes": 4, From 921ee5a7253aa16ed5a4daea7f57773b0fbc100a Mon Sep 17 00:00:00 2001 From: Zach Waterfield Date: Wed, 20 Mar 2024 12:54:05 -0600 Subject: [PATCH 17/17] feat: add reverse proxy step to quick start steps + more quick start events (#20939) * Add reverse proxy to the quick start list * Add sidebar task skipped event * Add sidebar open/close events * Update quick start proxy copy * Move the sidebar closed call * Put reportActivationSideBarShown import back * Remove eventUsageLogic calls * remove eventUserLogic usage * spelling fix * Switch sidebar events * update task skipped key * Update UI snapshots for `chromium` (2) * Update UI snapshots for `chromium` (2) * rename sidebar events --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .../panels/activation/SidePanelActivation.tsx | 3 +++ .../panels/activation/activationLogic.ts | 19 ++++++++++++++++--- .../sidepanel/sidePanelStateLogic.tsx | 5 ++++- frontend/src/lib/utils/eventUsageLogic.ts | 12 ------------ 4 files changed, 23 insertions(+), 16 deletions(-) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx index c78aa76f0faec..5aef55b51a523 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/SidePanelActivation.tsx @@ -97,6 +97,9 @@ const ActivationTask = ({ if (url) { params.to = url params.targetBlank = true + params.onClick = () => { + reportActivationSideBarTaskClicked(id) + } } else { params.onClick = () => { runTask(id) diff --git a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/activationLogic.ts b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/activationLogic.ts index 7a182c47ba2b1..e8afd00aea1ed 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/panels/activation/activationLogic.ts +++ b/frontend/src/layout/navigation-3000/sidepanel/panels/activation/activationLogic.ts @@ -2,8 +2,8 @@ import { actions, connect, events, kea, listeners, path, reducers, selectors } f import { loaders } from 'kea-loaders' import { router } from 'kea-router' import api from 'lib/api' -import { eventUsageLogic } from 'lib/utils/eventUsageLogic' import { permanentlyMount } from 'lib/utils/kea-logic-builders' +import posthog from 'posthog-js' import { membersLogic } from 'scenes/organization/membersLogic' import { pluginsLogic } from 'scenes/plugins/pluginsLogic' import { savedInsightsLogic } from 'scenes/saved-insights/savedInsightsLogic' @@ -25,6 +25,7 @@ export enum ActivationTasks { SetupSessionRecordings = 'setup_session_recordings', TrackCustomEvents = 'track_custom_events', InstallFirstApp = 'install_first_app', + SetUpReverseProxy = 'set_up_reverse_proxy', } export type ActivationTaskType = { @@ -65,8 +66,6 @@ export const activationLogic = kea([ ['loadPluginsSuccess', 'loadPluginsFailure'], sidePanelStateLogic, ['openSidePanel'], - eventUsageLogic, - ['reportActivationSideBarShown'], savedInsightsLogic, ['loadInsights', 'loadInsightsSuccess', 'loadInsightsFailure'], dashboardsModel, @@ -282,6 +281,17 @@ export const activationLogic = kea([ skipped: skippedTasks.includes(ActivationTasks.InstallFirstApp), }) break + case ActivationTasks.SetUpReverseProxy: + tasks.push({ + id: ActivationTasks.SetUpReverseProxy, + name: 'Set up a reverse proxy', + description: 'Send your events from your own domain to avoid tracking blockers', + completed: false, + canSkip: true, + skipped: skippedTasks.includes(ActivationTasks.SetUpReverseProxy), + url: 'https://posthog.com/docs/advanced/proxy', + }) + break default: break } @@ -342,6 +352,9 @@ export const activationLogic = kea([ } }, skipTask: ({ id }) => { + posthog.capture('activation sidebar task skipped', { + task: id, + }) if (values.currentTeam?.id) { actions.addSkippedTask(values.currentTeam.id, id) } diff --git a/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx index 6d1360cdf90ac..e4188d68842c7 100644 --- a/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx +++ b/frontend/src/layout/navigation-3000/sidepanel/sidePanelStateLogic.tsx @@ -1,6 +1,7 @@ import { actions, kea, listeners, path, reducers } from 'kea' import { actionToUrl, router, urlToAction } from 'kea-router' import { windowValues } from 'kea-window-values' +import posthog from 'posthog-js' import { SidePanelTab } from '~/types' @@ -55,10 +56,12 @@ export const sidePanelStateLogic = kea([ listeners(({ actions, values }) => ({ // NOTE: We explicitly reference the actions instead of connecting so that people don't accidentally // use this logic instead of sidePanelStateLogic - openSidePanel: () => { + openSidePanel: ({ tab }) => { + posthog.capture('sidebar opened', { tab }) actions.setSidePanelOpen(true) }, closeSidePanel: ({ tab }) => { + posthog.capture('sidebar closed', { tab }) if (!tab) { // If we aren't specifiying the tab we always close actions.setSidePanelOpen(false) diff --git a/frontend/src/lib/utils/eventUsageLogic.ts b/frontend/src/lib/utils/eventUsageLogic.ts index e88617ddd7bad..069d1758d0e56 100644 --- a/frontend/src/lib/utils/eventUsageLogic.ts +++ b/frontend/src/lib/utils/eventUsageLogic.ts @@ -457,11 +457,6 @@ export const eventUsageLogic = kea([ reportInstanceSettingChange: (name: string, value: string | boolean | number) => ({ name, value }), reportAxisUnitsChanged: (properties: Record) => ({ ...properties }), reportTeamSettingChange: (name: string, value: any) => ({ name, value }), - reportActivationSideBarShown: ( - activeTasksCount: number, - completedTasksCount: number, - completionPercent: number - ) => ({ activeTasksCount, completedTasksCount, completionPercent }), reportActivationSideBarTaskClicked: (key: string) => ({ key }), reportBillingUpgradeClicked: (plan: string) => ({ plan }), reportRoleCreated: (role: string) => ({ role }), @@ -1092,13 +1087,6 @@ export const eventUsageLogic = kea([ value, }) }, - reportActivationSideBarShown: ({ activeTasksCount, completedTasksCount, completionPercent }) => { - posthog.capture('activation sidebar shown', { - active_tasks_count: activeTasksCount, - completed_tasks_count: completedTasksCount, - completion_percent_count: completionPercent, - }) - }, reportActivationSideBarTaskClicked: ({ key }) => { posthog.capture('activation sidebar task clicked', { key,