From 7244ee2db4fc3291aaa0bfeaa4e2beed39ba584b Mon Sep 17 00:00:00 2001 From: eric Date: Tue, 26 Mar 2024 14:42:15 -0400 Subject: [PATCH] make separate property type in popup --- .../components/TaxonomicPropertyFilter.tsx | 4 +- .../taxonomicPropertyFilterLogic.ts | 12 +++-- .../lib/components/PropertyFilters/utils.ts | 4 ++ .../TaxonomicFilter/InfiniteList.tsx | 4 +- .../TaxonomicFilter/taxonomicFilterLogic.tsx | 13 ++++++ .../lib/components/TaxonomicFilter/types.ts | 1 + .../nodes/InsightViz/GlobalAndOrFilters.tsx | 1 + frontend/src/queries/schema.json | 3 ++ .../external/dataWarehouseJoinsLogic.ts | 44 ++++++++++++++++++- frontend/src/types.ts | 4 ++ plugin-server/src/types.ts | 1 + posthog/hogql/property.py | 5 ++- posthog/models/property/property.py | 3 ++ posthog/schema.py | 1 + 14 files changed, 88 insertions(+), 12 deletions(-) diff --git a/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx b/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx index d416c1e0502c3..acfcb0c8d7566 100644 --- a/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx +++ b/frontend/src/lib/components/PropertyFilters/components/TaxonomicPropertyFilter.tsx @@ -9,6 +9,7 @@ import { propertyFilterLogic } from 'lib/components/PropertyFilters/propertyFilt import { PropertyFilterInternalProps } from 'lib/components/PropertyFilters/types' import { isGroupPropertyFilter, + isPersonPropertyFilter, isPropertyFilterWithOperator, PROPERTY_FILTER_TYPE_TO_TAXONOMIC_FILTER_GROUP_TYPE, propertyFilterTypeToTaxonomicFilterType, @@ -63,7 +64,7 @@ export function TaxonomicPropertyFilter({ value, item ) => { - selectItem(taxonomicGroup, value, item?.propertyFilterType) + selectItem(taxonomicGroup, value, item) if ( taxonomicGroup.type === TaxonomicFilterGroupType.Cohorts || taxonomicGroup.type === TaxonomicFilterGroupType.HogQLExpression @@ -217,6 +218,7 @@ export function TaxonomicPropertyFilter({ ...(isGroupPropertyFilter(filter) ? { group_type_index: filter.group_type_index } : {}), + ...(isPersonPropertyFilter(filter) ? { table: filter?.table } : {}), } as AnyPropertyFilter) } if ( diff --git a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts index aa1a1ca685cc7..283e74a76b223 100644 --- a/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts +++ b/frontend/src/lib/components/PropertyFilters/components/taxonomicPropertyFilterLogic.ts @@ -51,14 +51,10 @@ export const taxonomicPropertyFilterLogic = kea ({ + selectItem: (taxonomicGroup: TaxonomicFilterGroup, propertyKey?: TaxonomicFilterValue, item?: any) => ({ taxonomicGroup, propertyKey, - itemPropertyFilterType, + item, }), openDropdown: true, closeDropdown: true, @@ -93,7 +89,8 @@ export const taxonomicPropertyFilterLogic = kea ({ - selectItem: ({ taxonomicGroup, propertyKey, itemPropertyFilterType }) => { + selectItem: ({ taxonomicGroup, propertyKey, item }) => { + const itemPropertyFilterType = item?.propertyFilterType as PropertyFilterType const propertyType = itemPropertyFilterType ?? taxonomicFilterTypeToPropertyFilterType(taxonomicGroup.type) if (propertyKey && propertyType) { if (propertyType === PropertyFilterType.Cohort) { @@ -142,6 +139,7 @@ export const taxonomicPropertyFilterLogic = kea v === filterType)?.[0] as | PropertyFilterType | undefined diff --git a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx index 8e0237d36f252..ab03c6f9721c0 100644 --- a/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/InfiniteList.tsx @@ -222,7 +222,9 @@ export function InfiniteList({ popupAnchorElement }: InfiniteListProps): JSX.Ele
canSelectItem(listGroupType) && selectItem(group, itemValue ?? null, item)} + onClick={() => { + return canSelectItem(listGroupType) && selectItem(group, itemValue ?? null, item) + }} > {renderItemContents({ item, diff --git a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx index cc3e727f7b10d..966b33c5527ed 100644 --- a/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx +++ b/frontend/src/lib/components/TaxonomicFilter/taxonomicFilterLogic.tsx @@ -15,6 +15,7 @@ import { IconCohort } from 'lib/lemon-ui/icons' import { CORE_FILTER_DEFINITIONS_BY_GROUP } from 'lib/taxonomy' import { capitalizeFirstLetter, pluralize, toParams } from 'lib/utils' import { getEventDefinitionIcon, getPropertyDefinitionIcon } from 'scenes/data-management/events/DefinitionHeader' +import { dataWarehouseJoinsLogic } from 'scenes/data-warehouse/external/dataWarehouseJoinsLogic' import { dataWarehouseSceneLogic } from 'scenes/data-warehouse/external/dataWarehouseSceneLogic' import { DataWarehouseTableType } from 'scenes/data-warehouse/types' import { experimentsLogic } from 'scenes/experiments/experimentsLogic' @@ -86,6 +87,8 @@ export const taxonomicFilterLogic = kea([ ['allGroupProperties'], dataWarehouseSceneLogic, ['externalTables'], + dataWarehouseJoinsLogic, + ['columnsJoinedToPersons'], ], }), actions(() => ({ @@ -225,6 +228,16 @@ export const taxonomicFilterLogic = kea([ getPopoverHeader: () => 'Data Warehouse Column', getIcon: () => , }, + { + name: 'Data Warehouse Person Properties', + searchPlaceholder: 'person properties from data warehouse tables', + type: TaxonomicFilterGroupType.DataWarehousePersonProperties, + logic: dataWarehouseJoinsLogic, + value: 'columnsJoinedToPersons', + getName: (personProperty: PersonProperty) => personProperty.name, + getValue: (personProperty: PersonProperty) => personProperty.id, + getPopoverHeader: () => 'Data Warehouse Person Property', + }, { name: 'Autocapture elements', searchPlaceholder: 'autocapture elements', diff --git a/frontend/src/lib/components/TaxonomicFilter/types.ts b/frontend/src/lib/components/TaxonomicFilter/types.ts index 964847c6eacaf..8b46784a19b7e 100644 --- a/frontend/src/lib/components/TaxonomicFilter/types.ts +++ b/frontend/src/lib/components/TaxonomicFilter/types.ts @@ -85,6 +85,7 @@ export enum TaxonomicFilterGroupType { CohortsWithAllUsers = 'cohorts_with_all', DataWarehouse = 'data_warehouse', DataWarehouseProperties = 'data_warehouse_properties', + DataWarehousePersonProperties = 'data_warehouse_person_properties', Elements = 'elements', Events = 'events', EventProperties = 'event_properties', diff --git a/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx b/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx index bd755cb3bd5e7..91f0a1924ec3c 100644 --- a/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx +++ b/frontend/src/queries/nodes/InsightViz/GlobalAndOrFilters.tsx @@ -26,6 +26,7 @@ export function GlobalAndOrFilters({ insightProps }: EditorFilterProps): JSX.Ele TaxonomicFilterGroupType.Elements, ...(isTrends ? [TaxonomicFilterGroupType.Sessions] : []), TaxonomicFilterGroupType.HogQLExpression, + TaxonomicFilterGroupType.DataWarehousePersonProperties, ] return ( diff --git a/frontend/src/queries/schema.json b/frontend/src/queries/schema.json index eeac4bb951269..bb6e2256a43c8 100644 --- a/frontend/src/queries/schema.json +++ b/frontend/src/queries/schema.json @@ -3460,6 +3460,9 @@ "operator": { "$ref": "#/definitions/PropertyOperator" }, + "table": { + "type": "string" + }, "type": { "const": "person", "description": "Person properties", diff --git a/frontend/src/scenes/data-warehouse/external/dataWarehouseJoinsLogic.ts b/frontend/src/scenes/data-warehouse/external/dataWarehouseJoinsLogic.ts index b5f493b2d7f17..377049900b37c 100644 --- a/frontend/src/scenes/data-warehouse/external/dataWarehouseJoinsLogic.ts +++ b/frontend/src/scenes/data-warehouse/external/dataWarehouseJoinsLogic.ts @@ -1,10 +1,13 @@ -import { afterMount, kea, path } from 'kea' +import { afterMount, connect, kea, path, selectors } from 'kea' import { loaders } from 'kea-loaders' import api from 'lib/api' +import { capitalizeFirstLetter } from 'lib/utils' -import { DataWarehouseViewLink } from '~/types' +import { DatabaseSchemaQueryResponseField } from '~/queries/schema' +import { DataWarehouseViewLink, PropertyDefinition, PropertyType } from '~/types' import type { dataWarehouseJoinsLogicType } from './dataWarehouseJoinsLogicType' +import { dataWarehouseSceneLogic } from './dataWarehouseSceneLogic' export const dataWarehouseJoinsLogic = kea([ path(['scenes', 'data-warehouse', 'external', 'dataWarehouseJoinsLogic']), @@ -19,6 +22,43 @@ export const dataWarehouseJoinsLogic = kea([ }, ], }), + connect(() => ({ + values: [dataWarehouseSceneLogic, ['externalTablesMap']], + })), + selectors({ + personTableJoins: [(s) => [s.joins], (joins) => joins.filter((join) => join.source_table_name === 'persons')], + tablesJoinedToPersons: [ + (s) => [s.externalTablesMap, s.personTableJoins], + (externalTablesMap, personTableJoins) => { + return personTableJoins.map((join: DataWarehouseViewLink) => { + // valid join should have a joining table name + const table = externalTablesMap[join.joining_table_name as string] + return { + table, + join, + } + }) + }, + ], + columnsJoinedToPersons: [ + (s) => [s.tablesJoinedToPersons], + (tablesJoinedToPersons) => { + return tablesJoinedToPersons.reduce((acc, { table, join }) => { + if (table) { + acc.push( + ...table.columns.map((column: DatabaseSchemaQueryResponseField) => ({ + id: column.key, + name: join.field_name + ': ' + column.key, + table: join.field_name, + property_type: capitalizeFirstLetter(column.type) as PropertyType, + })) + ) + } + return acc + }, [] as PropertyDefinition[]) + }, + ], + }), afterMount(({ actions }) => { actions.loadJoins() }), diff --git a/frontend/src/types.ts b/frontend/src/types.ts index a583fe34c26d2..c34634faf691f 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -672,6 +672,7 @@ export interface EventPropertyFilter extends BasePropertyFilter { export interface PersonPropertyFilter extends BasePropertyFilter { type: PropertyFilterType.Person operator: PropertyOperator + table?: string } export interface DataWarehousePropertyFilter extends BasePropertyFilter { @@ -2812,6 +2813,9 @@ export interface PropertyDefinition { verified?: boolean verified_at?: string verified_by?: string + + // For Data warehouse person properties + table?: string } export enum PropertyDefinitionState { diff --git a/plugin-server/src/types.ts b/plugin-server/src/types.ts index d70238307eaac..114547cfe605f 100644 --- a/plugin-server/src/types.ts +++ b/plugin-server/src/types.ts @@ -868,6 +868,7 @@ export interface EventPropertyFilter extends PropertyFilterWithOperator { /** Sync with posthog/frontend/src/types.ts */ export interface PersonPropertyFilter extends PropertyFilterWithOperator { type: 'person' + table?: string } /** Sync with posthog/frontend/src/types.ts */ diff --git a/posthog/hogql/property.py b/posthog/hogql/property.py index 98019cdaa54b7..ba9f92443b4e8 100644 --- a/posthog/hogql/property.py +++ b/posthog/hogql/property.py @@ -147,7 +147,10 @@ def property_to_expr( value = property.value if property.type == "person" and scope != "person": - chain = ["person", "properties"] + if property.table: + chain = ["person", property.table] + else: + chain = ["person", "properties"] elif property.type == "group": chain = [f"group_{property.group_type_index}", "properties"] elif property.type == "data_warehouse": diff --git a/posthog/models/property/property.py b/posthog/models/property/property.py index d0e0f94439cf5..4bd44646ec4a9 100644 --- a/posthog/models/property/property.py +++ b/posthog/models/property/property.py @@ -202,6 +202,7 @@ class Property: total_periods: Optional[int] min_periods: Optional[int] negation: Optional[bool] = False + table: Optional[str] _data: Dict def __init__( @@ -224,6 +225,7 @@ def __init__( seq_time_value: Optional[int] = None, seq_time_interval: Optional[OperatorInterval] = None, negation: Optional[bool] = None, + table: Optional[str] = None, **kwargs, ) -> None: self.key = key @@ -241,6 +243,7 @@ def __init__( self.seq_time_value = seq_time_value self.seq_time_interval = seq_time_interval self.negation = None if negation is None else str_to_bool(negation) + self.table = table if value is None and self.operator in ["is_set", "is_not_set"]: self.value = self.operator diff --git a/posthog/schema.py b/posthog/schema.py index 17ad11fc4f236..7068c39090a2f 100644 --- a/posthog/schema.py +++ b/posthog/schema.py @@ -1369,6 +1369,7 @@ class PersonPropertyFilter(BaseModel): key: str label: Optional[str] = None operator: PropertyOperator + table: Optional[str] = None type: Literal["person"] = Field(default="person", description="Person properties") value: Optional[Union[str, float, List[Union[str, float]]]] = None